"""IaC binary detection — resolve tofu or terraform on PATH.

Used by remediation/validation phases. Built in Phase 2 per architecture.
"""

from __future__ import annotations

import os
import shutil
import subprocess
from dataclasses import dataclass

from complyform.core.errors import IaCBinaryNotFoundError

_cached_binary: IaCBinary | None = None


@dataclass(frozen=True)
class IaCBinary:
    """Resolved IaC binary info."""

    path: str
    name: str
    version: str


def _parse_version(output: str) -> str:
    """Extract version from 'Terraform vX.Y.Z' or 'OpenTofu vX.Y.Z'."""
    for line in output.strip().splitlines():
        line = line.strip()
        if line.startswith(("Terraform v", "OpenTofu v")):
            return line.split("v", 1)[1].split()[0]
    return "unknown"


def _try_resolve(binary: str) -> IaCBinary | None:
    """Attempt to resolve a binary and extract its version."""
    resolved = shutil.which(binary)
    if resolved is None:
        return None
    try:
        result = subprocess.run(
            [resolved, "version"],
            capture_output=True,
            text=True,
            timeout=10,
        )
        version = _parse_version(result.stdout)
    except (
        subprocess.TimeoutExpired,
        FileNotFoundError,
        OSError,
    ):
        version = "unknown"

    name = "tofu" if "tofu" in os.path.basename(resolved) else "terraform"
    return IaCBinary(path=resolved, name=name, version=version)


def resolve_iac_binary() -> IaCBinary:
    """Resolve IaC binary. Cached per process.

    Resolution order:
    1. COMPLYFORM_IAC_BINARY env var
    2. tofu on PATH
    3. terraform on PATH
    4. Raise IaCBinaryNotFoundError
    """
    global _cached_binary  # noqa: PLW0603
    if _cached_binary is not None:
        return _cached_binary

    env_binary = os.environ.get("COMPLYFORM_IAC_BINARY")
    if env_binary:
        result = _try_resolve(env_binary)
        if result is not None:
            _cached_binary = result
            return result
        raise IaCBinaryNotFoundError

    for candidate in ("tofu", "terraform"):
        result = _try_resolve(candidate)
        if result is not None:
            _cached_binary = result
            return result

    raise IaCBinaryNotFoundError


def _reset_cache() -> None:
    """Reset cached binary (testing only)."""
    global _cached_binary  # noqa: PLW0603
    _cached_binary = None
