#!/usr/bin/env python3
"""Portable CLI for initializing a project from the Antigravity template."""

from __future__ import annotations

import argparse
import json
import re
import shutil
import subprocess
from pathlib import Path
from typing import Dict, List


VALID_MODES = {"quick", "full"}
VALID_SANDBOX_RUNTIMES = {"local", "microsandbox"}


def _is_within(child: Path, parent: Path) -> bool:
    """Return whether `child` is inside `parent`.

    Args:
        child: Candidate child path.
        parent: Candidate parent path.

    Returns:
        True if child resolves under parent.
    """

    try:
        child.relative_to(parent)
        return True
    except ValueError:
        return False


def _count_entries(path: Path) -> int:
    """Count filesystem entries recursively.

    Args:
        path: Directory path.

    Returns:
        Number of files and directories below path.
    """

    return sum(1 for _ in path.rglob("*"))


def _upsert_env_var(lines: List[str], key: str, value: str) -> List[str]:
    """Set or append an env variable line.

    Args:
        lines: Existing env file lines.
        key: Variable name.
        value: Variable value.

    Returns:
        Updated env file lines.
    """

    replaced = False
    updated: List[str] = []
    prefix_pattern = re.compile(rf"^\s*{re.escape(key)}\s*=")

    for line in lines:
        if not replaced and not line.lstrip().startswith("#") and prefix_pattern.match(line):
            updated.append(f"{key}={value}")
            replaced = True
            continue
        updated.append(line)

    if not replaced:
        updated.append(f"{key}={value}")

    return updated


def _configure_env_file(
    target_path: Path,
    project_name: str,
    mode: str,
    enable_mcp: bool,
    sandbox_runtime: str,
) -> None:
    """Create/update env file.

    Args:
        target_path: Destination project path.
        project_name: Destination project name.
        mode: Initialization mode.
        enable_mcp: MCP flag.
        sandbox_runtime: Sandbox runtime mode.
    """

    env_example = target_path / ".env.example"
    env_file = target_path / ".env"

    if not env_file.exists() and env_example.exists():
        shutil.copy2(env_example, env_file)

    if not env_file.exists():
        env_file.write_text("", encoding="utf-8")

    if mode == "quick":
        return

    lines = env_file.read_text(encoding="utf-8").splitlines()
    lines = _upsert_env_var(lines, "MCP_ENABLED", "true" if enable_mcp else "false")
    lines = _upsert_env_var(lines, "SANDBOX_TYPE", sandbox_runtime)
    lines = _upsert_env_var(lines, "AGENT_NAME", project_name)

    env_file.write_text("\n".join(lines) + "\n", encoding="utf-8")


def _write_full_mode_files(
    target_path: Path,
    mode: str,
    project_name: str,
    enable_mcp: bool,
    enable_swarm: bool,
    sandbox_runtime: str,
    init_git: bool,
) -> None:
    """Write full-mode profile files.

    Args:
        target_path: Destination project path.
        mode: Initialization mode.
        project_name: Destination project name.
        enable_mcp: MCP flag.
        enable_swarm: Swarm preference.
        sandbox_runtime: Sandbox runtime mode.
        init_git: Git init flag.
    """

    if mode != "full":
        return

    mission_content = (
        "# Agent Mission\n\n"
        f"**Project:** {project_name}\n\n"
        "## Objective\n"
        "Build a production-ready multi-agent workflow tailored to this repository.\n\n"
        "## Initial Runtime Profile\n"
        f"- Mode: {mode}\n"
        "- LLM Setup: run `ag-setup` in this project\n"
        f"- MCP Enabled: {enable_mcp}\n"
        f"- Swarm Preferred: {enable_swarm}\n"
        f"- Sandbox Runtime: {sandbox_runtime}\n"
    )
    (target_path / "mission.md").write_text(mission_content, encoding="utf-8")

    profile_path = target_path / ".context" / "agent_runtime_profile.md"
    profile_path.parent.mkdir(parents=True, exist_ok=True)
    profile_content = (
        "# Agent Runtime Profile\n\n"
        "This file is generated by `agent-repo-init` full mode.\n\n"
        "- LLM setup: run `ag-setup` in this project\n"
        f"- MCP enabled by default: `{enable_mcp}`\n"
        f"- Swarm workflow preference: `{enable_swarm}`\n"
        f"- Default sandbox mode: `{sandbox_runtime}`\n"
    )
    profile_path.write_text(profile_content, encoding="utf-8")

    report_path = target_path / "artifacts" / "logs" / "agent_repo_init_report.md"
    report_path.parent.mkdir(parents=True, exist_ok=True)
    report_content = (
        "# Agent Repo Init Report\n\n"
        f"- Mode: `{mode}`\n"
        f"- Project Name: `{project_name}`\n"
        "- LLM Setup: `ag-setup`\n"
        f"- MCP Enabled: `{enable_mcp}`\n"
        f"- Swarm Preferred: `{enable_swarm}`\n"
        f"- Sandbox Runtime: `{sandbox_runtime}`\n"
        f"- Git Initialized: `{init_git}`\n"
    )
    report_path.write_text(report_content, encoding="utf-8")


def _init_git_repo(target_path: Path) -> None:
    """Initialize git repository.

    Args:
        target_path: Destination project path.

    Raises:
        OSError: If git initialization fails.
    """

    try:
        subprocess.run(
            ["git", "init"],
            cwd=target_path,
            check=True,
            capture_output=True,
            text=True,
        )
    except subprocess.CalledProcessError as exc:
        raise OSError(f"git init failed: {exc.stderr.strip()}") from exc


def _build_parser() -> argparse.ArgumentParser:
    """Build CLI parser.

    Returns:
        Configured parser.
    """

    parser = argparse.ArgumentParser(description="Initialize project from template")
    parser.add_argument("--project-name", required=True, help="Destination project name")
    parser.add_argument("--destination-root", default=".", help="Parent output directory")
    parser.add_argument("--mode", default="quick", choices=sorted(VALID_MODES))
    parser.add_argument("--enable-mcp", action="store_true")
    parser.add_argument("--disable-swarm", action="store_true")
    parser.add_argument(
        "--sandbox-runtime",
        default="local",
        choices=sorted(VALID_SANDBOX_RUNTIMES),
    )
    parser.add_argument("--init-git", action="store_true")
    parser.add_argument(
        "--template-root",
        default=None,
        help="Optional explicit template root path",
    )
    return parser


def main() -> int:
    """Execute CLI.

    Returns:
        Exit code.

    Raises:
        ValueError: If arguments are invalid.
        OSError: If filesystem or git operations fail.
    """

    args = _build_parser().parse_args()

    if not re.fullmatch(r"[A-Za-z0-9._-]+", args.project_name):
        raise ValueError(
            "project_name must contain only letters, numbers, '.', '_', or '-'."
        )

    template_root = (
        Path(args.template_root).expanduser().resolve()
        if args.template_root
        else Path(__file__).resolve().parents[3]
    )
    destination_parent = Path(args.destination_root).expanduser().resolve()
    destination_parent.mkdir(parents=True, exist_ok=True)
    target_path = (destination_parent / args.project_name).resolve()

    if target_path.exists():
        raise ValueError(f"Target path already exists: {target_path}")

    if _is_within(target_path, template_root):
        raise ValueError(
            "Destination must be outside the current template repository to avoid recursive copies."
        )

    ignore = shutil.ignore_patterns(
        ".git",
        ".pytest_cache",
        "__pycache__",
        "venv",
        ".venv",
        "antigravity_workspace_template_venv",
        "agent_memory.json",
        "artifacts",
        "*.pyc",
    )
    shutil.copytree(template_root, target_path, ignore=ignore)

    (target_path / "artifacts" / "logs").mkdir(parents=True, exist_ok=True)

    enable_swarm = not args.disable_swarm
    _configure_env_file(
        target_path,
        project_name=args.project_name,
        mode=args.mode,
        enable_mcp=args.enable_mcp,
        sandbox_runtime=args.sandbox_runtime,
    )
    _write_full_mode_files(
        target_path,
        mode=args.mode,
        project_name=args.project_name,
        enable_mcp=args.enable_mcp,
        enable_swarm=enable_swarm,
        sandbox_runtime=args.sandbox_runtime,
        init_git=args.init_git,
    )

    if args.init_git:
        _init_git_repo(target_path)

    next_steps = [
        f"cd {target_path}",
        "python3 -m venv venv",
        "source venv/bin/activate",
        "pip install -e ./cli -e './engine[dev]'",
        "run /ag-setup (or configure OPENAI_* in .env)",
        "ag-refresh --workspace .",
    ]
    if args.mode == "full":
        next_steps.append("review .context/agent_runtime_profile.md")

    result: Dict[str, object] = {
        "project_name": args.project_name,
        "project_path": str(target_path),
        "mode": args.mode,
        "copied_entries": _count_entries(target_path),
        "next_steps": next_steps,
    }
    print(json.dumps(result, indent=2))
    return 0


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