"""Tests for Checkov custom policy generator."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from complyform.core.generators.checkov_custom import (
    CheckovCustomBundle,
    CheckovCustomGenerator,
    CheckovCustomPolicy,
    _get_category,
)

if TYPE_CHECKING:
    from pathlib import Path


@pytest.fixture
def generator() -> CheckovCustomGenerator:
    return CheckovCustomGenerator()


def _make_finding(
    *,
    control_id: str = "CC6.1",
    framework: str = "soc2",
    resource_type: str = "google_sql_database_instance",
    checkov_id: str | None = None,
    status: str = "FAIL",
    severity: str = "CRITICAL",
) -> dict[str, object]:
    return {
        "control_id": control_id,
        "framework": framework,
        "resource_type": resource_type,
        "resource_address": f"{resource_type}.main",
        "status": status,
        "severity": severity,
        "finding": "Test finding",
        "remediation": "Fix it",
        "checkov_id": checkov_id,
        "native_ids": None,
        "cross_mappings": None,
        "confidence": "high",
    }


def _make_mappings(
    *,
    check_id: str = "",
    enforcement_tier: str = "fully_automatable",
    resource_category: str = "database",
    resource_types: list[str] | None = None,
    control_id: str = "CC6.1",
) -> dict[str, object]:
    return {
        "mappings": [
            {
                "check_id": check_id,
                "control_id": control_id,
                "enforcement_tier": enforcement_tier,
                "resource_category": resource_category,
                "resource_types": resource_types or ["google_sql_database_instance"],
                "attribute_path": "encryption_configuration",
                "operator": "not_empty",
                "expected": None,
            }
        ]
    }


class TestCheckovCustomGenerator:
    def test_format_id(self, generator: CheckovCustomGenerator) -> None:
        assert generator.FORMAT_ID == "checkov"

    def test_tier_feature(self, generator: CheckovCustomGenerator) -> None:
        assert generator.TIER_FEATURE == "checkov_custom"

    def test_format_name(self, generator: CheckovCustomGenerator) -> None:
        assert generator.format_name() == "Custom Checkov"

    def test_skip_when_checkov_ids_nonempty(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding(checkov_id="CKV_GCP_6")]
        mappings = _make_mappings(check_id="CKV_GCP_6")
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        # Only __init__.py, no policy files
        assert result.artifact_count == 0

    def test_skip_evidence_automatable(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding()]
        mappings = _make_mappings(enforcement_tier="evidence_automatable")
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        assert result.artifact_count == 0

    def test_skip_human_only(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding()]
        mappings = _make_mappings(enforcement_tier="human_only")
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        assert result.artifact_count == 0

    def test_generate_when_fully_automatable(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding()]
        mappings = _make_mappings(enforcement_tier="fully_automatable")
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        assert result.artifact_count == 1
        # Check file exists
        policy_files = [f for f in result.files_written if f.name.startswith("CKV_")]
        assert len(policy_files) == 1

    def test_id_convention(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding()]
        mappings = _make_mappings()
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        policy_files = [f for f in result.files_written if f.name.startswith("CKV_")]
        assert policy_files[0].name == "CKV_COMPLYFORM_GCP_001.py"

    def test_generated_contains_base_resource_check(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        findings = [_make_finding()]
        mappings = _make_mappings()
        result = generator.generate(findings, mappings, "gcp", "pro", tmp_path)
        policy_files = [f for f in result.files_written if f.name.startswith("CKV_")]
        content = policy_files[0].read_text()
        assert "BaseResourceCheck" in content

    def test_empty_findings(
        self, generator: CheckovCustomGenerator, tmp_path: Path
    ) -> None:
        result = generator.generate([], {}, "gcp", "pro", tmp_path)
        assert result.artifact_count == 0

    @pytest.mark.parametrize(
        ("tier", "expected"),
        [
            ("community", False),
            ("pro", True),
            ("team", True),
            ("agency", True),
            ("enterprise", True),
            ("enterprise_unlimited", True),
        ],
    )
    def test_supports_tier(
        self, generator: CheckovCustomGenerator, tier: str, expected: bool
    ) -> None:
        assert generator.supports_tier(tier) is expected


class TestCategoryMapping:
    @pytest.mark.parametrize(
        ("category", "expected"),
        [
            ("database", "ENCRYPTION"),
            ("object_storage", "ENCRYPTION"),
            ("disk", "ENCRYPTION"),
            ("kms", "ENCRYPTION"),
            ("iam", "IAM"),
            ("network", "NETWORKING"),
            ("load_balancer", "NETWORKING"),
            ("api_gateway", "NETWORKING"),
            ("dns", "NETWORKING"),
            ("cdn", "NETWORKING"),
            ("logging", "LOGGING"),
            ("monitoring", "LOGGING"),
            ("compute", "GENERAL_SECURITY"),
            ("container", "GENERAL_SECURITY"),
            ("serverless", "GENERAL_SECURITY"),
            ("unknown", "GENERAL_SECURITY"),
        ],
    )
    def test_category_mapping(self, category: str, expected: str) -> None:
        assert _get_category(category) == expected


class TestCheckovModels:
    def test_policy_dataclass(self) -> None:
        policy = CheckovCustomPolicy(
            check_id="CKV_COMPLYFORM_GCP_001",
            class_name="CkvComplyformGcp001",
            name="Test",
            control_id="CC6.1",
            framework="soc2",
            resource_type="google_sql_database_instance",
            supported_resources=("google_sql_database_instance",),
            category="ENCRYPTION",
            attribute_path="encryption",
            operator="not_empty",
            expected=None,
            failure_message="fail",
            cloud="gcp",
            generated_at="2024-01-01",
            tool_version="0.1.0",
        )
        assert policy.check_id == "CKV_COMPLYFORM_GCP_001"

    def test_bundle_dataclass(self) -> None:
        bundle = CheckovCustomBundle(cloud="gcp")
        assert bundle.policies == []
        assert bundle.id_counter == {}
