"""CLI integration tests for the remediate command."""

from __future__ import annotations

import json
from typing import TYPE_CHECKING
from unittest.mock import patch

import pytest
from typer.testing import CliRunner

from complyform.cli.main import app

if TYPE_CHECKING:
    from pathlib import Path

runner = CliRunner()


def _mock_cached_assessment(
    fail: bool = True,
    cloud: str = "gcp",
) -> object:
    """Return a mock for ScanCache.latest()."""
    from complyform.core.scanner.state_parser import ResourceRecord

    resources = [
        ResourceRecord(
            provider="google",
            resource_type="google_sql_database_instance",
            name="main",
            address="google_sql_database_instance.main",
            attributes={
                "id": "test-instance",
                "name": "main",
                "project": "my-project",
                "settings": {"tier": "db-f1-micro"},
            },
            module=None,
            source_state="test",
        ),
    ]
    meta = {
        "cloud": cloud,
        "state_hash": "sha256:abc123",
        "provider_versions": {},
    }
    return resources, meta


def _mock_findings(fail: bool = True) -> list[object]:
    """Return mock findings."""
    from complyform.core.assessor.control_matcher import Finding

    status = "FAIL" if fail else "PASS"
    return [
        Finding(
            control_id="CC6.1",
            framework="soc2",
            resource_address="google_sql_database_instance.main",
            resource_type="google_sql_database_instance",
            status=status,
            severity="HIGH",
            finding="SSL not enforced",
            remediation="Enable SSL",
            checkov_id="CKV_GCP_6",
            cross_mappings=None,
            native_ids=None,
            confidence="high",
        )
    ]


def _mock_mapping() -> object:
    """Return mock ResourceMapping."""
    from complyform.core.profiles.models import (
        MappingFile,
        ResourceMapping,
    )

    rm = ResourceMapping(
        check_id="CKV_GCP_6",
        control_id="CC6.1",
        resource_types=["google_sql_database_instance"],
        required_config={
            "settings": {"ip_configuration": {"ssl_mode": "ENCRYPTED_ONLY"}}
        },
    )
    return MappingFile(
        schema_version=1,
        framework_id="soc2",
        cloud="gcp",
        mappings=[rm],
    )


class TestRemediateNoCachedAssessment:
    def test_no_cache_raises_error(self) -> None:
        with patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache:
            mock_cache.return_value.latest.return_value = None
            result = runner.invoke(app, ["remediate"])
            assert result.exit_code != 0


class TestRemediateAllPassing:
    def test_all_passing_raises_no_findings(self) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=False),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            result = runner.invoke(app, ["remediate"])
            assert result.exit_code != 0


class TestRemediateWithPatches:
    def test_generates_patches(self, tmp_path: Path) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
            patch(
                "complyform.core.profiles.mapping_loader.load_mappings",
                return_value=_mock_mapping(),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            output = str(tmp_path / "patches")
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--output",
                    output,
                ],
            )
            assert result.exit_code == 0


class TestRemediateDryRun:
    def test_dry_run_no_files(self, tmp_path: Path) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
            patch(
                "complyform.core.profiles.mapping_loader.load_mappings",
                return_value=_mock_mapping(),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            output = str(tmp_path / "patches")
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--dry-run",
                    "--output",
                    output,
                ],
            )
            assert result.exit_code == 0
            # Directory should not exist since dry-run
            import pathlib

            assert not pathlib.Path(output).exists()


class TestRemediateJSONFormat:
    def test_json_envelope(self, tmp_path: Path) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
            patch(
                "complyform.core.profiles.mapping_loader.load_mappings",
                return_value=_mock_mapping(),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--format",
                    "json",
                    "--output",
                    str(tmp_path / "patches"),
                ],
            )
            assert result.exit_code == 0
            lines = result.output.strip().splitlines()
            json_start = 0
            for i, line in enumerate(lines):
                if line.strip().startswith("{"):
                    json_start = i
                    break
            text = "\n".join(lines[json_start:])
            envelope = json.loads(text)
            assert envelope["command"] == "remediate"
            assert "patches" in envelope
            assert isinstance(envelope["patches"], list)


class TestRemediateControlsFilter:
    def test_controls_filter(self, tmp_path: Path) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
            patch(
                "complyform.core.profiles.mapping_loader.load_mappings",
                return_value=_mock_mapping(),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--controls",
                    "CC6.1",
                    "--output",
                    str(tmp_path / "patches"),
                ],
            )
            assert result.exit_code == 0


class TestRemediateFrameworksFilter:
    def test_frameworks_filter_no_match(self) -> None:
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--frameworks",
                    "nonexistent",
                ],
            )
            # No findings for this framework → NoFindingsToRemediate
            assert result.exit_code != 0


class TestCIFlagsTierGated:
    @pytest.mark.parametrize(
        "flag",
        [
            "--estimate-cost",
            "--iam-analysis",
        ],
    )
    def test_ci_flag_tier_gated(self, flag: str) -> None:
        """CI flags trigger tier enforcement, not future-release stub."""
        result = runner.invoke(app, ["remediate", flag])
        # Tier enforcement or credential error — not stub msg
        assert "future release" not in (result.output or "").lower()


class TestInteractivePRConflict:
    def test_interactive_open_pr_conflict(self) -> None:
        result = runner.invoke(
            app,
            [
                "remediate",
                "--interactive",
                "--open-pr",
            ],
        )
        assert result.exit_code != 0

    def test_interactive_json_conflict(self) -> None:
        result = runner.invoke(
            app,
            [
                "remediate",
                "--interactive",
                "--format",
                "json",
            ],
        )
        assert result.exit_code != 0


class TestInteractiveNotTTY:
    def test_not_tty_raises(self) -> None:
        with patch("sys.stdin") as mock_stdin:
            mock_stdin.isatty.return_value = False
            result = runner.invoke(
                app,
                ["remediate", "--interactive"],
            )
            assert result.exit_code != 0


class TestRemediationTierError:
    def test_no_required_config_raises_tier_error(self, tmp_path: Path) -> None:
        from complyform.core.profiles.models import (
            MappingFile,
            ResourceMapping,
        )

        rm = ResourceMapping(
            check_id="CKV_GCP_6",
            control_id="CC6.1",
            resource_types=["google_sql_database_instance"],
            required_config=None,
        )
        mf = MappingFile(
            schema_version=1,
            framework_id="soc2",
            cloud="gcp",
            mappings=[rm],
        )
        with (
            patch("complyform.cli.remediate_cmd.ScanCache") as mock_cache,
            patch(
                "complyform.cli.remediate_cmd._load_findings",
                return_value=_mock_findings(fail=True),
            ),
            patch(
                "complyform.core.profiles.mapping_loader.load_mappings",
                return_value=mf,
            ),
        ):
            mock_cache.return_value.latest.return_value = _mock_cached_assessment()
            result = runner.invoke(
                app,
                [
                    "remediate",
                    "--output",
                    str(tmp_path / "patches"),
                ],
            )
            assert result.exit_code != 0
