"""Top-level Click group and global options for the alice CLI."""

from __future__ import annotations

import contextlib
import signal
from typing import TYPE_CHECKING

import click

# Handle SIGPIPE gracefully so pipes like `alice invoke "x" | head` don't crash.
# SIGPIPE is not available on Windows; contextlib.suppress handles the AttributeError.
with contextlib.suppress(AttributeError):
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
import click_completion  # type: ignore[import-untyped,unused-ignore]
import rich_click

from alice_cli import __version__
from alice_cli.config import resolve_config
from alice_cli.errors import AliceCLIError

if TYPE_CHECKING:
    from alice_cli.models import CLIConfig

# Configure rich-click for better help output
rich_click.rich_click.USE_RICH_MARKUP = True
rich_click.rich_click.SHOW_ARGUMENTS = True
rich_click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
rich_click.rich_click.USE_MARKDOWN = True
rich_click.rich_click.MAX_WIDTH = 100

# Group commands into logical sections in --help output
rich_click.rich_click.COMMAND_GROUPS = {
    "alice": [
        {
            "name": "AI / Models",
            "commands": ["invoke", "chat", "batch", "compare", "dialog", "summarize"],
        },
        {
            "name": "Sessions",
            "commands": ["recall", "appraise", "quota"],
        },
        {
            "name": "Authentication",
            "commands": ["auth", "export", "status", "config", "diagnose"],
        },
        {
            "name": "Secrets & Infrastructure",
            "commands": ["get-secret", "list-secrets", "list-models", "list-aliases", "run"],
        },
        {
            "name": "AgentCore",
            "commands": ["thread", "compose"],
        },
        {
            "name": "Citations",
            "commands": ["cite"],
        },
        {
            "name": "Setup",
            "commands": ["install-completion"],
        },
    ]
}

# Initialize click-completion
click_completion.init()


def launch_tui(config: CLIConfig, *, wordstar: bool = False) -> None:
    """Launch the interactive TUI mode.

    Lazily imports ``textual`` so CLI-only users never pay for the dependency.
    Raises :class:`AliceCLIError` if the ``textual`` extra is not installed.

    Parameters
    ----------
    wordstar:
        Activate WordStar mode — typewriter-style character-by-character
        output reveal with variable keystroke speed and a retro status bar.
    """
    try:
        from alice_cli.tui.app import AliceTUIApp
    except ImportError as exc:
        raise AliceCLIError(
            "TUI mode needs the 'textual' package.\n"
            "  Install it with: poetry install --extras tui"
        ) from exc
    app = AliceTUIApp(config, wordstar=wordstar)
    app.run()


class _FlexibleGroup(rich_click.RichGroup):
    """Allow group-level options (e.g. --profile) after the subcommand name."""

    def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
        # Collect group-level option names and which ones take a value
        group_opts: set[str] = set()
        value_opts: set[str] = set()
        for param in self.params:
            all_opts = set(param.opts) | set(param.secondary_opts)
            group_opts.update(all_opts)
            if not getattr(param, "is_flag", False):
                value_opts.update(all_opts)

        # Find the subcommand position, skipping option values
        cmd_idx: int | None = None
        skip_next = False
        for i, arg in enumerate(args):
            if skip_next:
                skip_next = False
                continue
            if arg in value_opts:
                skip_next = True
                continue
            if arg.startswith("-"):
                continue
            if arg in self.commands:
                cmd_idx = i
                break

        if cmd_idx is not None:
            # Determine which options also exist on the subcommand
            sub_cmd = self.commands.get(args[cmd_idx])
            sub_opts: set[str] = set()
            if sub_cmd is not None:
                for param in sub_cmd.params:
                    sub_opts.update(set(param.opts) | set(param.secondary_opts))

            before = list(args[:cmd_idx])
            after = list(args[cmd_idx + 1 :])
            reordered: list[str] = []
            rest: list[str] = []
            i = 0
            while i < len(after):
                if after[i] in group_opts and after[i] not in sub_opts:
                    # Group-only option — hoist it before the subcommand
                    reordered.append(after[i])
                    if after[i] in value_opts and i + 1 < len(after):
                        reordered.append(after[i + 1])
                        i += 2
                    else:
                        i += 1
                else:
                    rest.append(after[i])
                    i += 1
            args = before + reordered + [args[cmd_idx]] + rest

        return super().parse_args(ctx, args)


@click.group(cls=_FlexibleGroup, invoke_without_command=True)
@click.option("--profile", envvar="AWS_PROFILE", default=None, help="AWS CLI profile")
@click.option("--region", default=None, help="AWS region (default: us-east-1)")
@click.option("--namespace", default=None, help="Resource namespace (default: drcc)")
@click.option("--environment", default=None, help="Deployment environment (default: ai)")
@click.option(
    "--api-key",
    envvar="BEDROCK_SECRET_KEY",
    default=None,
    help="Bedrock API key (skips SSO/Secrets Manager; or set BEDROCK_SECRET_KEY)",
)
@click.option("--quiet", is_flag=True, help="Suppress banner and status messages")
@click.option(
    "--no-color",
    is_flag=True,
    envvar="NO_COLOR",
    help="Disable all color and Rich markup in output (also: set NO_COLOR env var)",
)
@click.option("--debug", is_flag=True, help="Enable debug logging (structured logs to stderr)")
@click.option(
    "--personality",
    envvar="ALICE_PERSONALITY",
    default=None,
    help="Voice personality (alice, mr_rogers, shodan)",
)
@click.option("--tui", is_flag=True, help="Launch interactive TUI mode")
@click.option(
    "--wordstar",
    is_flag=True,
    help="WordStar mode — typewriter output with variable speed and retro status bar (implies --tui)",  # noqa: E501
)
@click.version_option(version=__version__)
@click.pass_context
def cli(
    ctx: click.Context,
    profile: str | None,
    region: str | None,
    namespace: str | None,
    environment: str | None,
    api_key: str | None,
    quiet: bool,
    no_color: bool,
    debug: bool,
    personality: str | None,
    tui: bool,
    wordstar: bool,
) -> None:
    """ALiCE — Your AI research companion at Johns Hopkins."""
    # Apply color settings before any output is produced.
    if no_color:
        from alice_cli.console import reconfigure

        reconfigure(no_color=True)

    ctx.ensure_object(dict)
    ctx.obj["config"] = resolve_config(
        profile, region, namespace, environment, quiet, debug, api_key, personality
    )

    # Configure logging based on debug flag
    from alice_cli.logging import configure_logging

    if debug:
        configure_logging(json_logs=False, log_level="DEBUG")
    else:
        # Disable logging output in normal mode
        configure_logging(json_logs=False, log_level="CRITICAL")

    # Activate the chosen personality so all message shims use it.
    from alice_cli.personalities import set_active

    set_active(ctx.obj["config"].personality)
    if wordstar or tui:
        launch_tui(ctx.obj["config"], wordstar=wordstar)
        ctx.exit()


@cli.command("install-completion")
@click.option(
    "--shell",
    type=click.Choice(["bash", "zsh", "fish", "powershell"]),
    help="Shell type (auto-detected if not specified)",
)
def install_completion(shell: str | None) -> None:
    """Install shell completion for alice CLI.

    This enables tab completion for commands, options, and arguments.
    After installation, restart your shell or source your shell config file.

    Examples:
        alice install-completion
        alice install-completion --shell bash
        alice install-completion --shell zsh
    """
    shell_type, path = click_completion.install(shell=shell, prog_name="alice")  # nosec B604
    click.echo(f"Shell completion installed for {shell_type}")
    click.echo(f"Completion script: {path}")
    click.echo("\nRestart your shell or run:")
    if shell_type == "bash":
        click.echo(f"  source {path}")
    elif shell_type == "zsh":
        click.echo("  source ~/.zshrc")
    elif shell_type == "fish":
        click.echo("  source ~/.config/fish/config.fish")


from alice_cli.commands.appraise import appraise_cmd  # noqa: E402
from alice_cli.commands.auth import auth  # noqa: E402
from alice_cli.commands.batch import batch_cmd  # noqa: E402
from alice_cli.commands.chat import chat_cmd  # noqa: E402
from alice_cli.commands.cite import cite_group  # noqa: E402
from alice_cli.commands.compare import compare_cmd  # noqa: E402
from alice_cli.commands.compose import compose  # noqa: E402
from alice_cli.commands.config_cmd import config_cmd  # noqa: E402
from alice_cli.commands.diagnose import diagnose_cmd  # noqa: E402
from alice_cli.commands.dialog import dialog_cmd  # noqa: E402
from alice_cli.commands.gateway import gateway_group  # noqa: E402
from alice_cli.commands.get_key import get_key as export_cmd  # noqa: E402
from alice_cli.commands.get_secret import get_secret  # noqa: E402
from alice_cli.commands.invoke import invoke_cmd  # noqa: E402
from alice_cli.commands.list_aliases import list_aliases  # noqa: E402
from alice_cli.commands.list_models import list_models  # noqa: E402
from alice_cli.commands.list_secrets import list_secrets  # noqa: E402
from alice_cli.commands.quota import quota_cmd  # noqa: E402
from alice_cli.commands.recall import recall_cmd  # noqa: E402
from alice_cli.commands.run import run_cmd  # noqa: E402
from alice_cli.commands.status import status  # noqa: E402
from alice_cli.commands.summarize import summarize_cmd  # noqa: E402
from alice_cli.commands.teaparty import teaparty_cmd  # noqa: E402
from alice_cli.commands.thread import thread_group  # noqa: E402

cli.add_command(appraise_cmd)
cli.add_command(auth)
cli.add_command(batch_cmd)
cli.add_command(chat_cmd)
cli.add_command(cite_group)
cli.add_command(compare_cmd)
cli.add_command(compose)
cli.add_command(config_cmd)
cli.add_command(diagnose_cmd)
cli.add_command(gateway_group)
cli.add_command(dialog_cmd)
cli.add_command(export_cmd)
cli.add_command(get_secret)
cli.add_command(invoke_cmd)
cli.add_command(list_aliases)
cli.add_command(list_models)
cli.add_command(list_secrets)
cli.add_command(quota_cmd)
cli.add_command(recall_cmd)
cli.add_command(run_cmd)
cli.add_command(status)
cli.add_command(summarize_cmd)
cli.add_command(teaparty_cmd)
cli.add_command(thread_group)

# Deprecated alias: 'alice env' → 'alice export'
import copy as _copy  # noqa: E402

_env_alias = _copy.copy(export_cmd)
_env_alias.name = "env"
_env_alias.hidden = True
_original_export_callback = _env_alias.callback


def _env_deprecated_callback(*args: object, **kwargs: object) -> object:
    click.echo(
        "Deprecated: 'alice env' has been renamed to 'alice export'.",
        err=True,
    )
    return _original_export_callback(*args, **kwargs)  # type: ignore[misc]


_env_alias.callback = _env_deprecated_callback
cli.add_command(_env_alias)
