from __future__ import annotations

import sys

from typing import TYPE_CHECKING

import click

from ai_rules.cli.display import print_dim

if TYPE_CHECKING:
    from ai_rules.bootstrap.updater import ToolSpec


def _filter_enabled(tools: list[ToolSpec]) -> list[ToolSpec]:
    """Drop tools whose ``is_enabled`` callable returns False."""
    return [t for t in tools if t.is_enabled is None or t.is_enabled()]


def _only_choices() -> list[str]:
    from ai_rules.bootstrap.updater import _TOOL_ID_ALIASES, get_updatable_tools

    return [t.tool_id for t in get_updatable_tools()] + list(_TOOL_ID_ALIASES.keys())


@click.command()
@click.option("--check", is_flag=True, help="Check for updates without installing")
@click.option("--force", is_flag=True, help="Force reinstall even if up to date")
@click.option(
    "-y", "--yes", is_flag=True, help="Auto-confirm installation without prompting"
)
@click.option(
    "--skip-install",
    is_flag=True,
    help="Skip running 'install --rebuild-cache' after upgrade",
)
@click.option(
    "--only",
    type=click.Choice(_only_choices()),
    help="Only upgrade specific tool",
)
def upgrade(
    check: bool, force: bool, yes: bool, skip_install: bool, only: str | None
) -> None:
    """Upgrade ai-agent-rules and related tools to the latest versions.

    Each tool upgrades from its configured install source (PyPI or GitHub).

    Examples:
        ai-agent-rules upgrade                    # Check and install all updates
        ai-agent-rules upgrade --check            # Only check for updates
        ai-agent-rules upgrade -y                 # Auto-confirm installation
        ai-agent-rules upgrade --only=statusline  # Only upgrade statusline tool
    """
    from ai_rules.bootstrap import (
        ToolSource,
        check_tool_updates,
        get_effective_install_source,
        get_updatable_tools,
        perform_tool_upgrade,
    )
    from ai_rules.bootstrap.installer import install_tool
    from ai_rules.bootstrap.updater import _TOOL_ID_ALIASES
    from ai_rules.cli.display import (
        console,
        print_done,
        print_error,
        print_hint,
        print_success,
        print_warning,
    )

    resolved_only = _TOOL_ID_ALIASES.get(only, only) if only else None
    all_tools = [
        t
        for t in get_updatable_tools()
        if resolved_only is None or t.tool_id == resolved_only
    ]
    tools = [t for t in all_tools if t.is_installed()]
    if resolved_only is None:
        tools = _filter_enabled(tools)
    missing_tools = [t for t in all_tools if not t.is_installed()]
    if resolved_only is None:
        missing_tools = _filter_enabled(missing_tools)

    for tool in missing_tools:
        print_warning(f"{tool.display_name} is not installed")

    if missing_tools and not check:
        if yes or click.confirm("\nReinstall missing tools?", default=True):
            for tool in missing_tools:
                source, local_path = get_effective_install_source(tool.tool_id)
                from_github = source == ToolSource.GITHUB
                github_url = tool.github_install_url if from_github else None
                with console.status(f"Installing {tool.display_name}..."):
                    success, msg = install_tool(
                        tool.package_name,
                        from_github=from_github,
                        github_url=github_url,
                        local_path=local_path,
                    )
                if success:
                    print_success(f"{tool.display_name} reinstalled")
                    tools.append(tool)
                else:
                    print_error(f"Failed to install {tool.display_name}: {msg}")

    if not tools:
        if only:
            print_warning(f"Tool '{only}' is not installed")
        else:
            print_warning("No tools are installed")
        sys.exit(1)

    tool_updates = []
    for tool in tools:
        try:
            current = tool.get_version()
            if current:
                print_dim(f"{tool.display_name} current version: {current}")
        except Exception as e:
            print_error(f"Could not get {tool.display_name} version: {e}")
            continue

        from ai_rules.bootstrap.updater import _resolve_effective_source

        if _resolve_effective_source(tool) == ToolSource.LOCAL:
            print_warning(
                f"{tool.display_name} is installed from a local path — "
                f"skipping update check. Reinstall from PyPI with: "
                f"uv tool install {tool.package_name} --force"
            )
            continue

        with console.status(f"Checking {tool.display_name} for updates..."):
            try:
                update_info = check_tool_updates(tool)
            except Exception as e:
                print_error(f"Failed to check {tool.display_name} updates: {e}")
                continue

        if update_info and (update_info.has_update or force):
            if update_info.check_failed and force:
                print_warning(
                    f"Update check failed for {tool.display_name}, forcing upgrade"
                )
            tool_updates.append((tool, update_info))
        elif update_info and update_info.check_failed:
            print_warning(f"Could not check {tool.display_name} for updates")
        elif update_info and not update_info.has_update:
            print_success(f"{tool.display_name} is already up to date!")

    console.print()

    if not tool_updates:
        print_success("All tools are up to date!")
        return

    for tool, update_info in tool_updates:
        if update_info.has_update:
            console.print(
                f"[cyan]Update available for {tool.display_name}:[/cyan] "
                f"{update_info.current_version} → {update_info.latest_version}"
            )

            if update_info.changelog_entries:
                console.print()
                for version, notes in update_info.changelog_entries:
                    console.print(f"  [bold]v{version}[/bold]")
                    for line in notes.strip().split("\n"):
                        if line.strip():
                            console.print(f"    {line.strip()}")
                console.print()

    if check:
        if tool_updates:
            print_hint("Run 'ai-agent-rules upgrade' to install")
        return

    if not force and not yes:
        if len(tool_updates) == 1:
            prompt = f"\nInstall {tool_updates[0][0].display_name} update?"
        else:
            prompt = f"\nInstall {len(tool_updates)} updates?"
        if not click.confirm(prompt, default=True):
            print_warning("Cancelled")
            return

    from ai_rules.state import get_active_profile

    current_profile = get_active_profile() or "default"
    self_post_upgrade_cmd = [
        "ai-agent-rules",
        "install",
        "--rebuild-cache",
        "--force",
        "-y",
        "--profile",
        current_profile,
    ]

    ai_rules_upgraded = False
    for tool, update_info in tool_updates:
        with console.status(f"Upgrading {tool.display_name}..."):
            try:
                success, msg, was_upgraded = perform_tool_upgrade(
                    tool,
                    target_version=update_info.latest_version,
                    is_self=(tool.tool_id == "ai-agent-rules"),
                    post_upgrade_cmd=self_post_upgrade_cmd
                    if tool.tool_id == "ai-agent-rules"
                    else None,
                )
            except Exception as e:
                print_error(f"{tool.display_name} upgrade failed: {e}")
                continue

        if success:
            if msg == "deferred":
                print_success(f"{tool.display_name} upgrade started in the background")
                print_hint(
                    "Run 'ai-agent-rules install --rebuild-cache' once it completes if needed"
                )
            else:
                new_version = tool.get_version()
                if new_version == update_info.latest_version:
                    print_success(f"{tool.display_name} upgraded to {new_version}")
                    if tool.tool_id == "ai-agent-rules":
                        ai_rules_upgraded = True
                elif new_version == update_info.current_version:
                    print_warning(
                        f"{tool.display_name} upgrade reported success but version "
                        f"unchanged ({new_version})"
                    )
                    if msg and msg != "Upgrade successful":
                        print_hint(msg)
                    else:
                        print_hint(
                            "This may be due to a Python version mismatch. Check the "
                            "package's requires-python and try: "
                            f"uv tool upgrade {tool.package_name} --python <version>"
                        )
                else:
                    print_success(f"{tool.display_name} upgraded to {new_version}")
                    if tool.tool_id == "ai-agent-rules":
                        ai_rules_upgraded = True
        else:
            print_error(f"{tool.display_name} upgrade failed: {msg}")

    if ai_rules_upgraded and not skip_install:
        try:
            import subprocess

            console.print()
            print_dim("Running 'ai-agent-rules install --rebuild-cache'...")

            from ai_rules.state import get_active_profile

            current_profile = get_active_profile() or "default"

            result = subprocess.run(
                [
                    "ai-agent-rules",
                    "install",
                    "--rebuild-cache",
                    "--force",
                    "-y",
                    "--profile",
                    current_profile,
                ],
                capture_output=False,
                text=True,
                timeout=30,
            )

            if result.returncode == 0:
                print_done("Install completed successfully")
            else:
                print_warning(f"Install failed with exit code {result.returncode}")
                print_hint(
                    "Run 'ai-agent-rules install --rebuild-cache --force' manually to retry"
                )
        except subprocess.TimeoutExpired:
            print_warning("Install timed out after 30 seconds")
            print_hint(
                "Run 'ai-agent-rules install --rebuild-cache --force' manually to retry"
            )
        except Exception as e:
            print_warning(f"Could not run install: {e}")
            print_hint("Run 'ai-agent-rules install --rebuild-cache' manually")
