#!/usr/bin/env python3
"""Deploy platform adapter templates outside the repository checkout."""

from __future__ import annotations

import argparse
import os
import shutil
import sys
from dataclasses import dataclass, field
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parent.parent
if str(REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(REPO_ROOT))

from scripts.runtime.skills import SkillRegistry

CODEX_TEMPLATE = REPO_ROOT / "codex_adapter_templates" / "codex"
ANTIGRAVITY_RULES_DIR = REPO_ROOT / ".agents" / "rules"
ANTIGRAVITY_WORKFLOWS_DIR = REPO_ROOT / ".agents" / "workflows"
ANTIGRAVITY_AGENTS_FILE = REPO_ROOT / ".agents" / "agents.md"


@dataclass
class DeployResult:
    platform: str
    target: Path
    written: list[Path] = field(default_factory=list)
    skipped: list[Path] = field(default_factory=list)
    planned: list[Path] = field(default_factory=list)
    backups: list[Path] = field(default_factory=list)


def default_codex_home(
    *,
    env: dict[str, str] | None = None,
    user_home: Path | None = None,
) -> Path:
    values = os.environ if env is None else env
    if values.get("CODEX_HOME"):
        return Path(values["CODEX_HOME"]).expanduser()
    home = Path.home() if user_home is None else user_home
    return home / ".codex"


def copy_tree_contents(
    src: Path,
    dst: Path,
    *,
    force: bool,
    dry_run: bool = False,
    backup_existing: bool = True,
    exclude: set[Path] | None = None,
) -> DeployResult:
    if not src.exists():
        raise FileNotFoundError(f"adapter template source not found: {src}")

    result = DeployResult(platform="copy", target=dst)
    excluded = exclude or set()
    for item in sorted(src.rglob("*")):
        rel = item.relative_to(src)
        if rel in excluded:
            continue
        target = dst / rel
        if item.is_dir():
            if not dry_run:
                target.mkdir(parents=True, exist_ok=True)
            continue

        if target.exists() and not force:
            result.skipped.append(target)
            continue

        result.planned.append(target)
        if dry_run:
            continue

        target.parent.mkdir(parents=True, exist_ok=True)
        if target.exists() and force and backup_existing:
            backup = target.with_name(f"{target.name}.bak")
            shutil.copy2(target, backup)
            result.backups.append(backup)
        shutil.copy2(item, target)
        result.written.append(target)
    return result


def _merge_results(platform: str, target: Path, parts: list[DeployResult]) -> DeployResult:
    merged = DeployResult(platform=platform, target=target)
    for part in parts:
        merged.written.extend(part.written)
        merged.skipped.extend(part.skipped)
        merged.planned.extend(part.planned)
        merged.backups.extend(part.backups)
    return merged


def copy_registered_skills(dst: Path, *, force: bool, dry_run: bool = False) -> DeployResult:
    result = DeployResult(platform="skills", target=dst)
    for skill in SkillRegistry().list():
        part = copy_tree_contents(skill.path.parent, dst / skill.name, force=force, dry_run=dry_run)
        result.written.extend(part.written)
        result.skipped.extend(part.skipped)
        result.planned.extend(part.planned)
        result.backups.extend(part.backups)
    return result


def copy_antigravity_rules(dst: Path, *, force: bool, dry_run: bool = False) -> DeployResult:
    result = DeployResult(platform="rules", target=dst)
    if not ANTIGRAVITY_RULES_DIR.exists():
        return result
    part = copy_tree_contents(ANTIGRAVITY_RULES_DIR, dst, force=force, dry_run=dry_run)
    result.written.extend(part.written)
    result.skipped.extend(part.skipped)
    result.planned.extend(part.planned)
    result.backups.extend(part.backups)
    return result


def copy_antigravity_workflows(dst: Path, *, force: bool, dry_run: bool = False) -> DeployResult:
    result = DeployResult(platform="workflows", target=dst)
    if not ANTIGRAVITY_WORKFLOWS_DIR.exists():
        return result
    part = copy_tree_contents(ANTIGRAVITY_WORKFLOWS_DIR, dst, force=force, dry_run=dry_run)
    result.written.extend(part.written)
    result.skipped.extend(part.skipped)
    result.planned.extend(part.planned)
    result.backups.extend(part.backups)
    return result


def copy_antigravity_agents_file(dst: Path, *, force: bool, dry_run: bool = False) -> DeployResult:
    result = DeployResult(platform="agents", target=dst)
    if not ANTIGRAVITY_AGENTS_FILE.exists():
        return result
    target = dst / "agents.md"
    if target.exists() and not force:
        result.skipped.append(target)
        return result
    result.planned.append(target)
    if dry_run:
        return result
    target.parent.mkdir(parents=True, exist_ok=True)
    if target.exists() and force:
        backup = target.with_name("agents.md.bak")
        shutil.copy2(target, backup)
        result.backups.append(backup)
    shutil.copy2(ANTIGRAVITY_AGENTS_FILE, target)
    result.written.append(target)
    return result


def deploy_codex(
    *,
    target: Path | None = None,
    force: bool = False,
    skip_skills: bool = False,
    dry_run: bool = False,
    include_config: bool = False,
    repo_root: Path = REPO_ROOT,
) -> DeployResult:
    codex_home = (target or default_codex_home()).expanduser()
    template = repo_root / "codex_adapter_templates" / "codex"

    parts = [
        copy_tree_contents(
            template,
            codex_home,
            force=force,
            dry_run=dry_run,
            exclude=set() if include_config else {Path("config.toml")},
        ),
    ]
    if not skip_skills:
        parts.append(copy_registered_skills(codex_home / "skills", force=force, dry_run=dry_run))
    return _merge_results("codex", codex_home, parts)


def deploy_antigravity(
    *,
    target: Path | None = None,
    force: bool = False,
    skip_skills: bool = False,
    skip_agents: bool = False,
    skip_rules: bool = False,
    skip_workflows: bool = False,
    dry_run: bool = False,
    repo_root: Path = REPO_ROOT,
) -> DeployResult:
    """Deploy Antigravity workspace rules and skills into a workspace root.

    This intentionally deploys to a workspace-local `.agents` directory, not
    global Antigravity/Gemini settings, so existing user configuration is not
    touched. Existing files are skipped unless `force=True`.
    """
    workspace = (target or repo_root).expanduser()
    parts: list[DeployResult] = []
    if not skip_agents:
        parts.append(copy_antigravity_agents_file(workspace / ".agents", force=force, dry_run=dry_run))
    if not skip_skills:
        parts.append(copy_registered_skills(workspace / ".agents" / "skills", force=force, dry_run=dry_run))
    if not skip_rules:
        parts.append(copy_antigravity_rules(workspace / ".agents" / "rules", force=force, dry_run=dry_run))
    if not skip_workflows:
        parts.append(copy_antigravity_workflows(workspace / ".agents" / "workflows", force=force, dry_run=dry_run))
    return _merge_results("antigravity", workspace, parts)


def print_result(result: DeployResult) -> None:
    action = "planned" if result.planned and not result.written else "written"
    print(f"{result.platform} adapter deployment")
    print(f"  target: {result.target}")
    print(f"  files {action}: {len(result.planned if action == 'planned' else result.written)}")
    print(f"  files skipped: {len(result.skipped)}")
    print(f"  backups: {len(result.backups)}")
    if result.written:
        for path in result.written:
            print(f"    {path}")
    elif result.planned:
        for path in result.planned:
            print(f"    {path}")


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Deploy Deep Research surface adapters")
    sub = parser.add_subparsers(dest="platform", required=True)

    codex = sub.add_parser("codex", help="Deploy Codex adapter to CODEX_HOME or a target directory")
    codex.add_argument("--target", type=Path, help="Codex home target; defaults to $CODEX_HOME or ~/.codex")
    codex.add_argument("--force", action="store_true", help="overwrite existing files and create .bak backups")
    codex.add_argument("--skip-skills", action="store_true", help="do not copy canonical skills into target/skills")
    codex.add_argument("--include-config", action="store_true", help="also copy config.toml; off by default to avoid overwriting global Codex config")
    codex.add_argument("--dry-run", action="store_true", help="show files that would be written")

    antigravity = sub.add_parser("antigravity", help="Deploy Antigravity workspace rules and skills")
    antigravity.add_argument("--target", type=Path, help="Workspace root; defaults to this repository")
    antigravity.add_argument("--force", action="store_true", help="overwrite existing files and create .bak backups")
    antigravity.add_argument("--skip-agents", action="store_true", help="do not copy role definitions into target/.agents/agents.md")
    antigravity.add_argument("--skip-skills", action="store_true", help="do not copy canonical skills into target/.agents/skills")
    antigravity.add_argument("--skip-rules", action="store_true", help="do not copy workspace rules into target/.agents/rules")
    antigravity.add_argument("--skip-workflows", action="store_true", help="do not copy workflows into target/.agents/workflows")
    antigravity.add_argument("--dry-run", action="store_true", help="show files that would be written without writing")
    return parser


def main() -> int:
    args = build_parser().parse_args()
    if args.platform == "codex":
        result = deploy_codex(
            target=args.target,
            force=args.force,
            skip_agents=args.skip_agents,
            skip_skills=args.skip_skills,
            dry_run=args.dry_run,
            include_config=args.include_config,
        )
        print_result(result)
        print()
        print("Run Codex from this repository after deployment:")
        if args.include_config:
            print("  codex --profile deep-research")
        else:
            print("  codex")
            print("Note: config.toml is not copied by default. Use --include-config only if you want the bundled profile.")
        return 0
    if args.platform == "antigravity":
        result = deploy_antigravity(
            target=args.target,
            force=args.force,
            skip_skills=args.skip_skills,
            skip_rules=args.skip_rules,
            skip_workflows=args.skip_workflows,
            dry_run=args.dry_run,
        )
        print_result(result)
        print()
        print("Open the target workspace in Antigravity and enable/mention the workspace rule if needed:")
        print("  .agents/rules/deep-research-antigravity.md")
        print("Workflow installed when supported by your Antigravity build:")
        print("  .agents/workflows/deep-research-native.md")
        print("Existing files are skipped by default. Use --force only when you want .bak backups and replacement.")
        return 0
    raise SystemExit(f"unsupported platform: {args.platform}")


if __name__ == "__main__":
    raise SystemExit(main())
