"""Configuration commands."""

from __future__ import annotations

import os

import typer
from rich import box
from rich.table import Table

try:
    from shared import NotFoundError, normalize_api_base_url
except ImportError:
    from cli._vendor.shared import NotFoundError, normalize_api_base_url

from ..exceptions import CLIError, KeyringFailure
from ..keychain import AuthSource, get_api_key_with_env_override
from ._auth import authenticated_client, require_auth
from ._context import require_cli_context

KEY_PREFIX_LENGTH = 14  # "ctxme_" (6 chars) + 8 chars for lookup


def _replace(config, **updates):
    return config.model_copy(update=updates)


def _set_project(cli_ctx, project: str) -> None:
    config = cli_ctx.load_config()
    config = _replace(config, default_project=project)
    cli_ctx.save_config(config)
    cli_ctx.console.print(f"[green]Default project set to[/green] [bold]{project}[/bold].")


def register(app: typer.Typer) -> None:
    @app.command("config", help="Update CLI configuration.")
    def configure(
        ctx: typer.Context,
        api: str = typer.Option(None, "--api", hidden=True, help="Set backend API base URL."),
        default_project: str = typer.Option(
            None,
            "--default-project",
            "-p",
            help="Set the default project key.",
        ),
        mode: str = typer.Option(
            None,
            "--mode",
            hidden=True,
            case_sensitive=False,
            help="Force mock or live client mode.",
        ),
    ) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()
        updates: dict[str, str | bool | None] = {}
        if api:
            updates["api_base_url"] = normalize_api_base_url(api)
            if mode is None:
                updates["use_mock_client"] = False

        if default_project:
            updates["default_project"] = default_project

        if mode is not None:
            normalized = mode.lower()
            if normalized not in {"mock", "live"}:
                raise typer.BadParameter("Mode must be either 'mock' or 'live'.", param_name="mode")
            updates["use_mock_client"] = normalized == "mock"

        if not updates:
            cli_ctx.console.print("[yellow]No configuration changes provided.[/yellow]")
            return

        config = _replace(config, **updates)
        cli_ctx.save_config(config)
        table = Table(title="CLI Configuration", box=box.MINIMAL_DOUBLE_HEAD)
        table.add_column("Key", style="dark_cyan")
        table.add_column("Value", style="green")
        table.add_row("API Base URL", config.api_base_url or "<not set>")
        table.add_row("Default Project", config.default_project or "<not set>")
        table.add_row("Use Mock Client", "yes" if config.use_mock_client else "no")
        cli_ctx.console.print(table)

    @app.command("set-project", help="Set the default project key.")
    @app.command("use", help="Set the default project key (alias for 'set-project').", hidden=True)
    def set_project(
        ctx: typer.Context, project: str = typer.Argument(..., help="Project key to pin.")
    ) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()

        # Require authentication first (mock mode bypasses this)
        require_auth(config)

        # Validate project exists via API (mock mode bypasses this)
        if not config.use_mock_client:
            try:
                with authenticated_client(cli_ctx, config) as client:
                    client.get_project(project)
            except NotFoundError:
                raise CLIError(
                    f"Project '{project}' not found. "
                    "Run 'ctxme projects list' to see available projects."
                )

        _set_project(cli_ctx, project)

    @app.command("current", help="Show the current default project.")
    def current_project(ctx: typer.Context) -> None:
        """Show the current default project (alias for 'projects current')."""
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()
        if config.default_project:
            cli_ctx.console.print(
                f"[green]Default project:[/green] [bold]{config.default_project}[/bold]."
            )
        else:
            cli_ctx.console.print("[yellow]No default project set.[/yellow]")

    @app.command("status", help="Show local configuration and authentication status.")
    def status(ctx: typer.Context) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()

        project_value = (
            f"{config.default_project} (default)"
            if config.default_project
            else "None (use 'set-project' to set)"
        )

        try:
            api_key, source = get_api_key_with_env_override()
        except KeyringFailure:
            api_key = None
            source = None

        if api_key:
            auth_value = f"Signed in (key: {api_key[:KEY_PREFIX_LENGTH]}...)"
        else:
            auth_value = "Not signed in"

        # Show API and Mode only in debug mode (--debug flag or CTXME_DEBUG=1)
        debug_value = os.getenv("CTXME_DEBUG", "").strip().lower()
        if debug_value in {"1", "true", "yes"}:
            api_value = config.api_base_url or "Not configured"
            mode_value = "mock" if config.use_mock_client else "live"
            cli_ctx.console.print(f"API: {api_value}")
            cli_ctx.console.print(f"Mode: {mode_value}")
            if source == AuthSource.ENV:
                cli_ctx.console.print("API Key: Using override (CLI__API_KEY)")

        cli_ctx.console.print(f"Auth: {auth_value}")
        cli_ctx.console.print(f"Project: {project_value}")
