"""Checkov subprocess wrapper for validation."""

from __future__ import annotations

import json
import shutil
import subprocess
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from complyform.core.errors import CheckovNotFoundError, ComplyFormError

if TYPE_CHECKING:
    from pathlib import Path

_DEFAULT_TIMEOUT = 30


@dataclass
class CheckovResult:
    """Single Checkov check result."""

    check_id: str
    check_name: str
    status: str  # "PASSED" | "FAILED"
    resource: str
    file_path: str
    guideline: str | None


class CheckovRunner:
    """Run Checkov and parse results."""

    def __init__(self, checkov_path: str | None = None) -> None:
        self._path = checkov_path

    def _resolve_path(self) -> str:
        if self._path:
            return self._path
        found = shutil.which("checkov")
        if found is None:
            raise CheckovNotFoundError()
        return found

    def is_available(self) -> bool:
        """Check if Checkov is installed and accessible."""
        if self._path:
            return shutil.which(self._path) is not None
        return shutil.which("checkov") is not None

    def run(
        self,
        target_dir: Path,
        framework: str = "terraform",
        check_ids: list[str] | None = None,
        timeout: int = _DEFAULT_TIMEOUT,
    ) -> list[CheckovResult]:
        """Run Checkov and return parsed results."""
        path = self._resolve_path()

        cmd = [
            path,
            "-d",
            str(target_dir),
            "--framework",
            framework,
            "--output",
            "json",
            "--compact",
            "--quiet",
        ]

        if check_ids:
            cmd.extend(["--check", ",".join(check_ids)])

        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout,
            )
        except FileNotFoundError:
            raise CheckovNotFoundError() from None
        except subprocess.TimeoutExpired:
            raise ComplyFormError(
                title="Checkov Timeout",
                message=(f"Checkov timed out after {timeout}s."),
                fix=("Try running on a smaller directory or increase timeout."),
            ) from None

        return self._parse_output(result.stdout)

    def _parse_output(self, raw: str) -> list[CheckovResult]:
        """Parse Checkov JSON output into results."""
        if not raw.strip():
            return []

        try:
            data: Any = json.loads(raw)
        except json.JSONDecodeError:
            raise ComplyFormError(
                title="Checkov Output Error",
                message="Could not parse Checkov output.",
                fix="Run Checkov manually to verify output.",
            ) from None

        results: list[CheckovResult] = []

        if isinstance(data, list):
            for block in data:
                results.extend(self._extract_checks(block))
        elif isinstance(data, dict):
            results.extend(self._extract_checks(data))

        return results

    def _extract_checks(self, block: dict[str, Any]) -> list[CheckovResult]:
        """Extract checks from a single Checkov output block."""
        results: list[CheckovResult] = []

        for section_key in ("passed_checks", "failed_checks"):
            checks = block.get("results", {}).get(section_key, [])
            status = "PASSED" if section_key == "passed_checks" else "FAILED"
            for check in checks:
                results.append(
                    CheckovResult(
                        check_id=check.get("check_id", ""),
                        check_name=check.get("check_name", ""),
                        status=status,
                        resource=check.get("resource", ""),
                        file_path=check.get("file_path", ""),
                        guideline=check.get("guideline"),
                    )
                )

        return results
