"""ievo install — set up iEvo in a project."""

from __future__ import annotations

import asyncio
from datetime import date
from pathlib import Path

import typer

from ievo.commands.init_templates import (
    CLAUDE_MD,
    CLAUDE_SETTINGS_LOCAL_JSON,
    GITIGNORE,
    SPEC_INDEX_MD,
)
from ievo.core.config import (
    AGENTS_DIR,
    CLAUDE_AGENTS_DIR,
    IEVO_MANIFEST,
    PLANS_DIR,
    SPEC_DIR,
    ensure_home,
)
from ievo.core.cortex import (
    CORTEX_RELEASES_URL,
    CortexDownloadError,
    download_cortex_release,
    fetch_latest_cortex_tag,
)
from ievo.core.project import ProjectManifest
from ievo.utils.console import info, step, success, warn
from ievo.version import __version__

# Single-line sentinel injected into CLAUDE.md during install
IEVO_LINE = "Read `.ievo/iEVO.md` for pipeline conventions."


def patch_claude_md(path: Path) -> bool:
    """Inject iEvo reference line into CLAUDE.md if not already present.

    Returns True if the patch was applied, False if already present or
    the file does not exist.
    """
    if not path.is_file():
        return False

    content = path.read_text()

    if IEVO_LINE in content:
        return False

    # Ensure separation from existing content by exactly one blank line
    if content and not content.endswith("\n"):
        content += "\n"
    if content and not content.endswith("\n\n"):
        content += "\n"

    content += IEVO_LINE + "\n"
    path.write_text(content)
    return True


# Directories that are expected to be empty at install time — need .gitkeep
_EMPTY_DIRS = {
    f"{SPEC_DIR}/requirements",
    f"{SPEC_DIR}/changes",
    f"{SPEC_DIR}/questions",
    PLANS_DIR,
    AGENTS_DIR,
}


def _write_if_missing(path: Path, content: str) -> bool:
    """Write file if it doesn't exist. Returns True if written."""
    if path.exists():
        return False
    path.write_text(content)
    return True


def install(
    name: str = typer.Argument(
        default=".",
        help="Project name or path. Use '.' for current directory.",
    ),
) -> None:
    """Set up iEvo in the current project."""
    if name == ".":
        root = Path.cwd()
        project_name = root.name
    else:
        root = Path.cwd() / name
        project_name = name

    root.mkdir(parents=True, exist_ok=True)

    # ── Global ~/.ievo setup (keys, config) ──────────────────
    home = ensure_home()

    # GitHub auth (only on first install when ~/.ievo was just created)
    from ievo.core.global_config import get_config

    if get_config(home, "github_auth") is None:
        from ievo.core.github_auth import prompt_github_auth

        prompt_github_auth(home)

    info(f"Initializing [bold]{project_name}[/bold]...")

    # ── Directories ──────────────────────────────────────────
    dirs = [
        ".ievo",
        SPEC_DIR,
        f"{SPEC_DIR}/requirements",
        f"{SPEC_DIR}/changes",
        f"{SPEC_DIR}/questions",
        f"{SPEC_DIR}/templates",
        PLANS_DIR,
        AGENTS_DIR,
        ".claude/hooks",
        ".claude/skills",
        CLAUDE_AGENTS_DIR,
    ]

    info("[bold]Directories[/bold]")
    for d in dirs:
        dir_path = root / d
        dir_path.mkdir(parents=True, exist_ok=True)
        step(d + "/")
        # Add .gitkeep to empty dirs
        if d in _EMPTY_DIRS:
            gitkeep = dir_path / ".gitkeep"
            if not gitkeep.exists():
                gitkeep.write_text("")

    # ── Project files ────────────────────────────────────────
    info("[bold]Project files[/bold]")

    manifest_path = root / IEVO_MANIFEST
    if manifest_path.is_file():
        manifest = ProjectManifest.load(root)
        manifest.cli_version = __version__
    else:
        manifest = ProjectManifest(
            project=project_name,
            version="0.1.0",
            created=str(date.today()),
            cli_version=__version__,
        )
    manifest.save(root)
    step(".ievo/manifest.yaml")

    if _write_if_missing(root / "CLAUDE.md", CLAUDE_MD.format(project_name=project_name)):
        step("CLAUDE.md")

    # Patch CLAUDE.md with iEvo marker block (idempotent — skips if already present)
    claude_md_path = root / "CLAUDE.md"
    if patch_claude_md(claude_md_path):
        step("CLAUDE.md (patched with iEvo pipeline reference)")

    if _write_if_missing(root / ".gitignore", GITIGNORE):
        step(".gitignore")

    # ── Spec files ──────────────────────────────────────────────
    info("[bold]Spec files[/bold]")

    if _write_if_missing(root / SPEC_DIR / "SPEC_INDEX.md", SPEC_INDEX_MD):
        step("spec/SPEC_INDEX.md")

    # ── Claude Code hooks ──────────────────────────────────────
    info("[bold]Claude Code hooks[/bold]")

    hook_source = Path(__file__).parent.parent / "hooks" / "precompact_save.py"
    hook_dest = root / ".claude" / "hooks" / "precompact_save.py"
    if _write_if_missing(hook_dest, hook_source.read_text()):
        step(".claude/hooks/precompact_save.py")

    settings_local = root / ".claude" / "settings.local.json"
    if _write_if_missing(settings_local, CLAUDE_SETTINGS_LOCAL_JSON):
        step(".claude/settings.local.json")

    # ── Agents, Skills, Templates ────────────────────────────────
    info("[bold]Marketplace files[/bold]")

    from ievo.core.agent_sync import sync_marketplace

    for name in sync_marketplace(root):
        step(name)

    # ── Cortex kernel ────────────────────────────────────────
    info("[bold]Cortex kernel[/bold]")
    try:
        tag, asset_url = asyncio.run(fetch_latest_cortex_tag())
        asyncio.run(download_cortex_release(tag=tag, asset_url=asset_url, root=root))
        step(f"cortex {tag}")
    except CortexDownloadError as exc:
        warn(f"Cortex download failed: {exc}")
        info(f"Releases: {CORTEX_RELEASES_URL}")
        info("You can download later with: [bold]ievo cortex build[/bold]")

    # ── Done ─────────────────────────────────────────────────
    success(f"Project [bold]{project_name}[/bold] initialized.")
    info("Next: [bold]ievo add spec-writer architect coder[/bold]")
