"""Infracost subprocess management.

Contract:
- All functions return parsed dict or None on any failure.
- All failures non-fatal — caller falls back to pricing APIs.
- Temp directories always cleaned up.
"""

from __future__ import annotations

import json
import shutil
import subprocess
import sys
import tempfile

__all__ = [
    "run_infracost_breakdown",
    "run_infracost_diff",
]


def run_infracost_breakdown(
    tf_path: str,
    timeout: int = 120,
) -> dict | None:  # type: ignore[type-arg]
    """Run infracost breakdown and return parsed JSON.

    Returns None if Infracost unavailable or failed.
    """
    binary = shutil.which("infracost")
    if binary is None:
        print("Warning: Infracost not installed", file=sys.stderr)
        return None

    tmp_dir = tempfile.mkdtemp(prefix="complyform-cost-")
    try:
        cmd = [
            binary,
            "breakdown",
            f"--path={tf_path}",
            "--format=json",
            "--no-color",
            "--log-level=warn",
        ]
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
            cwd=tmp_dir,
        )
        if result.returncode != 0:
            stderr_snippet = result.stderr[:200] if result.stderr else "unknown error"
            print(
                f"Warning: Infracost exited {result.returncode}: {stderr_snippet}",
                file=sys.stderr,
            )
            return None
        return json.loads(result.stdout)  # type: ignore[no-any-return]
    except FileNotFoundError:
        print("Warning: Infracost not installed", file=sys.stderr)
        return None
    except subprocess.TimeoutExpired:
        print("Warning: Infracost timed out", file=sys.stderr)
        return None
    except json.JSONDecodeError:
        print("Warning: Infracost returned invalid JSON", file=sys.stderr)
        return None
    except Exception as exc:
        print(f"Warning: Infracost error: {exc}", file=sys.stderr)
        return None
    finally:
        shutil.rmtree(tmp_dir, ignore_errors=True)


def run_infracost_diff(
    patched_path: str,
    baseline_json: str,
    timeout: int = 120,
) -> dict | None:  # type: ignore[type-arg]
    """Run infracost diff and return parsed JSON.

    Returns None if Infracost unavailable or failed.
    """
    binary = shutil.which("infracost")
    if binary is None:
        print("Warning: Infracost not installed", file=sys.stderr)
        return None

    tmp_dir = tempfile.mkdtemp(prefix="complyform-cost-")
    try:
        cmd = [
            binary,
            "diff",
            f"--path={patched_path}",
            f"--compare-to={baseline_json}",
            "--format=json",
        ]
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
            cwd=tmp_dir,
        )
        if result.returncode != 0:
            stderr_snippet = result.stderr[:200] if result.stderr else "unknown error"
            print(
                f"Warning: Infracost diff exited {result.returncode}: {stderr_snippet}",
                file=sys.stderr,
            )
            return None
        return json.loads(result.stdout)  # type: ignore[no-any-return]
    except FileNotFoundError:
        print("Warning: Infracost not installed", file=sys.stderr)
        return None
    except subprocess.TimeoutExpired:
        print("Warning: Infracost diff timed out", file=sys.stderr)
        return None
    except json.JSONDecodeError:
        print("Warning: Infracost returned invalid JSON", file=sys.stderr)
        return None
    except Exception as exc:
        print(f"Warning: Infracost error: {exc}", file=sys.stderr)
        return None
    finally:
        shutil.rmtree(tmp_dir, ignore_errors=True)
