#!/usr/bin/env python3
"""Cleave CLI - Thin wrapper around cleave library.

This CLI provides command-line access to the cleave library's functionality.
All core logic lives in cleave.core.* modules.

Usage:
    cleave init --directive "..." --children '["A", "B", "C"]'
    cleave assess --directive "Add JWT auth..."
    cleave match --directive "Add Stripe payment processing"
    cleave context --manifest .cleave/manifest.yaml
    cleave conflicts --results ".cleave/0-task.md,.cleave/1-task.md"
    cleave reunify --workspace .cleave
    cleave install-skill
"""

from __future__ import annotations

import argparse
import json
import sys

from cleave.core.assessment import (
    PATTERNS,
    assess_directive,
    calculate_complexity,
    effective_complexity,
    match_pattern,
)
from cleave.core.install import install_skill
from cleave.core.permissions import (
    check_missing_permissions,
    get_settings_path,
    infer_permissions,
)
from cleave.core.reunify import reunify_workspace
from cleave.core.workspace import init_workspace, reconstruct_context
from cleave.core.yaml_utils import parse_yaml_simple, to_yaml


def cmd_init(args: argparse.Namespace) -> int:
    """Handle init subcommand."""
    children = json.loads(args.children)

    created = init_workspace(
        directive=args.directive,
        children=children,
        output_dir=args.output,
        mode=args.mode,
        threshold=args.threshold,
        max_depth=args.max_depth,
        parent_manifest_path=args.parent,
        node_id=args.node_id,
        depth=args.depth,
        include_permissions=args.infer_permissions,
        tdd=not args.no_tdd,
    )

    # Handle MAX_DEPTH error
    if "error" in created:
        print(f"Error: {created['error']}", file=sys.stderr)
        print(f"  {created['message']}", file=sys.stderr)
        print(f"  Guidance: {created['guidance']}", file=sys.stderr)
        return 1

    print(f"Workspace initialized at {args.output}/")
    if args.depth > 0:
        print(f"  Nested cleave at depth {args.depth}, node {args.node_id}")
        print(f"  Remaining budget: {args.max_depth - args.depth} levels")

    print("\nFiles created:")
    for name in created:
        print(f"  - {name}")

    assessment = assess_directive(args.directive, args.threshold)
    print(f"\nAssessment:")
    print(f"  Complexity: {assessment.complexity}")
    print(f"  Decision: {assessment.decision}")
    print(f"  Method: {assessment.method}")
    if assessment.pattern:
        print(f"  Pattern: {assessment.pattern} ({assessment.confidence:.0%})")

    # Warn if approaching MAX_DEPTH
    remaining = args.max_depth - args.depth
    if remaining == 1:
        print(f"\n⚠ WARNING: At depth {args.depth}, only 1 level of cleaving remains.", file=sys.stderr)
        print("  Child tasks should be atomic (execute without further cleaving).", file=sys.stderr)

    # Check permissions against settings if requested
    if args.infer_permissions:
        match = match_pattern(args.directive)
        pattern_id = match.pattern_id if match else None
        perms = infer_permissions(args.directive, pattern_id)
        settings_check = check_missing_permissions(perms)

        if not settings_check["all_covered"]:
            print(f"\n⚠ MISSING PERMISSIONS:", file=sys.stderr)
            print(f"  {settings_check['missing_count']} permission(s) not in settings.local.json", file=sys.stderr)
            for perm in settings_check["missing"]:
                print(f"    - {perm}", file=sys.stderr)
            print("\n  Add these to ~/.claude/settings.local.json for fire-and-forget execution.", file=sys.stderr)
        else:
            print(f"\n✓ All {settings_check['covered_count']} inferred permissions are configured.")

    return 0


def cmd_assess(args: argparse.Namespace) -> int:
    """Handle assess subcommand."""
    if args.directive:
        result = assess_directive(args.directive, args.threshold, args.validate)
        match = match_pattern(args.directive)
        pattern_id = match.pattern_id if match else None

        output = {
            "complexity": result.complexity,
            "systems": result.systems,
            "modifiers": result.modifiers,
            "method": result.method,
            "pattern": result.pattern,
            "confidence": result.confidence,
            "decision": result.decision,
            "reasoning": result.reasoning,
            "skip_interrogation": result.skip_interrogation,
            "tier": 0 if result.skip_interrogation else (1 if result.decision == "execute" else 2),
        }

        if args.infer_permissions:
            perms = infer_permissions(args.directive, pattern_id)
            output["inferred_permissions"] = perms
            settings_check = check_missing_permissions(perms)
            output["settings_check"] = settings_check
    else:
        systems_list = [s.strip() for s in args.systems.split(",")] if args.systems else []
        modifiers = [m.strip() for m in args.modifiers.split(",")] if args.modifiers else []

        complexity = calculate_complexity(len(systems_list), modifiers)
        eff_complexity = effective_complexity(complexity, args.validate)
        decision = "execute" if eff_complexity <= args.threshold else "cleave"

        output = {
            "complexity": complexity,
            "effective_complexity": eff_complexity,
            "systems": len(systems_list),
            "systems_list": systems_list,
            "modifiers": modifiers,
            "threshold": args.threshold,
            "decision": decision,
            "reasoning": f"(1 + {len(systems_list)}) × (1 + 0.5 × {len(modifiers)}) = {complexity}. Effective: {eff_complexity}",
        }

    if args.format == "yaml":
        print(to_yaml(output))
    else:
        print(json.dumps(output, indent=2))

    return 0


def cmd_match(args: argparse.Namespace) -> int:
    """Handle match subcommand."""
    match = match_pattern(args.directive)

    if match:
        output = {
            "matched": True,
            "pattern": match.name,
            "pattern_id": match.pattern_id,
            "confidence": match.confidence,
            "keywords_matched": match.keywords_matched,
            "systems": match.systems,
            "default_modifiers": match.modifiers,
            "split_strategy": PATTERNS[match.pattern_id]["split_strategy"],
        }
    else:
        directive_lower = args.directive.lower()
        partial_matches = []
        for pid, pattern in PATTERNS.items():
            keywords_matched = [kw for kw in pattern["keywords"] if kw in directive_lower]
            if keywords_matched:
                partial_matches.append(
                    {
                        "pattern": pattern["name"],
                        "keywords_matched": keywords_matched,
                        "count": len(keywords_matched),
                    }
                )
        partial_matches.sort(key=lambda x: x["count"], reverse=True)

        output = {
            "matched": False,
            "reason": "No pattern matched with confidence >= 0.80",
            "recommendation": "Use sequential thinking for assessment",
            "partial_matches": partial_matches[:3] if partial_matches else [],
        }

    if args.format == "yaml":
        print(to_yaml(output))
    else:
        print(json.dumps(output, indent=2))

    return 0


def cmd_context(args: argparse.Namespace) -> int:
    """Handle context subcommand."""
    context = reconstruct_context(args.manifest)

    if args.format == "yaml":
        print(to_yaml(context))
    else:
        print(json.dumps(context, indent=2, default=str))

    return 0


def cmd_check_permissions(args: argparse.Namespace) -> int:
    """Handle check-permissions subcommand."""
    match = match_pattern(args.directive)
    pattern_id = match.pattern_id if match else None

    perms = infer_permissions(args.directive, pattern_id)
    settings_check = check_missing_permissions(perms)

    output = {
        "directive": args.directive,
        "pattern": match.name if match else None,
        "inferred": {
            "bundles": perms["bundles"],
            "permissions": perms["permissions"],
            "count": len(perms["permissions"]),
        },
        "settings": {
            "path": str(get_settings_path()),
            "current_permissions": settings_check["current_count"],
            "covered": settings_check["covered"],
            "missing": settings_check["missing"],
            "all_covered": settings_check["all_covered"],
        },
    }

    if args.snippet and settings_check["missing"]:
        print("# Add these to ~/.claude/settings.local.json under permissions.allow:\n")
        for perm in settings_check["missing"]:
            print(f'"{perm}",')
        return 0

    if args.format == "yaml":
        print(to_yaml(output))
    else:
        print(json.dumps(output, indent=2))

    if settings_check["all_covered"]:
        print(f"\n✓ All {len(perms['permissions'])} inferred permissions are configured.", file=sys.stderr)
    else:
        print(
            f"\n⚠ {len(settings_check['missing'])}/{len(perms['permissions'])} permissions missing from settings.local.json",
            file=sys.stderr,
        )
        print("\nTo enable fire-and-forget execution, add these permissions:", file=sys.stderr)
        for perm in settings_check["missing"]:
            print(f"  {perm}", file=sys.stderr)

    return 0 if settings_check["all_covered"] else 1


def cmd_install_skill(args: argparse.Namespace) -> int:
    """Handle install-skill subcommand."""
    result = install_skill()

    if result.success:
        if result.action == "created":
            print(f"Skill installed: {result.target_path} -> {result.source_path}")
        elif result.action == "updated":
            print(f"Skill symlink updated: {result.target_path} -> {result.source_path}")
        elif result.action == "skipped":
            print(f"Skill already installed at {result.target_path}")
        return 0
    else:
        print(f"Error: {result.message}", file=sys.stderr)
        return 1


def cmd_reunify(args: argparse.Namespace) -> int:
    """Handle reunify subcommand."""
    result = reunify_workspace(
        workspace_path=args.workspace,
        write_merge=args.write_merge,
        write_review=not args.no_review,
    )

    if "error" in result:
        print(f"Error: {result['error']}", file=sys.stderr)
        return 1

    if args.format == "yaml":
        print(to_yaml(result))
    else:
        print(json.dumps(result, indent=2))

    if not args.quiet:
        print("\n--- Reunification Summary ---", file=sys.stderr)
        print(f"Tasks: {result['tasks_found']}", file=sys.stderr)
        print(f"Status: {result['rollup_status']}", file=sys.stderr)
        print(f"Conflicts: {result['conflicts_found']}", file=sys.stderr)
        print(f"Verification: {result['verification']['coverage']} tasks provided evidence", file=sys.stderr)
        if result["merge_file"]:
            print(f"Merge file: {result['merge_file']}", file=sys.stderr)
        if result["review_file"]:
            print(f"Review file: {result['review_file']}", file=sys.stderr)
        if result["ready_to_close"]:
            print("✓ Ready to close - no conflicts, all tasks succeeded", file=sys.stderr)
        else:
            print("⚠ Review required before closing", file=sys.stderr)

    return 0 if result["ready_to_close"] else 2


def main() -> int:
    """Main entry point."""
    parser = argparse.ArgumentParser(
        description="Cleave CLI - Task decomposition automation",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  cleave init --directive "Add JWT auth" --children '["Auth Core", "Middleware", "Database"]'
  cleave assess --directive "Add JWT auth to API"
  cleave match --directive "Add Stripe payment processing"
  cleave context --manifest .cleave/manifest.yaml
  cleave reunify --workspace .cleave
  cleave install-skill
        """,
    )

    subparsers = parser.add_subparsers(dest="command", required=True)

    # init
    init_parser = subparsers.add_parser("init", help="Initialize cleave workspace")
    init_parser.add_argument("--directive", "-d", required=True)
    init_parser.add_argument("--children", "-c", required=True, help="JSON array of child labels")
    init_parser.add_argument("--output", "-o", default=".cleave")
    init_parser.add_argument("--mode", "-m", default="lean", choices=["lean", "robust"])
    init_parser.add_argument("--threshold", "-t", type=float, default=2.0)
    init_parser.add_argument("--max-depth", type=int, default=5)
    init_parser.add_argument("--infer-permissions", action="store_true")
    init_parser.add_argument("--no-tdd", action="store_true")
    init_parser.add_argument("--parent", "-p", help="Path to parent manifest.yaml")
    init_parser.add_argument("--node-id", default="root")
    init_parser.add_argument("--depth", type=int, default=0)
    init_parser.set_defaults(func=cmd_init)

    # assess
    assess_parser = subparsers.add_parser("assess", help="Assess directive complexity")
    assess_parser.add_argument("--directive", "-d")
    assess_parser.add_argument("--systems", "-s")
    assess_parser.add_argument("--modifiers", "-m")
    assess_parser.add_argument("--threshold", "-t", type=float, default=2.0)
    assess_parser.add_argument("--validate/--no-validate", dest="validate", default=True)
    assess_parser.add_argument("--infer-permissions", action="store_true")
    assess_parser.add_argument("--format", "-f", default="json", choices=["json", "yaml"])
    assess_parser.set_defaults(func=cmd_assess)

    # match
    match_parser = subparsers.add_parser("match", help="Match directive against patterns")
    match_parser.add_argument("--directive", "-d", required=True)
    match_parser.add_argument("--format", "-f", default="json", choices=["json", "yaml"])
    match_parser.set_defaults(func=cmd_match)

    # context
    context_parser = subparsers.add_parser("context", help="Reconstruct context from manifest")
    context_parser.add_argument("--manifest", "-m", required=True)
    context_parser.add_argument("--format", "-f", default="json", choices=["json", "yaml"])
    context_parser.set_defaults(func=cmd_context)

    # check-permissions
    check_parser = subparsers.add_parser("check-permissions", help="Check permission gaps")
    check_parser.add_argument("--directive", "-d", required=True)
    check_parser.add_argument("--snippet", action="store_true")
    check_parser.add_argument("--format", "-f", default="json", choices=["json", "yaml"])
    check_parser.set_defaults(func=cmd_check_permissions)

    # reunify
    reunify_parser = subparsers.add_parser("reunify", help="Reunify child tasks")
    reunify_parser.add_argument("--workspace", "-w", required=True)
    reunify_parser.add_argument("--write-merge/--no-write-merge", dest="write_merge", default=True)
    reunify_parser.add_argument("--no-review", action="store_true")
    reunify_parser.add_argument("--quiet", "-q", action="store_true")
    reunify_parser.add_argument("--format", "-f", default="json", choices=["json", "yaml"])
    reunify_parser.set_defaults(func=cmd_reunify)

    # install-skill
    install_skill_parser = subparsers.add_parser(
        "install-skill", help="Install cleave skill to ~/.claude/skills/"
    )
    install_skill_parser.set_defaults(func=cmd_install_skill)

    args = parser.parse_args()
    return args.func(args)


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