import pytest
import mongoengine
from app.services.auth import AuthService
from unittest.mock import patch, Mock, MagicMock
import json
import mongoengine
from app.services.auth import AuthService
from app.models import User
from mongoengine.queryset.visitor import Q



class TestAuthUser:
    
    @patch("app.models.base.Base.save")
    def test_save_user_called(self,save_mocker,user_model_factory):
        auth = AuthService()
        payload = json.loads(user_model_factory.to_json())
        auth.create_user(payload)
        
        save_mocker.assert_called_once()
        save_mocker.assert_called_with()


    

    @patch.object(User,"objects")
    def test_get_user_by_id(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Mock User.objects to return a mock query
        mock_query = Mock()
        mocker.return_value = mock_query

        # Mock the first() method of the query
        mock_user = Mock()
        mock_query.first.return_value = mock_user

        # Call the method being tested
        result = AuthService.get_user_by_id(payload['public_id'])

        # Assert that the method returns the expected user
        assert result == mock_user

        # Assert that User.objects(public_id=user_id).first() was called
        mock_query.first.assert_called_once()   

    @patch.object(User,'objects')
    def test_get_user_by_email_or_username(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Mock User.objects to return a mock query
        mock_query = Mock()
        mocker.return_value = mock_query

        # Mock the first() method of the query
        mock_user = Mock()
        mock_query.first.return_value = mock_user

        # Call the method being tested
        result = AuthService.get_user_by_email_or_username(payload['email'])

        # Assert that the method returns the expected user
        assert result == mock_user

        # Assert that User.objects(public_id=user_id).first() was called
        mock_query.first.assert_called_once()


    @pytest.mark.skip(reason="Q searh should be called but isn't called")
    @patch("mongoengine.queryset.visitor")
    def test_get_user_by_email_Q_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())
        result = AuthService.get_user_by_email_or_username(payload['email'])
        print(result)
        mocker.Q.assert_called_once_with(payload['email']) 
        mocker.Q.assert_called_once()

        
    @patch.object(User,'objects')
    def test_get_user_by_email(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Mock User.objects to return a mock query
        mock_query = Mock()
        mocker.return_value = mock_query

        # Mock the first() method of the query
        mock_user = Mock()
        mock_query.first.return_value = mock_user

        # Call the method being tested
        result = AuthService.get_user_by_email(payload['email'])

        # Assert that the method returns the expected user
        assert result == mock_user

        # Assert that User.objects(public_id=user_id).first() was called
        mock_query.first.assert_called_once()

    @pytest.mark.xfail(reason="email has changed in every call")
    @patch.object(User,'objects')
    def test_search_users_by_keyword(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.search_users_by_keyword(payload['username'])

        # Assert that the method returns the expected user
        # assert result == mock_user

        # Assert that User.objects(public_id=user_id).first() was called
        mocker.assert_called_once()
        mocker.assert_called_once_with(Q(username__icontains=payload['username']) | Q(email__icontains=payload['email'].split('@')[0]))

    @patch("app.services.auth.AuthService")
    def test_authenticate_get_user_by_email_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.authenticate(payload['email'],payload['password'])

        mocker.get_user_by_email.assert_called_once()
        mocker.get_user_by_email.assert_called_once_with(payload['email'])


    @pytest.mark.xfail(reason="check_pasword should be called")
    @patch("app.models.user.User")
    def test_authenticate_get_user_by_email_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.authenticate(payload['email'],payload['password'])

        mocker.check_password.assert_called_once()
        mocker.check_password.assert_called_once_with(payload['password'])

    @patch("app.services.auth.AuthService")
    def test_change_password_get_user_by_id_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.change_password(payload["public_id"],payload["password"])

        mocker.get_user_by_id.assert_called_once()
        mocker.get_user_by_id.assert_called_once_with(payload["public_id"])


    @patch("app.services.auth.AuthService")
    def test_promote_user_get_user_by_id_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.promote_user(payload["public_id"],payload["role"])

        mocker.get_user_by_id.assert_called_once()
        mocker.get_user_by_id.assert_called_once_with(payload["public_id"])

    
        
    @patch("app.services.auth.AuthService")
    def test_reset_password_get_user_by_id_called(self,mocker,user_model_factory):
        payload = json.loads(user_model_factory.to_json())

        # Call the method being tested
        result = AuthService.reset_password(payload["public_id"],payload["password"])

        mocker.get_user_by_id.assert_called_once()
        mocker.get_user_by_id.assert_called_once_with(payload["public_id"])
        assert result ==  True
