"""Tests for complyform.core.remediator.patch_generator."""

from __future__ import annotations

from complyform.core.assessor.control_matcher import Finding
from complyform.core.profiles.models import (
    Dependency,
    ResourceMapping,
)
from complyform.core.remediator.patch_generator import (
    PatchGenerator,
    _merge_attributes,
    _render_hcl_value,
    _sanitize_filename,
)
from complyform.core.scanner.state_parser import ResourceRecord


def _finding(
    control_id: str = "CC6.1",
    framework: str = "soc2",
    resource_address: str = "google_sql_database_instance.main",
    resource_type: str = "google_sql_database_instance",
    status: str = "FAIL",
    severity: str = "HIGH",
) -> Finding:
    return Finding(
        control_id=control_id,
        framework=framework,
        resource_address=resource_address,
        resource_type=resource_type,
        status=status,
        severity=severity,
        finding=f"Control {control_id} failed",
        remediation="Fix it",
        checkov_id="CKV_GCP_6",
        cross_mappings=None,
        native_ids=None,
        confidence="high",
    )


def _resource(
    address: str = "google_sql_database_instance.main",
    resource_type: str = "google_sql_database_instance",
) -> ResourceRecord:
    return ResourceRecord(
        provider="google",
        resource_type=resource_type,
        name=address.rsplit(".", 1)[-1],
        address=address,
        attributes={
            "id": "test-instance",
            "name": "main",
            "project": "my-project",
            "settings": {"tier": "db-f1-micro"},
        },
        module=None,
        source_state="test",
    )


def _mapping(
    control_id: str = "CC6.1",
    resource_type: str = "google_sql_database_instance",
    required_config: dict[str, object] | None = None,
    dependencies: list[Dependency] | None = None,
) -> ResourceMapping:
    return ResourceMapping(
        check_id="CKV_GCP_6",
        control_id=control_id,
        resource_types=[resource_type],
        required_config=required_config
        or {"settings": {"ip_configuration": {"ssl_mode": "ENCRYPTED_ONLY"}}},
        dependencies=dependencies or [],
    )


class TestStateOnlyStrategy:
    def test_generates_import_block_and_resource(self) -> None:
        gen = PatchGenerator()
        findings = [_finding()]
        resources = [_resource()]
        mappings = {"CC6.1:google_sql_database_instance": _mapping()}

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert len(patches) == 1
        patch = patches[0]
        assert patch.strategy == "state_only"
        assert "import {" in patch.content
        assert "google_sql_database_instance" in patch.content
        assert "ssl_mode" in patch.content
        assert patch.resource_address == ("google_sql_database_instance.main")

    def test_required_config_merged(self) -> None:
        gen = PatchGenerator()
        findings = [_finding()]
        resources = [_resource()]
        mappings = {
            "CC6.1:google_sql_database_instance": _mapping(
                required_config={"encryption_enabled": True}
            )
        }

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert len(patches) == 1
        assert "encryption_enabled" in patches[0].content


class TestDependencies:
    def test_dependency_generates_resource_block(self) -> None:
        dep = Dependency(
            resource_type="google_kms_crypto_key",
            template='name = "main-key"',
            risk_level="medium",
        )
        gen = PatchGenerator()
        findings = [_finding()]
        resources = [_resource()]
        mappings = {"CC6.1:google_sql_database_instance": _mapping(dependencies=[dep])}

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert len(patches) == 1
        assert "google_kms_crypto_key" in patches[0].content
        assert patches[0].risk_level == "medium"
        assert len(patches[0].dependencies) == 1

    def test_duplicate_deps_deduplicated(self) -> None:
        dep = Dependency(
            resource_type="google_kms_key_ring",
            template='name = "keyring"',
            risk_level="low",
        )
        gen = PatchGenerator()
        f1 = _finding(control_id="CC6.1")
        f2 = _finding(
            control_id="CC6.7",
            resource_address="google_sql_database_instance.secondary",
        )
        r1 = _resource()
        r2 = _resource(address="google_sql_database_instance.secondary")
        m1 = _mapping(control_id="CC6.1", dependencies=[dep])
        m2 = _mapping(control_id="CC6.7", dependencies=[dep])
        mappings = {
            "CC6.1:google_sql_database_instance": m1,
            "CC6.7:google_sql_database_instance": m2,
        }

        patches = gen.generate_patches([f1, f2], mappings, [r1, r2], "gcp")

        # Dep should only appear once across all patches
        dep_content = sum(p.content.count("google_kms_key_ring") for p in patches)
        # At least one patch has the dep, others don't duplicate
        assert dep_content >= 1


class TestPatchFileNaming:
    def test_follows_convention(self) -> None:
        gen = PatchGenerator()
        findings = [_finding()]
        resources = [_resource()]
        mappings = {"CC6.1:google_sql_database_instance": _mapping()}

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert patches[0].filename == ("google_sql_database_instance_main_CC6.1.tf")


class TestDiffGeneration:
    def test_diff_generated(self) -> None:
        gen = PatchGenerator()
        findings = [_finding()]
        resources = [_resource()]
        mappings = {"CC6.1:google_sql_database_instance": _mapping()}

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert patches[0].diff
        assert "+++" in patches[0].diff


class TestMultipleFindingsSameResource:
    def test_consolidated(self) -> None:
        gen = PatchGenerator()
        f1 = _finding(control_id="CC6.1")
        f2 = _finding(control_id="CC6.7")
        resources = [_resource()]
        m1 = _mapping(control_id="CC6.1")
        m2 = _mapping(
            control_id="CC6.7",
            required_config={"backup_enabled": True},
        )
        mappings = {
            "CC6.1:google_sql_database_instance": m1,
            "CC6.7:google_sql_database_instance": m2,
        }

        patches = gen.generate_patches([f1, f2], mappings, resources, "gcp")

        # Each finding gets its own patch file
        assert len(patches) == 2


class TestPassFindingsIgnored:
    def test_pass_findings_not_patched(self) -> None:
        gen = PatchGenerator()
        findings = [_finding(status="PASS")]
        resources = [_resource()]
        mappings = {"CC6.1:google_sql_database_instance": _mapping()}

        patches = gen.generate_patches(findings, mappings, resources, "gcp")

        assert len(patches) == 0


class TestHelpers:
    def test_sanitize_filename(self) -> None:
        assert _sanitize_filename("CC6.1") == "CC6.1"
        assert _sanitize_filename("a/b:c") == "a_b_c"

    def test_render_hcl_value_bool(self) -> None:
        assert _render_hcl_value(True) == "true"
        assert _render_hcl_value(False) == "false"

    def test_render_hcl_value_string(self) -> None:
        assert _render_hcl_value("hello") == '"hello"'

    def test_render_hcl_value_int(self) -> None:
        assert _render_hcl_value(42) == "42"

    def test_render_hcl_value_list(self) -> None:
        assert _render_hcl_value([1, 2]) == "[1, 2]"

    def test_merge_attributes_deep(self) -> None:
        base = {"a": {"b": 1, "c": 2}, "d": 3}
        required = {"a": {"c": 99, "e": 4}}
        result = _merge_attributes(base, required)
        assert result["a"]["b"] == 1
        assert result["a"]["c"] == 99
        assert result["a"]["e"] == 4
        assert result["d"] == 3
