"""Unit tests for the Antigravity provider."""

import json
import os
import tempfile
from pathlib import Path
from unittest import mock

import pytest

from providers.antigravity import AntigravityProvider
from providers.antigravity_auth import AntigravityAccount, AntigravityTokenManager
from providers.registries.antigravity import AntigravityModelRegistry
from providers.shared import ProviderType


class TestAntigravityModelRegistry:
    """Tests for the Antigravity model registry."""

    def test_registry_loads_models(self):
        """Verify registry loads models from config file."""
        registry = AntigravityModelRegistry()
        models = registry.list_models()

        assert len(models) > 0
        assert "claude-opus-4-5-thinking" in models
        assert "claude-sonnet-4-5" in models
        assert "gemini-3-pro-high" in models

    def test_registry_aliases(self):
        """Verify alias resolution works correctly."""
        registry = AntigravityModelRegistry()

        # Test alias resolution
        capabilities = registry.resolve("antigravity-claude-opus-4-5-thinking")
        assert capabilities is not None
        assert capabilities.model_name == "claude-opus-4-5-thinking"

        # Test short alias
        capabilities = registry.resolve("ag-opus-thinking")
        assert capabilities is not None
        assert capabilities.model_name == "claude-opus-4-5-thinking"

    def test_model_capabilities(self):
        """Verify model capabilities are correct."""
        registry = AntigravityModelRegistry()

        opus = registry.get_capabilities("claude-opus-4-5-thinking")
        assert opus is not None
        assert opus.supports_extended_thinking is True
        assert opus.max_thinking_tokens == 32768
        assert opus.supports_images is True

        sonnet = registry.get_capabilities("claude-sonnet-4-5")
        assert sonnet is not None
        assert sonnet.supports_extended_thinking is False

    def test_quota_pool(self):
        """Verify quota pool assignment."""
        registry = AntigravityModelRegistry()

        # Antigravity quota models
        assert registry.get_quota_pool("claude-opus-4-5-thinking") == "antigravity"
        assert registry.get_quota_pool("gemini-3-pro-high") == "antigravity"

        # Gemini CLI quota models
        assert registry.get_quota_pool("gemini-2.5-pro") == "gemini-cli"
        assert registry.get_quota_pool("gemini-3-flash-preview") == "gemini-cli"


class TestAntigravityTokenManager:
    """Tests for the OAuth2 token manager."""

    def test_empty_initialization(self):
        """Verify manager handles no accounts gracefully."""
        # Create temp dir without accounts file
        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "nonexistent.json"
            manager = AntigravityTokenManager(accounts_file=fake_path)
            assert not manager.has_accounts()
            assert manager.get_account_count() == 0

    def test_env_var_override(self, monkeypatch):
        """Verify environment variable takes precedence."""
        monkeypatch.setenv("ANTIGRAVITY_REFRESH_TOKEN", "test_token_123")
        monkeypatch.setenv("ANTIGRAVITY_PROJECT_ID", "test-project")

        manager = AntigravityTokenManager()
        assert manager.has_accounts()
        assert manager.get_account_count() == 1

    def test_load_accounts_from_file(self):
        """Verify loading accounts from JSON file."""
        accounts_data = {
            "version": 3,
            "accounts": [
                {
                    "email": "test@example.com",
                    "refreshToken": "token_1",
                    "projectId": "project-1",
                    "enabled": True,
                },
                {
                    "email": "test2@example.com",
                    "refreshToken": "token_2",
                    "projectId": "project-2",
                    "enabled": True,
                },
                {
                    "email": "disabled@example.com",
                    "refreshToken": "token_3",
                    "projectId": "project-3",
                    "enabled": False,  # Should be skipped
                },
            ],
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(accounts_data, f)
            temp_path = f.name

        try:
            # Clear env to ensure file is used
            with mock.patch.dict(os.environ, {}, clear=True):
                manager = AntigravityTokenManager(accounts_file=temp_path)
                assert manager.has_accounts()
                assert manager.get_account_count() == 2  # Disabled account skipped
        finally:
            os.unlink(temp_path)


class TestAntigravityAccount:
    """Tests for the AntigravityAccount dataclass."""

    def test_token_validity(self):
        """Test token validity checking."""
        import time

        account = AntigravityAccount(
            email="test@example.com",
            refresh_token="token",
            project_id="project",
        )

        # No access token
        assert not account.is_token_valid()

        # Set valid token
        account.access_token = "access_token"
        account.token_expiry = time.time() + 3600
        assert account.is_token_valid()

        # Expired token
        account.token_expiry = time.time() - 100
        assert not account.is_token_valid()

    def test_rate_limit_tracking(self):
        """Test rate limit tracking."""
        import time

        account = AntigravityAccount(
            email="test@example.com",
            refresh_token="token",
            project_id="project",
        )

        # No rate limits initially
        assert not account.is_rate_limited()
        assert not account.is_rate_limited("claude-opus-4-5-thinking")

        # Set rate limit
        account.set_rate_limited("claude-opus-4-5-thinking", 60.0)
        assert account.is_rate_limited("claude-opus-4-5-thinking")
        assert account.is_rate_limited()  # Any rate limit active


class TestAntigravityProvider:
    """Tests for the Antigravity provider."""

    def test_provider_type(self):
        """Verify provider type is correct."""
        # Mock token manager to avoid needing real credentials
        with mock.patch.object(AntigravityTokenManager, "__init__", return_value=None):
            with mock.patch.object(AntigravityTokenManager, "has_accounts", return_value=True):
                provider = AntigravityProvider()
                assert provider.get_provider_type() == ProviderType.ANTIGRAVITY

    def test_model_capabilities(self):
        """Verify model capabilities are accessible."""
        with mock.patch.object(AntigravityTokenManager, "__init__", return_value=None):
            with mock.patch.object(AntigravityTokenManager, "has_accounts", return_value=True):
                provider = AntigravityProvider()
                caps = provider.get_all_model_capabilities()
                assert len(caps) > 0
                assert "claude-opus-4-5-thinking" in caps

    def test_model_resolution(self):
        """Verify alias resolution works through provider."""
        with mock.patch.object(AntigravityTokenManager, "__init__", return_value=None):
            with mock.patch.object(AntigravityTokenManager, "has_accounts", return_value=True):
                provider = AntigravityProvider()

                # Test alias resolution
                resolved = provider._resolve_model_name("ag-opus-thinking")
                assert resolved == "claude-opus-4-5-thinking"

                # Test canonical name
                resolved = provider._resolve_model_name("claude-sonnet-4-5")
                assert resolved == "claude-sonnet-4-5"

    def test_request_building(self):
        """Verify request body is built correctly."""
        with mock.patch.object(AntigravityTokenManager, "__init__", return_value=None):
            with mock.patch.object(AntigravityTokenManager, "has_accounts", return_value=True):
                provider = AntigravityProvider()

                capabilities = provider.get_capabilities("claude-opus-4-5-thinking")

                request = provider._build_request(
                    prompt="Hello, world!",
                    model_name="claude-opus-4-5-thinking",
                    project_id="test-project",
                    system_prompt="You are a helpful assistant.",
                    temperature=0.7,
                    max_output_tokens=1000,
                    thinking_mode="medium",
                    capabilities=capabilities,
                )

                assert request["project"] == "test-project"
                assert request["model"] == "claude-opus-4-5-thinking"
                assert "contents" in request["request"]
                assert "systemInstruction" in request["request"]
                assert "generationConfig" in request["request"]

                # Check thinking config for thinking models
                config = request["request"]["generationConfig"]
                assert "thinkingConfig" in config
                assert config["thinkingConfig"]["thinkingBudget"] > 0

    def test_list_models(self):
        """Verify list_models returns expected models."""
        with mock.patch.object(AntigravityTokenManager, "__init__", return_value=None):
            with mock.patch.object(AntigravityTokenManager, "has_accounts", return_value=True):
                provider = AntigravityProvider()
                models = provider.list_models(respect_restrictions=False)

                assert len(models) > 0
                # Check that both canonical names and aliases are present
                assert any("claude" in m.lower() for m in models)
                assert any("gemini" in m.lower() for m in models)


@pytest.mark.integration
class TestAntigravityIntegration:
    """Integration tests (require actual credentials and valid tokens).

    These tests require:
    - Valid Antigravity credentials (env var or accounts file)
    - Working OAuth tokens that can be refreshed

    Run with: pytest -m integration tests/test_antigravity_provider.py
    Skip with: pytest -m "not integration" tests/test_antigravity_provider.py
    """

    @pytest.mark.skipif(
        not os.getenv("ANTIGRAVITY_REFRESH_TOKEN")
        and not Path.home().joinpath(".config/opencode/antigravity-accounts.json").exists(),
        reason="Antigravity credentials not available",
    )
    def test_token_refresh(self):
        """Test actual token refresh with real credentials."""
        manager = AntigravityTokenManager()
        assert manager.has_accounts()

        # This will trigger a token refresh - may fail if tokens are expired
        try:
            token, project_id, headers = manager.get_access_token()
            assert token is not None
            assert len(token) > 0
            assert project_id is not None
        except RuntimeError as e:
            if "rate limited or have invalid tokens" in str(e):
                pytest.skip("Antigravity tokens are expired or invalid")
            raise

    @pytest.mark.skipif(
        not os.getenv("ANTIGRAVITY_REFRESH_TOKEN")
        and not Path.home().joinpath(".config/opencode/antigravity-accounts.json").exists(),
        reason="Antigravity credentials not available",
    )
    def test_simple_generation(self):
        """Test actual content generation with Antigravity API."""
        provider = AntigravityProvider()

        try:
            response = provider.generate_content(
                prompt="Say 'hello' and nothing else.",
                model_name="claude-sonnet-4-5",
                temperature=0.0,
                max_output_tokens=100,
            )
        except RuntimeError as e:
            if "rate limited or have invalid tokens" in str(e):
                pytest.skip("Antigravity tokens are expired or invalid")
            raise

        assert response is not None
        assert response.content is not None
        assert "hello" in response.content.lower()
        assert response.provider == ProviderType.ANTIGRAVITY
