"""Tests for assess command cloud intelligence integration."""

from __future__ import annotations

import dataclasses
import json
from typing import TYPE_CHECKING, Any
from unittest.mock import patch

from complyform.core.cloud_intelligence.cost_estimator import CostImpactEstimate
from complyform.core.cloud_intelligence.cross_validator import CrossValidationResult
from complyform.core.cloud_intelligence.iam_analyzer import (
    IAMAnalysisResult,
    IAMFinding,
)
from complyform.core.scanner.landing_zone import LandingZoneContext

if TYPE_CHECKING:
    from pathlib import Path

    from complyform.core.cloud_intelligence import CloudIntelligenceResult


def _make_iam_finding(**overrides: object) -> IAMFinding:
    defaults: dict[str, object] = {
        "cloud": "gcp",
        "principal": "sa@proj.iam.gserviceaccount.com",
        "principal_type": "service_account",
        "current_role": "roles/editor",
        "recommended_role": "roles/viewer",
        "unused_permissions": ["storage.buckets.delete"],
        "last_activity": "2025-01-01T00:00:00Z",
        "risk_level": "HIGH",
        "framework_controls": [{"framework": "soc2", "control_id": "CC6.1"}],
        "remediation_terraform": None,
        "source_api": "iam_recommender",
    }
    defaults.update(overrides)
    return IAMFinding(**defaults)  # type: ignore[arg-type]


def _make_iam_result(
    findings: list[IAMFinding] | None = None,
) -> IAMAnalysisResult:
    return IAMAnalysisResult(
        feature="iam_analysis",
        cloud="gcp",
        success=True,
        error=None,
        duration_ms=100,
        timestamp="2025-01-01T00:00:00Z",
        findings=findings or [_make_iam_finding()],
        principals_scanned=10,
        over_provisioned_count=1,
        unused_role_count=0,
    )


def _make_xval_result() -> CrossValidationResult:
    return CrossValidationResult(
        feature="cross_validation",
        cloud="gcp",
        success=True,
        error=None,
        duration_ms=200,
        timestamp="2025-01-01T00:00:00Z",
        cross_validation_score=80,
        native_tool="scc",
    )


def _make_cost_result() -> CostImpactEstimate:
    return CostImpactEstimate(
        feature="cost_impact",
        cloud="gcp",
        success=True,
        error=None,
        duration_ms=150,
        timestamp="2025-01-01T00:00:00Z",
        total_monthly_delta=25.0,
        total_annual_delta=300.0,
        confidence="estimated",
    )


class TestIAMFindingMerge:
    """IAM findings merged into main findings list."""

    def test_iam_findings_converted_to_findings(self) -> None:
        from complyform.core.cloud_intelligence.iam_analyzer import (
            iam_finding_to_findings,
        )

        iam = _make_iam_finding()
        findings = iam_finding_to_findings(iam, ["soc2"])
        assert len(findings) > 0
        assert all(f.status == "FAIL" for f in findings)
        assert all(f.resource_type == "iam_binding" for f in findings)

    def test_iam_merge_changes_score(self) -> None:
        """Score recomputed after IAM finding merge."""
        from complyform.core.assessor.control_matcher import Finding
        from complyform.core.assessor.scoring import compute_compliance_score
        from complyform.core.cloud_intelligence.iam_analyzer import (
            iam_finding_to_findings,
        )

        base_findings = [
            Finding(
                control_id="CC6.1",
                framework="soc2",
                resource_address="res1",
                resource_type="bucket",
                status="PASS",
                severity="HIGH",
                finding="OK",
                remediation="",
                checkov_id=None,
                cross_mappings=None,
                native_ids=None,
                confidence="verified",
            )
        ]
        score_before = compute_compliance_score(base_findings)

        # Add IAM FAIL findings
        iam = _make_iam_finding()
        iam_findings = iam_finding_to_findings(iam, ["soc2"])
        merged = base_findings + iam_findings
        score_after = compute_compliance_score(merged)

        assert score_after < score_before


class TestLandingZoneContext:
    """Landing zone context modifies findings before scoring."""

    def test_landing_zone_sets_findings_to_pass(self) -> None:
        from complyform.core.cloud_intelligence.assessment_modifier import (
            apply_landing_zone_context,
        )

        lz = LandingZoneContext(
            cloud="gcp",
            detected=True,
            type="assured_workloads",
            inherited_policies=["policy1"],
        )

        # Mock the mapping to cover our finding
        with patch(
            "complyform.core.cloud_intelligence.assessment_modifier.LANDING_ZONE_CONTROL_MAP",
            {
                "assured_workloads": {
                    "policy1": [
                        {"framework": "soc2", "control_id": "CC6.1"},
                    ],
                },
            },
        ):
            from complyform.core.assessor.control_matcher import Finding

            findings = [
                Finding(
                    control_id="CC6.1",
                    framework="soc2",
                    resource_address="res1",
                    resource_type="bucket",
                    status="FAIL",
                    severity="HIGH",
                    finding="Missing encryption",
                    remediation="",
                    checkov_id=None,
                    cross_mappings=None,
                    native_ids=None,
                    confidence="verified",
                )
            ]
            modified, skipped = apply_landing_zone_context(findings, lz)
            assert skipped == 1
            assert modified[0].status == "PASS"


class TestCacheCloudIntelligence:
    """Cache includes CI fields after assess."""

    def test_cache_ci_roundtrip(self, tmp_path: Path) -> None:
        """CI results written to cache metadata can be read back."""
        meta_dir = tmp_path / "abcdef0123456789"
        meta_dir.mkdir()
        meta_path = meta_dir / "metadata.json"
        meta_path.write_text(
            json.dumps({"scanned_at": "2025-01-01T00:00:00Z", "cloud": "gcp"}),
            encoding="utf-8",
        )

        iam_result = _make_iam_result()
        meta = json.loads(meta_path.read_text(encoding="utf-8"))
        meta["iam_analysis"] = dataclasses.asdict(iam_result)
        meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")

        reloaded = json.loads(meta_path.read_text(encoding="utf-8"))
        assert reloaded["iam_analysis"]["success"] is True
        assert reloaded["iam_analysis"]["principals_scanned"] == 10

    def test_old_cache_without_ci_loads_fine(self, tmp_path: Path) -> None:
        """Cache without CI fields (pre-19c) loads without error."""
        meta_dir = tmp_path / "abcdef0123456789"
        meta_dir.mkdir()
        meta_path = meta_dir / "metadata.json"
        old_meta = {
            "scanned_at": "2025-01-01T00:00:00Z",
            "cloud": "gcp",
            "state_hash": "sha256:abc",
        }
        meta_path.write_text(json.dumps(old_meta), encoding="utf-8")

        meta = json.loads(meta_path.read_text(encoding="utf-8"))
        # CI fields default to None when not present
        assert meta.get("iam_analysis") is None
        assert meta.get("cross_validation") is None
        assert meta.get("cost_impact") is None
        assert meta.get("landing_zone") is None


class TestJSONOutputCISection:
    """All CI flags produce sections in JSON output."""

    def test_ci_results_serialized(self) -> None:
        """CI results serialize to dict via dataclasses.asdict."""
        iam = _make_iam_result(findings=[])
        xval = _make_xval_result()
        cost = _make_cost_result()

        ci_results: dict[str, CloudIntelligenceResult] = {
            "iam_analysis": iam,
            "cross_validation": xval,
            "cost_impact": cost,
        }

        ci_payload: dict[str, Any] = {}
        for key, result in ci_results.items():
            ci_payload[key] = dataclasses.asdict(result)

        assert "iam_analysis" in ci_payload
        assert "cross_validation" in ci_payload
        assert "cost_impact" in ci_payload
        assert ci_payload["cross_validation"]["cross_validation_score"] == 80
        assert ci_payload["cost_impact"]["total_monthly_delta"] == 25.0
