"""Agentbyte CLI — entry point for all agentbyte commands."""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

from agentbyte.__about__ import VERSION


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _bundled_skills_root():
    """Return a Traversable / Path pointing to the bundled skills directory.

    - Installed wheel: ``agentbyte/skills/`` is inside the package (via hatchling
      force-include), so ``importlib.resources.files`` resolves it directly.
    - Editable / dev install: the wheel bundle doesn't exist yet; we walk up from
      ``src/agentbyte/`` to find ``.github/skills/`` in the repo root instead.
    """
    from importlib.resources import files
    from importlib.util import find_spec

    traversable = files("agentbyte").joinpath("skills")
    try:
        next(iter(traversable.iterdir()))
        return traversable
    except (FileNotFoundError, NotADirectoryError, StopIteration, OSError):
        pass

    # Fallback for editable installs: locate the repo root via the package __file__
    spec = find_spec("agentbyte")
    if spec and spec.origin:
        pkg_dir = Path(spec.origin).parent  # …/src/agentbyte/
        candidate = pkg_dir
        for _ in range(6):
            candidate = candidate.parent
            skills_dir = candidate / ".github" / "skills"
            if skills_dir.is_dir():
                return skills_dir

    raise FileNotFoundError(
        "Could not locate bundled skills directory. "
        "Expected agentbyte/skills/ in the installed package or "
        ".github/skills/ in the repository root."
    )


def _iter_skill_files(source_traversable, relative_prefix: Path):
    """Recursively yield (relative_path, traversable_file) pairs."""
    for entry in source_traversable.iterdir():
        entry_rel = relative_prefix / entry.name
        if entry.is_dir():
            yield from _iter_skill_files(entry, entry_rel)
        else:
            yield entry_rel, entry


def _copy_skills(source_traversable, dest_dir: Path) -> list[Path]:
    """Copy all files from source_traversable into dest_dir. Returns list of written paths."""
    written = []
    for rel_path, src_file in _iter_skill_files(source_traversable, Path()):
        dest_file = dest_dir / rel_path
        dest_file.parent.mkdir(parents=True, exist_ok=True)
        dest_file.write_bytes(src_file.read_bytes())
        written.append(dest_file)
    return written


# ---------------------------------------------------------------------------
# Sub-command: create-skills
# ---------------------------------------------------------------------------

_PROVIDER_TARGET = {
    "github-copilot": Path(".github") / "skills",
    "claude": Path(".claude") / "skills",
}

_PROVIDERS = list(_PROVIDER_TARGET.keys())


def cmd_create_skills(args: argparse.Namespace, cwd: Path | None = None) -> int:
    """Implement the create-skills sub-command."""
    provider: str = args.provider
    target_rel = _PROVIDER_TARGET[provider]
    base = cwd or Path.cwd()
    dest_dir = base / target_rel

    try:
        skills_root = _bundled_skills_root()
    except Exception as exc:
        print(f"Error: could not locate bundled skills: {exc}", file=sys.stderr)
        return 1

    # Collect what will be written
    try:
        pending = list(_iter_skill_files(skills_root, Path()))
    except Exception as exc:
        print(f"Error: could not read bundled skills: {exc}", file=sys.stderr)
        return 1

    if not pending:
        print("No bundled skills found.")
        return 0

    # Preview
    print(f"\nProvider : {provider}")
    print(f"Target   : {dest_dir}")
    print(f"\nFiles to be written ({len(pending)}):")
    for rel_path, _ in sorted(pending, key=lambda x: str(x[0])):
        marker = "  [overwrite]" if (dest_dir / rel_path).exists() else ""
        print(f"  {rel_path}{marker}")

    # Confirm
    print()
    try:
        answer = input("Proceed? [y/N]: ").strip().lower()
    except (EOFError, KeyboardInterrupt):
        print("\nAborted.")
        return 0

    if answer != "y":
        print("Aborted.")
        return 0

    written = _copy_skills(skills_root, dest_dir)
    print(f"\n✓ {len(written)} file(s) written to {dest_dir}")
    return 0


# ---------------------------------------------------------------------------
# Interactive wizard
# ---------------------------------------------------------------------------

_COMMANDS = [
    ("create-skills", "Copy AI assistant skills into this project"),
    ("webui", "Launch the Agentbyte WebUI"),
]


def cmd_webui(args: argparse.Namespace) -> int:
    """Implement the webui sub-command."""
    try:
        from agentbyte.webui import launch
    except Exception as exc:
        print(
            "Error: WebUI dependencies are not available. "
            "Install them with `uv sync --extra webui` or `pip install agentbyte[webui]`.",
            file=sys.stderr,
        )
        print(f"Details: {exc}", file=sys.stderr)
        return 1

    try:
        launch(
            entities_dir=args.dir,
            port=args.port,
            host=args.host,
            auto_open=not args.no_open,
            reload=args.reload,
            log_level=args.log_level,
        )
    except KeyboardInterrupt:
        print("\nShutting down Agentbyte WebUI.")
        return 0
    except Exception as exc:
        print(f"Error starting Agentbyte WebUI: {exc}", file=sys.stderr)
        return 1

    return 0


def _prompt_choice(prompt: str, choices: list[str], default: int = 1) -> str | None:
    """Print a numbered menu and return the selected string value.

    Re-prompts once on invalid input, then returns None (abort).
    Default is applied when the user presses Enter without typing anything.
    """
    for i, choice in enumerate(choices, 1):
        print(f"  {i}. {choice}")
    print()

    for attempt in range(2):
        try:
            raw = input(f"{prompt} [{default}]: ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\nAborted.")
            return None

        value = raw if raw else str(default)
        if value.isdigit() and 1 <= int(value) <= len(choices):
            return choices[int(value) - 1]

        if attempt == 0:
            print(f"  Invalid choice. Please enter a number between 1 and {len(choices)}.")
        else:
            print("  Aborted.")
            return None

    return None  # unreachable, but satisfies type checkers


def _interactive_wizard() -> int:
    """Guided interactive wizard shown when `agentbyte` is run with no arguments."""
    print(f"\nAgentbyte v{VERSION}\n")

    # Phase 1 — command selection
    print("Commands:\n")
    command_names = [name for name, _ in _COMMANDS]
    command_labels = [f"{name:<20} {desc}" for name, desc in _COMMANDS]
    selected_label = _prompt_choice("Enter choice", command_labels, default=1)
    if selected_label is None:
        return 0

    # Resolve back to the plain command name from the label
    idx = command_labels.index(selected_label)
    command = command_names[idx]

    if command == "create-skills":
        # Phase 2 — provider selection
        print("\nSelect provider:\n")
        provider_labels = [
            f"{name:<20} →  {target}"
            for name, target in _PROVIDER_TARGET.items()
        ]
        selected_provider_label = _prompt_choice("Enter choice", provider_labels, default=1)
        if selected_provider_label is None:
            return 0

        provider_idx = provider_labels.index(selected_provider_label)
        provider = _PROVIDERS[provider_idx]

        args = argparse.Namespace(provider=provider)
        return cmd_create_skills(args)

    if command == "webui":
        args = argparse.Namespace(
            dir=".",
            port=8080,
            host="127.0.0.1",
            no_open=False,
            reload=False,
            log_level="info",
        )
        return cmd_webui(args)

    return 0


# ---------------------------------------------------------------------------
# Parser
# ---------------------------------------------------------------------------

def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="agentbyte",
        description="Agentbyte — enterprise-ready multiagent toolkit",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"agentbyte {VERSION}",
    )

    subparsers = parser.add_subparsers(dest="command", metavar="<command>")

    # create-skills
    create_skills_parser = subparsers.add_parser(
        "create-skills",
        help="Copy bundled AI assistant skills into the current project",
    )
    create_skills_parser.add_argument(
        "--provider",
        choices=_PROVIDERS,
        default="github-copilot",
        help=(
            "Which AI assistant provider to target. "
            "'github-copilot' writes to .github/skills/, "
            "'claude' writes to .claude/skills/. "
            "(default: github-copilot)"
        ),
    )

    webui_parser = subparsers.add_parser(
        "webui",
        help="Launch the packaged Agentbyte WebUI",
    )
    webui_parser.add_argument(
        "--dir",
        default=".",
        help="Directory to scan for agents, workflows, and orchestrators",
    )
    webui_parser.add_argument(
        "--port",
        "-p",
        type=int,
        default=8080,
        help="Port to run the server on (default: 8080)",
    )
    webui_parser.add_argument(
        "--host",
        default="127.0.0.1",
        help="Host to bind the server to (default: 127.0.0.1)",
    )
    webui_parser.add_argument(
        "--no-open",
        action="store_true",
        help="Do not automatically open the browser",
    )
    webui_parser.add_argument(
        "--reload",
        action="store_true",
        help="Enable auto-reload for development",
    )
    webui_parser.add_argument(
        "--log-level",
        choices=["debug", "info", "warning", "error"],
        default="info",
        help="Server log level (default: info)",
    )

    return parser


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main() -> None:
    # No args → run the interactive wizard
    if not sys.argv[1:]:
        sys.exit(_interactive_wizard())

    parser = _build_parser()
    args = parser.parse_args()

    if args.command == "create-skills":
        sys.exit(cmd_create_skills(args))
    if args.command == "webui":
        sys.exit(cmd_webui(args))

    parser.print_help()
    sys.exit(1)
