import json
from unittest.mock import patch

import pytest
from aioresponses import aioresponses

from iis_access._types import AuthContext
from iis_access.auth import get_auth_context, is_authenticated


@pytest.fixture
def mock_credentials(tmp_path):
    """Create a temporary credentials file."""
    iis_dir = tmp_path / ".iis"
    iis_dir.mkdir()
    creds_file = iis_dir / "credentials.json"

    def _write(data: dict):
        creds_file.write_text(json.dumps(data))
        return iis_dir

    return _write


class TestGetAuthContext:
    async def test_returns_none_when_no_credentials(self):
        with patch("iis_access.auth.get_credentials", return_value=None):
            result = await get_auth_context()
            assert result is None

    async def test_returns_none_when_auth_disabled(self):
        with patch.dict("os.environ", {"IIS_NO_AUTH": "1"}):
            result = await get_auth_context()
            assert result is None

    async def test_returns_none_when_no_account_id(self):
        with patch(
            "iis_access.auth.get_credentials",
            return_value={"refreshToken": "tok"},
        ):
            result = await get_auth_context()
            assert result is None

    async def test_returns_none_when_no_refresh_token(self):
        with patch(
            "iis_access.auth.get_credentials",
            return_value={"accountId": "acc-123"},
        ):
            with patch("iis_access.auth._get_keyring", return_value=None):
                result = await get_auth_context()
                assert result is None

    async def test_returns_auth_context_on_successful_refresh(self):
        creds = {"accountId": "acc-123", "refreshToken": "iis_rt_test"}

        with (
            patch("iis_access.auth.get_credentials", return_value=creds),
            patch("iis_access.auth._get_keyring", return_value=None),
            patch(
                "iis_access.auth.get_account_url",
                return_value="http://localhost:9060",
            ),
            aioresponses() as mocked,
        ):
            mocked.post(
                "http://localhost:9060/auth/refresh",
                payload={
                    "accessToken": "jwt-token-abc",
                    "refreshToken": "iis_rt_new",
                    "plan": "pro",
                },
            )

            result = await get_auth_context()

            assert result is not None
            assert isinstance(result, AuthContext)
            assert result.account_id == "acc-123"
            assert result.access_token == "jwt-token-abc"
            assert result.plan == "pro"

    async def test_returns_none_on_refresh_failure(self):
        creds = {"accountId": "acc-123", "refreshToken": "iis_rt_test"}

        with (
            patch("iis_access.auth.get_credentials", return_value=creds),
            patch("iis_access.auth._get_keyring", return_value=None),
            patch(
                "iis_access.auth.get_account_url",
                return_value="http://localhost:9060",
            ),
            aioresponses() as mocked,
        ):
            mocked.post(
                "http://localhost:9060/auth/refresh",
                status=401,
            )

            result = await get_auth_context()
            assert result is None


class TestIsAuthenticated:
    def test_returns_false_when_no_credentials(self):
        with patch("iis_access.auth.get_credentials", return_value=None):
            assert is_authenticated() is False

    def test_returns_false_when_no_account_id(self):
        with patch(
            "iis_access.auth.get_credentials",
            return_value={"refreshToken": "tok"},
        ):
            assert is_authenticated() is False

    def test_returns_true_when_credentials_exist(self):
        with patch(
            "iis_access.auth.get_credentials",
            return_value={"accountId": "acc-123", "refreshToken": "tok"},
        ):
            assert is_authenticated() is True
