"""
Code quality domain command handlers.

SST Reference: packages/lightwave-core/lightwave/schema/definitions/cli.yaml
Domain: quality (runtime: python)

These commands provide code quality tools including linting, formatting, and CI.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any


def _find_project_root() -> Path | None:
    """Find the project root directory."""
    cwd = Path.cwd()

    # Look for common markers
    markers = ["pyproject.toml", "CLAUDE.md", ".git"]

    for parent in [cwd, *cwd.parents]:
        for marker in markers:
            if (parent / marker).exists():
                return parent

    return cwd


def _run_command(
    cmd: list[str],
    *,
    cwd: Path | None = None,
    check: bool = True,
    capture_output: bool = False,
) -> subprocess.CompletedProcess[str]:
    """Run a command with consistent options."""
    return subprocess.run(
        cmd,
        cwd=cwd,
        check=check,
        text=True,
        capture_output=capture_output,
    )


def quality_lint(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run ruff + eslint linters.

    SST: domains.quality.commands[name=lint]
    flags: [--fix, --path]
    """
    fix = "--fix" in args
    path = None

    for i, arg in enumerate(args):
        if arg == "--path" and i + 1 < len(args):
            path = args[i + 1]
            break

    project_root = _find_project_root()

    if dry_run:
        steps = []
        if path:
            steps.append(f"ruff check {'--fix ' if fix else ''}{path}")
        else:
            steps.append(f"ruff check {'--fix ' if fix else ''}.")
        steps.append("pnpm eslint {'--fix ' if fix else ''}.")

        if not json_output:
            print("[dry-run] Would run:")
            for step in steps:
                print(f"  {step}")
        return {"success": True, "dry_run": True, "steps": steps}

    results = {"success": True, "ruff": None, "eslint": None}
    errors = []

    # Run ruff
    ruff_cmd = ["ruff", "check"]
    if fix:
        ruff_cmd.append("--fix")
    ruff_cmd.append(path or ".")

    if verbose:
        print(f"Running: {' '.join(ruff_cmd)}")

    try:
        _run_command(ruff_cmd, cwd=project_root)
        results["ruff"] = "passed"
        if not json_output:
            print("✓ ruff check passed")
    except subprocess.CalledProcessError as e:
        results["ruff"] = "failed"
        results["success"] = False
        errors.append(f"ruff: exit code {e.returncode}")
        if not json_output:
            print("✗ ruff check failed")
    except FileNotFoundError:
        results["ruff"] = "not installed"
        if not json_output:
            print("⚠ ruff not installed")

    # Run eslint (if package.json exists)
    package_json = project_root / "package.json" if project_root else Path("package.json")
    if package_json.exists():
        eslint_cmd = ["pnpm", "eslint"]
        if fix:
            eslint_cmd.append("--fix")
        eslint_cmd.append(path or ".")

        if verbose:
            print(f"Running: {' '.join(eslint_cmd)}")

        try:
            _run_command(eslint_cmd, cwd=project_root)
            results["eslint"] = "passed"
            if not json_output:
                print("✓ eslint check passed")
        except subprocess.CalledProcessError as e:
            results["eslint"] = "failed"
            results["success"] = False
            errors.append(f"eslint: exit code {e.returncode}")
            if not json_output:
                print("✗ eslint check failed")
        except FileNotFoundError:
            results["eslint"] = "not installed"
            if not json_output:
                print("⚠ eslint not installed")
    else:
        results["eslint"] = "skipped (no package.json)"

    if json_output:
        if errors:
            results["errors"] = errors
        return results

    if not results["success"]:
        sys.exit(1)

    return None


def quality_format(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run ruff format + prettier formatters.

    SST: domains.quality.commands[name=format]
    flags: [--check, --path]
    """
    check_only = "--check" in args
    path = None

    for i, arg in enumerate(args):
        if arg == "--path" and i + 1 < len(args):
            path = args[i + 1]
            break

    project_root = _find_project_root()

    if dry_run:
        mode = "check" if check_only else "format"
        steps = [
            f"ruff format {'--check ' if check_only else ''}{path or '.'}",
            f"pnpm prettier {'--check ' if check_only else '--write '}{path or '.'}",
        ]

        if not json_output:
            print(f"[dry-run] Would run ({mode}):")
            for step in steps:
                print(f"  {step}")
        return {"success": True, "dry_run": True, "mode": mode, "steps": steps}

    results = {"success": True, "ruff": None, "prettier": None}
    errors = []

    # Run ruff format
    ruff_cmd = ["ruff", "format"]
    if check_only:
        ruff_cmd.append("--check")
    ruff_cmd.append(path or ".")

    if verbose:
        print(f"Running: {' '.join(ruff_cmd)}")

    try:
        _run_command(ruff_cmd, cwd=project_root)
        results["ruff"] = "passed" if check_only else "formatted"
        if not json_output:
            print(f"✓ ruff format {'passed' if check_only else 'complete'}")
    except subprocess.CalledProcessError as e:
        results["ruff"] = "needs formatting" if check_only else "failed"
        results["success"] = False
        errors.append(f"ruff format: exit code {e.returncode}")
        if not json_output:
            print("✗ ruff format failed")
    except FileNotFoundError:
        results["ruff"] = "not installed"
        if not json_output:
            print("⚠ ruff not installed")

    # Run prettier (if package.json exists)
    package_json = project_root / "package.json" if project_root else Path("package.json")
    if package_json.exists():
        prettier_cmd = ["pnpm", "prettier"]
        if check_only:
            prettier_cmd.append("--check")
        else:
            prettier_cmd.append("--write")
        prettier_cmd.append(path or ".")

        if verbose:
            print(f"Running: {' '.join(prettier_cmd)}")

        try:
            _run_command(prettier_cmd, cwd=project_root)
            results["prettier"] = "passed" if check_only else "formatted"
            if not json_output:
                print(f"✓ prettier {'passed' if check_only else 'complete'}")
        except subprocess.CalledProcessError as e:
            results["prettier"] = "needs formatting" if check_only else "failed"
            results["success"] = False
            errors.append(f"prettier: exit code {e.returncode}")
            if not json_output:
                print("✗ prettier failed")
        except FileNotFoundError:
            results["prettier"] = "not installed"
            if not json_output:
                print("⚠ prettier not installed")
    else:
        results["prettier"] = "skipped (no package.json)"

    if json_output:
        if errors:
            results["errors"] = errors
        return results

    if not results["success"]:
        sys.exit(1)

    return None


def quality_ci(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run full CI pipeline locally.

    SST: domains.quality.commands[name=ci]
    flags: [--skip-tests]
    """
    skip_tests = "--skip-tests" in args

    steps = [
        ("lint", "Run linters (ruff + eslint)"),
        ("format", "Check formatting (ruff format + prettier)"),
        ("type-check", "Run type checking (mypy + tsc)"),
    ]

    if not skip_tests:
        steps.append(("test", "Run test suite (pytest)"))

    if dry_run:
        if not json_output:
            print("[dry-run] Would run CI pipeline:")
            for step, desc in steps:
                print(f"  {step}: {desc}")
        return {
            "success": True,
            "dry_run": True,
            "steps": [{"name": s, "description": d} for s, d in steps],
        }

    project_root = _find_project_root()
    results = {"success": True, "steps": {}}
    failed_steps = []

    for step, desc in steps:
        if not json_output:
            print(f"\n>>> {desc}")

        try:
            if step == "lint":
                quality_lint(["--path", "."], json_output=False, verbose=verbose)
                results["steps"]["lint"] = "passed"
            elif step == "format":
                quality_format(["--check"], json_output=False, verbose=verbose)
                results["steps"]["format"] = "passed"
            elif step == "type-check":
                # Run mypy
                try:
                    _run_command(["mypy", "."], cwd=project_root)
                    if not json_output:
                        print("✓ mypy passed")
                except (subprocess.CalledProcessError, FileNotFoundError):
                    if not json_output:
                        print("⚠ mypy skipped or failed")

                # Run tsc
                package_json = project_root / "package.json" if project_root else None
                if package_json and package_json.exists():
                    try:
                        _run_command(["pnpm", "tsc", "--noEmit"], cwd=project_root)
                        if not json_output:
                            print("✓ tsc passed")
                    except (subprocess.CalledProcessError, FileNotFoundError):
                        if not json_output:
                            print("⚠ tsc skipped or failed")

                results["steps"]["type-check"] = "passed"
            elif step == "test":
                _run_command(["pytest"], cwd=project_root)
                results["steps"]["test"] = "passed"
                if not json_output:
                    print("✓ tests passed")

        except subprocess.CalledProcessError as e:
            results["steps"][step] = "failed"
            results["success"] = False
            failed_steps.append(step)
            if not json_output:
                print(f"✗ {step} failed (exit code {e.returncode})")
        except SystemExit:
            results["steps"][step] = "failed"
            results["success"] = False
            failed_steps.append(step)

    if json_output:
        if failed_steps:
            results["failed_steps"] = failed_steps
        return results

    if not json_output:
        print("\n" + "=" * 50)
        if results["success"]:
            print("✓ CI pipeline passed!")
        else:
            print(f"✗ CI pipeline failed: {', '.join(failed_steps)}")
            sys.exit(1)

    return None


def quality_precommit(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run pre-commit hooks.

    SST: domains.quality.commands[name=pre-commit]
    flags: [--all-files]
    """
    all_files = "--all-files" in args

    project_root = _find_project_root()

    cmd = ["pre-commit", "run"]
    if all_files:
        cmd.append("--all-files")

    if dry_run:
        cmd_str = " ".join(cmd)
        if not json_output:
            print(f"[dry-run] Would run: {cmd_str}")
        return {"success": True, "dry_run": True, "command": cmd_str}

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    try:
        _run_command(cmd, cwd=project_root)
        if json_output:
            return {"success": True, "message": "Pre-commit hooks passed"}
        print("✓ Pre-commit hooks passed")
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": f"Pre-commit failed: exit code {e.returncode}"}
        print(f"✗ Pre-commit hooks failed (exit code {e.returncode})")
        sys.exit(e.returncode)
    except FileNotFoundError:
        if json_output:
            return {"success": False, "error": "pre-commit not installed"}
        print("Error: pre-commit not installed. Run: pip install pre-commit", file=sys.stderr)
        sys.exit(1)
