"""Skill installer — detects, copies, and prompts for agent skill installation."""

import shutil
import subprocess
from pathlib import Path

from rich.console import Console
from rich.prompt import Prompt

BUNDLED_DIR = Path(__file__).parent / "bundled_skills"

SKILLS = [
    {"name": "openspec", "label": "OpenSpec", "desc": "spec-driven change workflow"},
    {"name": "beads", "label": "Beads", "desc": "task graph & agent memory"},
]

CONFIG_DIR = Path.home() / ".config" / "hangar"
NO_PROMPT_MARKER = CONFIG_DIR / "no-prompt"

console = Console()


def detect_skills(project_path: Path) -> dict[str, bool]:
    """Check which skills are installed in the project."""
    return {
        s["name"]: (project_path / ".claude" / "skills" / s["name"] / "SKILL.md").is_file()
        for s in SKILLS
    }


def install_skills(target: Path) -> int:
    """Copy bundled skills into target/.claude/skills/. Returns count installed."""
    count = 0
    for skill in SKILLS:
        src = BUNDLED_DIR / skill["name"] / "SKILL.md"
        dst = target / ".claude" / "skills" / skill["name"] / "SKILL.md"
        dst.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(src, dst)
        console.print(f"    .claude/skills/{skill['name']}/SKILL.md", end="")
        console.print("  [green]✓[/green]")
        count += 1
    return count


def set_no_prompt() -> None:
    """Write the no-prompt marker so the wizard never runs again."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    NO_PROMPT_MARKER.touch()


def should_prompt() -> bool:
    """Check if the wizard should run (no marker file exists)."""
    return not NO_PROMPT_MARKER.exists()


def detect_init_status(project_path: Path) -> dict[str, bool]:
    """Check which init prerequisites are fulfilled."""
    skills = detect_skills(project_path)
    agents_md = project_path / "AGENTS.md"
    has_agents_md = agents_md.exists() and "openspec" in agents_md.read_text().lower() and "beads" in agents_md.read_text().lower()
    return {
        "git": (project_path / ".git").exists(),
        "skills": all(skills.values()),
        "agents_md": has_agents_md,
    }


def run_wizard(project_path: Path) -> None:
    """Check init status on startup — offer to init if anything is missing."""
    status = detect_init_status(project_path)

    if all(status.values()):
        # Everything initialized — show green status line
        console.print("  [green]✓[/green] git  [green]✓[/green] skills  [green]✓[/green] AGENTS.md")
        return

    if not should_prompt():
        return

    # Show what's missing
    console.print()
    labels = {"git": "Git repo", "skills": "Agent skills", "agents_md": "AGENTS.md"}
    for key, label in labels.items():
        if status[key]:
            console.print(f"  [green]✓[/green] {label}")
        else:
            console.print(f"  [red]✗[/red] {label}")
    console.print()

    choices = {
        "1": "Initialize",
        "2": "Skip for now",
        "3": "Skip and don't ask again",
    }
    for k, v in choices.items():
        console.print(f"    [cyan]{k}[/cyan]  {v}")
    console.print()

    choice = Prompt.ask(
        "  [cyan]?[/cyan] Set up project",
        choices=list(choices.keys()),
        default="1",
    )

    if choice == "1":
        run_full_init(project_path)

    elif choice == "2":
        console.print(
            "\n  Skipped. Run [bold]hangar init[/bold] anytime.\n"
        )

    elif choice == "3":
        set_no_prompt()
        console.print(
            "\n  Got it. Run [bold]hangar init[/bold] to set up manually.\n"
        )


AGENTS_MD_SECTION = """\
## Workflow

This project uses [Hangar](https://github.com/pyros-projects/hangar) for orchestration-first development.

**Before starting any work**, read and follow the installed agent skills:

1. **OpenSpec** (`.claude/skills/openspec/SKILL.md`) — Plan what to build using structured specs, proposals, and design docs before writing code. Use `/opsx:new` to start a change, `/opsx:ff` to fast-forward through planning, and `/opsx:apply` to implement.

2. **Beads** (`.claude/skills/beads/SKILL.md`) — Track tasks as a dependency graph. Use `bd ready` to find unblocked work, `bd update <id> --claim` to claim a task, and `bd close <id> --reason "..."` when done. Always close first, then commit (close-then-commit rule).

**Do NOT start coding without first creating an OpenSpec change and importing tasks into Beads.** The skills contain detailed instructions for the full workflow.
"""


def detect_full_init_status(project_path: Path) -> dict:
    """Return detailed init status for the UI (non-interactive).

    Returns a dict with 'checks' (list of dicts) and 'plan' (list of strings).
    """
    skills = detect_skills(project_path)
    agents_md = project_path / "AGENTS.md"
    has_agents_md_file = agents_md.exists()
    has_agents_md_content = (
        has_agents_md_file
        and "openspec" in agents_md.read_text().lower()
        and "beads" in agents_md.read_text().lower()
    )

    checks = []
    plan = []

    # 1. Git
    git_ok = (project_path / ".git").exists()
    checks.append({
        "name": "Git Repository",
        "ok": git_ok,
        "detail": "Initialized" if git_ok else "Not initialized",
        "will_do": None if git_ok else "Will run git init",
    })
    if not git_ok:
        plan.append("Initialize git repository")

    # 2. OpenSpec directory
    openspec_dir = (project_path / "openspec").is_dir()
    if openspec_dir:
        checks.append({
            "name": "OpenSpec",
            "ok": True,
            "detail": "openspec/ directory found",
            "will_do": None,
        })
    else:
        # Check if CLI is available
        cli_found = shutil.which("openspec") is not None
        detail = "openspec/ directory not found" if cli_found else "CLI not found on PATH"
        checks.append({
            "name": "OpenSpec",
            "ok": False,
            "detail": detail,
            "will_do": "Created on first openspec new" if cli_found else "Requires manual install (pip install openspec-cli)",
        })

    # 3. Beads directory
    beads_dir = (project_path / ".beads").is_dir()
    if beads_dir:
        checks.append({
            "name": "Beads",
            "ok": True,
            "detail": ".beads/ directory found",
            "will_do": None,
        })
    else:
        cli_found = shutil.which("bd") is not None
        detail = ".beads/ directory not found" if cli_found else "CLI not found on PATH"
        checks.append({
            "name": "Beads",
            "ok": False,
            "detail": detail,
            "will_do": "Created on first bd init" if cli_found else "Requires manual install (pip install beads)",
        })

    # 4. Agent Skills
    all_skills = all(skills.values())
    missing = [s["label"] for s in SKILLS if not skills.get(s["name"])]
    if all_skills:
        checks.append({
            "name": "Agent Skills",
            "ok": True,
            "detail": "All skills installed",
            "will_do": None,
        })
    else:
        checks.append({
            "name": "Agent Skills",
            "ok": False,
            "detail": f"Missing: {', '.join(missing)}" if missing else "No skills installed",
            "will_do": "Will install OpenSpec + Beads skills",
        })
        plan.append("Install agent skills to .claude/skills/")

    # 5. AGENTS.md
    if has_agents_md_content:
        checks.append({
            "name": "AGENTS.md",
            "ok": True,
            "detail": "Present with workflow section",
            "will_do": None,
        })
    elif has_agents_md_file:
        checks.append({
            "name": "AGENTS.md",
            "ok": False,
            "detail": "Missing workflow instructions",
            "will_do": "Will append workflow section",
        })
        plan.append("Update AGENTS.md with workflow instructions")
    else:
        checks.append({
            "name": "AGENTS.md",
            "ok": False,
            "detail": "Not found",
            "will_do": "Will create with workflow instructions",
        })
        plan.append("Create AGENTS.md with workflow instructions")

    return {
        "checks": checks,
        "plan": plan,
        "ready": all(c["ok"] for c in checks),
    }


def run_init_headless(project_path: Path) -> list[dict]:
    """Run full init without console output (for API use).

    Returns a list of step results: [{"name": ..., "action": ..., "ok": bool}]
    """
    steps = []

    # 1. Git
    if not (project_path / ".git").exists():
        try:
            import subprocess
            subprocess.run(
                ["git", "init"], cwd=project_path, check=True, capture_output=True
            )
            steps.append({"name": "git", "action": "initialized", "ok": True})
        except Exception as e:
            steps.append({"name": "git", "action": f"failed: {e}", "ok": False})
    else:
        steps.append({"name": "git", "action": "already_exists", "ok": True})

    # 2. Skills
    status = detect_skills(project_path)
    if all(status.values()):
        steps.append({"name": "skills", "action": "already_installed", "ok": True})
    else:
        try:
            count = 0
            for skill in SKILLS:
                src = BUNDLED_DIR / skill["name"] / "SKILL.md"
                dst = project_path / ".claude" / "skills" / skill["name"] / "SKILL.md"
                dst.parent.mkdir(parents=True, exist_ok=True)
                shutil.copy2(src, dst)
                count += 1
            steps.append({"name": "skills", "action": f"installed_{count}", "ok": True})
        except Exception as e:
            steps.append({"name": "skills", "action": f"failed: {e}", "ok": False})

    # 3. AGENTS.md
    agents_md = project_path / "AGENTS.md"
    if agents_md.exists():
        content = agents_md.read_text()
        if "openspec" not in content.lower() or "beads" not in content.lower():
            try:
                with agents_md.open("a") as f:
                    f.write("\n" + AGENTS_MD_SECTION)
                steps.append({"name": "agents_md", "action": "updated", "ok": True})
            except Exception as e:
                steps.append({"name": "agents_md", "action": f"failed: {e}", "ok": False})
        else:
            steps.append({"name": "agents_md", "action": "already_configured", "ok": True})
    else:
        try:
            agents_md.write_text("# Agent Instructions\n\n" + AGENTS_MD_SECTION)
            steps.append({"name": "agents_md", "action": "created", "ok": True})
        except Exception as e:
            steps.append({"name": "agents_md", "action": f"failed: {e}", "ok": False})

    return steps


def run_full_init(project_path: Path) -> None:
    """Initialize a project: git repo, agent skills, AGENTS.md."""
    short = str(project_path).replace(str(Path.home()), "~")
    console.print(f"\n  Initializing [bold white]{short}[/bold white] ...\n")

    # 1. Git
    if not (project_path / ".git").exists():
        subprocess.run(
            ["git", "init"], cwd=project_path, check=True, capture_output=True
        )
        console.print("  [green]✓[/green] Initialized git repository")
    else:
        console.print("  [dim]–[/dim] Git repository already exists")

    # 2. Skills (reuse existing)
    status = detect_skills(project_path)
    if all(status.values()):
        console.print("  [dim]–[/dim] Agent skills already installed")
    else:
        install_skills(project_path)

    # 3. AGENTS.md
    agents_md = project_path / "AGENTS.md"
    if agents_md.exists():
        content = agents_md.read_text()
        if "openspec" not in content.lower() or "beads" not in content.lower():
            with agents_md.open("a") as f:
                f.write("\n" + AGENTS_MD_SECTION)
            console.print("  [green]✓[/green] Updated AGENTS.md with workflow instructions")
        else:
            console.print("  [dim]–[/dim] AGENTS.md already has workflow instructions")
    else:
        agents_md.write_text("# Agent Instructions\n\n" + AGENTS_MD_SECTION)
        console.print("  [green]✓[/green] Created AGENTS.md")

    console.print()
