"""Tests for complyform.cli.scan_cmd."""

from __future__ import annotations

import json
from pathlib import Path

import pytest
from typer.testing import CliRunner

from complyform.cli.main import app

runner = CliRunner()

FIXTURES = Path(__file__).parent / "fixtures"


class TestScanTerminalOutput:
    def test_renders_table(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "gcp",
            ],
        )
        assert result.exit_code == 0
        assert "Resource Inventory" in result.output
        assert "google_compute_instan" in result.output


class TestScanJSONOutput:
    def test_valid_json_envelope(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "gcp",
                "--format",
                "json",
            ],
        )
        assert result.exit_code == 0
        # JSON goes to stdout; find the JSON block in output
        # The output may contain stderr mixed in, so find the JSON
        lines = result.output.strip().splitlines()
        # Find the start of JSON
        json_start = 0
        for i, line in enumerate(lines):
            if line.strip().startswith("{"):
                json_start = i
                break
        json_text = "\n".join(lines[json_start:])
        envelope = json.loads(json_text)
        assert envelope["schema_version"] == 1
        assert envelope["tool"] == "complyform"
        assert envelope["command"] == "scan"
        assert envelope["cloud"] == "gcp"
        assert envelope["scan_source"] == "state"
        assert "sha256:" in envelope["state_hash"]
        assert len(envelope["resources"]) == 5
        assert envelope["metadata"]["resource_count"] == 5
        assert envelope["metadata"]["terraform_version"] == "1.9.0"


class TestScanDefaultStatePath:
    def test_defaults_to_terraform_tfstate(
        self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        monkeypatch.chdir(tmp_path)
        src = FIXTURES / "gcp_state.json"
        (tmp_path / "terraform.tfstate").write_bytes(src.read_bytes())
        result = runner.invoke(app, ["scan", "--cloud", "gcp", "--format", "json"])
        assert result.exit_code == 0
        lines = result.output.strip().splitlines()
        json_start = next(
            i for i, line in enumerate(lines) if line.strip().startswith("{")
        )
        envelope = json.loads("\n".join(lines[json_start:]))
        assert len(envelope["resources"]) == 5


class TestScanInvalidCloud:
    def test_bad_cloud_value(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "oracle",
            ],
        )
        assert result.exit_code != 0


class TestScanRemoteStateBlocked:
    @pytest.mark.parametrize(
        "prefix",
        [
            "gs://bucket/state",
            "s3://bucket/state",
            "azure://container/state",
            "app.terraform.io/org/ws",
        ],
    )
    def test_remote_blocked(self, prefix: str) -> None:
        result = runner.invoke(
            app,
            ["scan", "--state", prefix, "--cloud", "gcp"],
        )
        assert result.exit_code != 0


class TestScanAPISourceBlocked:
    def test_api_not_available(self) -> None:
        result = runner.invoke(
            app,
            ["scan", "--source", "api", "--cloud", "gcp"],
        )
        assert result.exit_code != 0


class TestScanWritesCache:
    def test_cache_written(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "gcp",
                "--format",
                "json",
            ],
        )
        assert result.exit_code == 0


class TestScanExitCodes:
    def test_success_exit_0(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "gcp",
            ],
        )
        assert result.exit_code == 0

    def test_runtime_error_exit_nonzero(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                "/nonexistent/path.tfstate",
                "--cloud",
                "gcp",
            ],
        )
        assert result.exit_code != 0


class TestScanFutureFlags:
    def test_discover_nonexistent_dir(self) -> None:
        result = runner.invoke(
            app,
            ["scan", "--discover", "/nonexistent/dir", "--cloud", "gcp"],
        )
        # Raises DiscoverNoStatesFoundError
        assert result.exit_code != 0

    def test_batch_on_community_blocked(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--batch",
                "manifest.yaml",
                "--cloud",
                "gcp",
            ],
        )
        assert result.exit_code != 0

    def test_assess_chain_requires_frameworks(self) -> None:
        result = runner.invoke(
            app,
            [
                "scan",
                "--state",
                str(FIXTURES / "gcp_state.json"),
                "--cloud",
                "gcp",
                "--assess",
            ],
        )
        assert result.exit_code != 0
