from unittest.mock import MagicMock, patch

import pytest
from django.contrib.auth import get_user_model

User = get_user_model()


@pytest.fixture
def user_profile(db):
    user = User.objects.create_user(username="testuser", password="testpass")
    return user.profile


@pytest.mark.django_db
class TestGenerateTodoistOauthUrl:
    def test_generates_url_with_state(self, user_profile):
        from tasks.todoist import generate_todoist_oauth_url

        url = generate_todoist_oauth_url(user_profile.user_id)

        user_profile.refresh_from_db()
        assert user_profile.todoist_state is not None
        assert len(user_profile.todoist_state) == 32
        assert url.startswith("https://todoist.com/oauth/authorize")
        assert user_profile.todoist_state in url

    def test_updates_existing_state(self, user_profile):
        from tasks.todoist import generate_todoist_oauth_url

        old_state = "oldstate12345678901234567890123"
        user_profile.todoist_state = old_state
        user_profile.save()

        url = generate_todoist_oauth_url(user_profile.user_id)

        user_profile.refresh_from_db()
        assert user_profile.todoist_state != old_state


@pytest.mark.django_db
class TestGetTodoistAccessToken:
    def test_raises_when_profile_not_found(self):
        from tasks.todoist import get_todoist_access_token

        with pytest.raises(Exception, match="Could not find profile"):
            get_todoist_access_token(user_id=999, state="anystate", code="anycode")

    def test_raises_when_state_mismatch(self, user_profile):
        from tasks.todoist import get_todoist_access_token

        user_profile.todoist_state = "correctstate1234567890123"
        user_profile.save()

        with pytest.raises(Exception, match="state mismatch"):
            get_todoist_access_token(
                user_id=user_profile.user_id, state="wrongstate", code="anycode"
            )

    @patch("tasks.todoist.requests.post")
    def test_exchanges_code_for_token(self, mock_post, user_profile):
        from tasks.todoist import get_todoist_access_token

        user_profile.todoist_state = "correctstate1234567890123"
        user_profile.save()

        mock_token_response = MagicMock()
        mock_token_response.status_code = 200
        mock_token_response.json.return_value = {"access_token": "test_access_token"}
        mock_post.return_value = mock_token_response

        get_todoist_access_token(
            user_id=user_profile.user_id,
            state="correctstate1234567890123",
            code="testcode",
        )

        user_profile.refresh_from_db()
        assert user_profile.todoist_auth_key == "test_access_token"
        assert user_profile.todoist_state is None

    @patch("tasks.todoist.requests.post")
    def test_fetches_todoist_user_id(self, mock_post, user_profile):
        from tasks.todoist import get_todoist_access_token

        user_profile.todoist_state = "correctstate1234567890123"
        user_profile.save()

        mock_token_response = MagicMock()
        mock_token_response.status_code = 200
        mock_token_response.json.return_value = {"access_token": "test_access_token"}

        mock_sync_response = MagicMock()
        mock_sync_response.status_code = 200
        mock_sync_response.json.return_value = {"user": {"id": "12345"}}

        mock_post.side_effect = [mock_token_response, mock_sync_response]

        get_todoist_access_token(
            user_id=user_profile.user_id,
            state="correctstate1234567890123",
            code="testcode",
        )

        user_profile.refresh_from_db()
        assert user_profile.todoist_user_id == "12345"

    @patch("tasks.todoist.requests.post")
    def test_handles_token_exchange_failure(self, mock_post, user_profile):
        from tasks.todoist import get_todoist_access_token

        user_profile.todoist_state = "correctstate1234567890123"
        user_profile.save()

        mock_response = MagicMock()
        mock_response.status_code = 400
        mock_post.return_value = mock_response

        get_todoist_access_token(
            user_id=user_profile.user_id,
            state="correctstate1234567890123",
            code="badcode",
        )

        user_profile.refresh_from_db()
        assert user_profile.todoist_auth_key is None
        assert user_profile.todoist_state == "correctstate1234567890123"
