"""CLI wrapper for the raxit binary."""

from __future__ import annotations

import json
import os
import platform
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Any


class RaxitError(Exception):
    """Raised when the raxit binary returns a non-zero exit code."""

    def __init__(self, returncode: int, stderr: str) -> None:
        self.returncode = returncode
        self.stderr = stderr
        super().__init__(f"raxit exited with code {returncode}: {stderr}")


def _find_binary() -> str:
    """Locate the raxit binary.

    Search order:
    1. Bundled binary at raxit/bin/raxit (or raxit.exe on Windows)
    2. Binary on PATH
    """
    # Check bundled binary
    bin_name = "raxit.exe" if platform.system() == "Windows" else "raxit"
    bundled = Path(__file__).parent / "bin" / bin_name
    if bundled.is_file():
        return str(bundled)

    # Check PATH
    on_path = shutil.which("raxit")
    if on_path:
        return on_path

    raise FileNotFoundError(
        "raxit binary not found. Install it via: "
        "cargo install raxit, or download from GitHub releases."
    )


def _run(args: list[str], *, check: bool = True) -> subprocess.CompletedProcess[str]:
    """Run the raxit binary with given arguments."""
    binary = _find_binary()
    result = subprocess.run(
        [binary, *args],
        capture_output=True,
        text=True,
    )
    if check and result.returncode != 0:
        raise RaxitError(result.returncode, result.stderr)
    return result


def scan(
    path: str | os.PathLike[str] = ".",
    *,
    format: str = "json",
    exclude: list[str] | None = None,
    parallel: int = 0,
    verbose: bool = False,
) -> dict[str, Any]:
    """Scan a project and return the parsed Agent Assets Schema.

    Args:
        path: Path to the project to scan.
        format: Output format (only "json" is used internally).
        exclude: Glob patterns to exclude.
        parallel: Number of parallel workers (0 = auto).
        verbose: Enable verbose logging.

    Returns:
        Parsed schema as a dictionary.
    """
    args = ["init", str(path), "--format", "json", "--output", "-"]

    if exclude:
        for pattern in exclude:
            args.extend(["--exclude", pattern])
    if parallel > 0:
        args.extend(["--parallel", str(parallel)])
    if verbose:
        args.append("--verbose")

    result = _run(args)
    return json.loads(result.stdout)


def scan_raw(
    path: str | os.PathLike[str] = ".",
    *,
    format: str = "yaml",
    exclude: list[str] | None = None,
    parallel: int = 0,
    verbose: bool = False,
) -> str:
    """Scan a project and return the raw output string.

    Args:
        path: Path to the project to scan.
        format: Output format ("yaml" or "json").
        exclude: Glob patterns to exclude.
        parallel: Number of parallel workers (0 = auto).
        verbose: Enable verbose logging.

    Returns:
        Raw schema string in the requested format.
    """
    args = ["init", str(path), "--format", format, "--output", "-"]

    if exclude:
        for pattern in exclude:
            args.extend(["--exclude", pattern])
    if parallel > 0:
        args.extend(["--parallel", str(parallel)])
    if verbose:
        args.append("--verbose")

    result = _run(args)
    return result.stdout


def version() -> str:
    """Return the raxit binary version string."""
    result = _run(["--version"])
    return result.stdout.strip()
