"""Tests for CloudIntelligenceAnalyzer ABC and resolve_cloud_credentials."""

from __future__ import annotations

import os
from datetime import UTC, datetime
from unittest.mock import MagicMock, patch

import pytest

from complyform.core.cloud_intelligence import (
    CloudIntelligenceAnalyzer,
    CloudIntelligenceResult,
    resolve_cloud_credentials,
)
from complyform.core.errors import CloudCredentialError


class TestCloudIntelligenceResult:
    """Tests for CloudIntelligenceResult dataclass."""

    def test_all_fields_present(self) -> None:
        ts = datetime.now(tz=UTC).isoformat()
        result = CloudIntelligenceResult(
            feature="iam_analysis",
            cloud="gcp",
            success=True,
            error=None,
            duration_ms=123,
            timestamp=ts,
        )
        assert result.feature == "iam_analysis"
        assert result.cloud == "gcp"
        assert result.success is True
        assert result.error is None
        assert result.duration_ms == 123
        assert result.timestamp == ts

    def test_timestamp_is_iso8601(self) -> None:
        ts = datetime.now(tz=UTC).isoformat()
        result = CloudIntelligenceResult(
            feature="iam_analysis",
            cloud="aws",
            success=True,
            error=None,
            duration_ms=0,
            timestamp=ts,
        )
        # Verify it parses back as ISO 8601.
        parsed = datetime.fromisoformat(result.timestamp)
        assert parsed.tzinfo is not None


class TestCloudIntelligenceAnalyzerABC:
    """Tests for CloudIntelligenceAnalyzer ABC."""

    def test_cannot_instantiate_directly(self) -> None:
        with pytest.raises(TypeError, match="abstract"):
            CloudIntelligenceAnalyzer(cloud="gcp", project_id="test")  # type: ignore[abstract]

    def test_subclass_instantiates(self) -> None:
        class ConcreteAnalyzer(CloudIntelligenceAnalyzer):
            def analyze(self) -> CloudIntelligenceResult:
                return CloudIntelligenceResult(
                    feature="test",
                    cloud=self.cloud,
                    success=True,
                    error=None,
                    duration_ms=0,
                    timestamp=datetime.now(tz=UTC).isoformat(),
                )

            def check_prerequisites(self) -> list[str]:
                return []

        analyzer = ConcreteAnalyzer(cloud="gcp", project_id="my-project")
        assert analyzer.cloud == "gcp"
        assert analyzer.project_id == "my-project"
        result = analyzer.analyze()
        assert result.success is True

    def test_timeout_env_override(self) -> None:
        class ConcreteAnalyzer(CloudIntelligenceAnalyzer):
            def analyze(self) -> CloudIntelligenceResult:
                return CloudIntelligenceResult(
                    feature="test",
                    cloud=self.cloud,
                    success=True,
                    error=None,
                    duration_ms=0,
                    timestamp=datetime.now(tz=UTC).isoformat(),
                )

            def check_prerequisites(self) -> list[str]:
                return []

        with patch.dict(os.environ, {"COMPLYFORM_CI_TIMEOUT": "60"}):
            analyzer = ConcreteAnalyzer(cloud="gcp", project_id="test")
            assert analyzer.total_timeout == 60

    def test_default_timeout(self) -> None:
        class ConcreteAnalyzer(CloudIntelligenceAnalyzer):
            def analyze(self) -> CloudIntelligenceResult:
                return CloudIntelligenceResult(
                    feature="test",
                    cloud=self.cloud,
                    success=True,
                    error=None,
                    duration_ms=0,
                    timestamp=datetime.now(tz=UTC).isoformat(),
                )

            def check_prerequisites(self) -> list[str]:
                return []

        with patch.dict(os.environ, {}, clear=True):
            # Remove COMPLYFORM_CI_TIMEOUT if present.
            os.environ.pop("COMPLYFORM_CI_TIMEOUT", None)
            analyzer = ConcreteAnalyzer(cloud="gcp", project_id="test")
            assert analyzer.total_timeout == 120


class TestResolveCloudCredentials:
    """Tests for resolve_cloud_credentials."""

    def test_gcp_no_credentials_raises(self) -> None:
        mock_google_auth = MagicMock()
        mock_google_auth.default.side_effect = Exception("No credentials")
        with (
            patch.dict(os.environ, {}, clear=True),
            patch.dict("sys.modules", {"google.auth": mock_google_auth}),
        ):
            os.environ.pop("GOOGLE_APPLICATION_CREDENTIALS", None)
            with pytest.raises(CloudCredentialError) as exc_info:
                resolve_cloud_credentials("gcp", None)
            assert exc_info.value.error_code == 11

    def test_aws_no_credentials_raises(self) -> None:
        mock_boto3 = MagicMock()
        mock_session = MagicMock()
        mock_session.get_credentials.return_value = None
        mock_boto3.Session.return_value = mock_session
        with (
            patch.dict(os.environ, {}, clear=True),
            patch.dict("sys.modules", {"boto3": mock_boto3}),
        ):
            with pytest.raises(CloudCredentialError) as exc_info:
                resolve_cloud_credentials("aws", None)
            assert exc_info.value.error_code == 11

    def test_azure_no_credentials_raises(self) -> None:
        mock_azure_identity = MagicMock()
        mock_azure_identity.DefaultAzureCredential.side_effect = Exception(
            "No credentials"
        )
        with (
            patch.dict(os.environ, {}, clear=True),
            patch.dict(
                "sys.modules",
                {"azure.identity": mock_azure_identity},
            ),
        ):
            with pytest.raises(CloudCredentialError) as exc_info:
                resolve_cloud_credentials("azure", None)
            assert exc_info.value.error_code == 11
