"""CLI for abi-spec — compile and validate SpecPacks."""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

from abi_spec.compiler import compile_prd_json_to_specpack, validate_specpack
from abi_spec.sdd import SpecTracker
from abi_spec.traceability import generate_traceability_matrix


def cmd_compile(args: argparse.Namespace) -> int:
    """Compile a PRD JSON to a SpecPack."""
    try:
        result = compile_prd_json_to_specpack(args.prd_json, args.out)
        print(f"spec_id: compiled from {args.prd_json}")
        print(f"prd_hash: {result['prd_hash']}")
        print(f"manifest_hash: {result['manifest_hash']}")
        print(f"output: {args.out}")
        return 0
    except (ValueError, Exception) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1


def cmd_validate(args: argparse.Namespace) -> int:
    """Validate an existing SpecPack directory."""
    try:
        validate_specpack(args.specpack_dir)
        print(f"PASS: {args.specpack_dir} is valid")
        return 0
    except (ValueError, Exception) as exc:
        print(f"FAIL: {exc}", file=sys.stderr)
        return 1


def cmd_traceability(args: argparse.Namespace) -> int:
    """Generate a requirements traceability matrix."""
    try:
        specpack_dir = Path(args.specpack_dir)
        manifest_path = specpack_dir / "spec_manifest.json"

        if not manifest_path.exists():
            print(f"ERROR: spec_manifest.json not found in {specpack_dir}", file=sys.stderr)
            return 1

        # Load spec manifest
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
        requirements_index = manifest.get("requirements_index", [])

        # Load test results if provided
        test_results = {}
        if args.test_results:
            test_results_path = Path(args.test_results)
            test_results = json.loads(test_results_path.read_text(encoding="utf-8"))

        # Generate traceability matrix
        matrix = generate_traceability_matrix(requirements_index, test_results)

        # Output to file or stdout
        if args.output:
            output_path = Path(args.output)
            output_path.write_text(
                json.dumps(matrix, indent=2, sort_keys=True), encoding="utf-8"
            )
            print(f"Traceability matrix written to: {args.output}")
        else:
            print(json.dumps(matrix, indent=2, sort_keys=True))

        # Print summary
        summary = matrix["summary"]
        print(f"\nSummary:", file=sys.stderr)
        print(
            f"  Total requirements: {summary['total_requirements']}", file=sys.stderr
        )
        print(
            f"  Covered: {summary['covered_requirements']}", file=sys.stderr
        )
        print(
            f"  Uncovered: {summary['uncovered_requirements']}", file=sys.stderr
        )
        print(f"  Passed: {summary['passed_requirements']}", file=sys.stderr)
        print(f"  Failed: {summary['failed_requirements']}", file=sys.stderr)
        print(f"  Pending: {summary['pending_requirements']}", file=sys.stderr)

        return 0
    except (ValueError, Exception) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1


def cmd_drift(args: argparse.Namespace) -> int:
    """Check for drift between spec and code."""
    try:
        tracker = SpecTracker()

        if args.drift_command == "check":
            # Detect drift
            result = tracker.detect_drift(args.spec, args.code)

            print(f"Drift Detection Results:")
            print(f"  Spec: {args.spec}")
            print(f"  Code: {', '.join(args.code)}")
            print(f"  Has Drift: {result.has_drift}")
            print(f"  Spec Changed Since Code: {result.spec_changed_since_code}")
            print(f"  Code Changed Since Spec: {result.code_changed_since_spec}")
            print(f"  Details: {result.details}")

            return 1 if result.has_drift else 0

        elif args.drift_command == "track":
            # Track spec
            spec_version = tracker.track(args.spec, args.code)

            print(f"Spec Tracked:")
            print(f"  Spec ID: {spec_version.spec_id}")
            print(f"  Version: {spec_version.version}")
            print(f"  Content Hash: {spec_version.content_hash}")
            print(f"  Updated At: {spec_version.updated_at}")
            print(f"  Linked Code Paths: {', '.join(spec_version.linked_code_paths)}")

            return 0

        else:
            print(f"ERROR: Unknown drift command: {args.drift_command}", file=sys.stderr)
            return 1

    except FileNotFoundError as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1
    except (ValueError, Exception) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1


def main() -> int:
    parser = argparse.ArgumentParser(prog="abi_spec", description="ABI Spec CLI")
    sub = parser.add_subparsers(dest="command")

    p_compile = sub.add_parser("compile", help="Compile PRD JSON to SpecPack")
    p_compile.add_argument("prd_json", help="Path to PRD JSON file")
    p_compile.add_argument("--out", required=True, help="Output directory for SpecPack")

    p_validate = sub.add_parser("validate", help="Validate an existing SpecPack")
    p_validate.add_argument("specpack_dir", help="Path to SpecPack directory")

    p_trace = sub.add_parser("traceability", help="Generate requirements traceability matrix")
    p_trace.add_argument("specpack_dir", help="Path to SpecPack directory")
    p_trace.add_argument(
        "--test-results",
        help="Path to test results JSON file",
    )
    p_trace.add_argument(
        "--output", "-o", help="Output file for traceability matrix (default: stdout)"
    )

    p_drift = sub.add_parser("drift", help="Spec-Driven Development drift detection")
    drift_sub = p_drift.add_subparsers(dest="drift_command")

    # drift check subcommand
    p_drift_check = drift_sub.add_parser("check", help="Check for drift between spec and code")
    p_drift_check.add_argument("--spec", required=True, help="Path to specification file")
    p_drift_check.add_argument(
        "--code",
        nargs="+",
        required=True,
        help="Path(s) to code file(s) or directory(ies)",
    )

    # drift track subcommand
    p_drift_track = drift_sub.add_parser("track", help="Track a spec and its linked code")
    p_drift_track.add_argument("--spec", required=True, help="Path to specification file")
    p_drift_track.add_argument(
        "--code",
        nargs="+",
        required=True,
        help="Path(s) to code file(s) or directory(ies)",
    )

    args = parser.parse_args()

    if args.command == "compile":
        return cmd_compile(args)
    elif args.command == "validate":
        return cmd_validate(args)
    elif args.command == "traceability":
        return cmd_traceability(args)
    elif args.command == "drift":
        return cmd_drift(args)
    else:
        parser.print_help()
        return 1


if __name__ == "__main__":
    sys.exit(main())
