"""``ilum auth`` sub-app — manage authentication and OAuth configuration."""

from __future__ import annotations

import typer

import ilum.cli.output as output_mod
from ilum.cli.defaults import resolve_profile_defaults
from ilum.config.manager import ConfigManager
from ilum.config.paths import IlumPaths
from ilum.constants import DEFAULT_CHART_REF
from ilum.core.auth import (
    AuthMode,
    detect_auth_mode,
    generate_disable_flags,
    generate_enable_flags,
    get_provider,
    list_providers,
)
from ilum.core.helm import HelmClient
from ilum.core.kubernetes import KubeClient
from ilum.core.modules import ModuleResolver
from ilum.core.release import ReleaseManager
from ilum.errors import AuthError, IlumError
from ilum.wizard.deps import ensure_tools

auth_app = typer.Typer(help="Manage authentication and OAuth configuration.", no_args_is_help=True)


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


def _build_manager(
    context: str,
    namespace: str,
    timeout: str = "10m",
) -> ReleaseManager:
    paths = IlumPaths.default()
    paths.ensure_dirs()
    return ReleaseManager(
        helm=HelmClient(kubecontext=context, namespace=namespace, timeout=timeout),
        k8s=KubeClient(kubecontext=context),
        resolver=ModuleResolver(),
        config_mgr=ConfigManager(paths),
        paths=paths,
    )


def _mask_secret(value: str) -> str:
    """Mask a secret value for display, showing first 4 chars."""
    if len(value) <= 4:
        return "****"
    return value[:4] + "****"


def _run_upgrade(
    mgr: ReleaseManager,
    release: str,
    namespace: str,
    set_flags: list[str],
    extra_set_flags: list[str] | None,
    dry_run: bool,
    yes: bool,
    console: output_mod.IlumConsole,
    chart: str = "",
    devel: bool = False,
    reset_defaults: bool = False,
) -> None:
    """Execute a Helm upgrade through the values-safety pipeline."""
    chart_ref = chart or DEFAULT_CHART_REF
    all_flags = set_flags + (extra_set_flags or [])

    plan = mgr.plan_upgrade(
        release=release,
        chart=chart_ref,
        set_flags=all_flags,
        devel=devel,
        reset_defaults=reset_defaults,
    )

    # Show drift
    if plan.drift and plan.drift.has_drift and plan.drift.diff:
        console.warning("External changes detected:")
        console.diff_table(plan.drift.diff, title="External Changes (Drift)")

    # Show diff
    if plan.effective_diff and not plan.effective_diff.is_empty:
        console.diff_table(plan.effective_diff)
    else:
        console.info("No values changes needed.")
        return

    console.command_preview(mgr.preview_command(plan))

    if dry_run:
        console.info("Dry-run mode — no changes applied.")
        return

    if not yes and not console.confirm("Proceed?"):
        console.info("Aborted.")
        raise typer.Exit()

    from ilum.cli.progress import execute_with_progress

    execute_with_progress(mgr, plan, console, message="Updating auth configuration...")


# ---------------------------------------------------------------------------
# ilum auth setup
# ---------------------------------------------------------------------------


@auth_app.command()
def setup(
    release: str | None = typer.Option(
        None, "--release", "-r", show_default="from profile", help="Helm release name."
    ),
    namespace: str | None = typer.Option(
        None, "--namespace", "-n", show_default="from profile", help="Kubernetes namespace."
    ),
    context: str | None = typer.Option(
        None, "--context", show_default="from profile", help="Kubernetes context."
    ),
    set_flags: list[str] | None = typer.Option(  # noqa: B008
        None, "--set", help="Additional Helm --set flag (repeatable)."
    ),
    chart: str = typer.Option(
        "", "--chart", help="Chart reference or local path (default: ilum/ilum)."
    ),
    devel: bool = typer.Option(False, "--devel", help="Include pre-release chart versions."),
    reset_defaults: bool = typer.Option(
        False,
        "--reset-defaults",
        help="Reset to new chart defaults before applying user values."
        " Use when upgrading across chart versions with breaking changes.",
    ),
    yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
    dry_run: bool = typer.Option(False, "--dry-run", help="Preview without executing."),
    timeout: str = typer.Option("10m", "--timeout", help="Helm timeout."),
) -> None:
    """Interactive authentication setup wizard."""
    import questionary

    release, namespace, context = resolve_profile_defaults(release, namespace, context)
    console = output_mod.console

    try:
        ensure_tools(["helm"], console, interactive=not yes)

        # Step 1: Choose auth mode
        mode_choice = questionary.select(
            "Select authentication mode:",
            choices=[
                questionary.Choice("Internal (Hydra OIDC)", value="hydra"),
                questionary.Choice("External OAuth2 provider", value="external"),
                questionary.Choice("Disable (revert to internal auth)", value="disable"),
            ],
        ).ask()

        if mode_choice is None:
            raise typer.Exit()

        if mode_choice == "disable":
            mgr = _build_manager(context, namespace, timeout)
            flags = generate_disable_flags()
            _run_upgrade(
                mgr,
                release,
                namespace,
                flags,
                set_flags,
                dry_run,
                yes,
                console,
                chart=chart,
                devel=devel,
                reset_defaults=reset_defaults,
            )
            console.success("Authentication reverted to internal.")
            return

        if mode_choice == "external":
            # Step 2: Choose provider
            providers = list_providers()
            external = [p for p in providers if p.auth_mode is AuthMode.EXTERNAL_OAUTH2]
            provider_choice = questionary.select(
                "Select OAuth2 provider:",
                choices=[
                    questionary.Choice(f"{p.name.value} — {p.description}", value=p.name.value)
                    for p in external
                ],
            ).ask()

            if provider_choice is None:
                raise typer.Exit()

            provider = get_provider(provider_choice)
        else:
            provider = get_provider("hydra")

        # Step 3: Collect parameters
        params: dict[str, str] = {}
        for param in provider.params:
            default = param.default or None
            value = questionary.text(
                f"{param.prompt}:",
                default=default or "",
            ).ask()

            if value is None:
                raise typer.Exit()
            if not value and param.required and not param.default:
                console.error(f"Required parameter '{param.name}' cannot be empty.")
                raise typer.Exit(code=1)
            params[param.name] = value or param.default

        # Step 4: Generate flags and execute
        flags = generate_enable_flags(provider, params)

        # Show masked summary
        summary_rows = [
            ["Action", "Enable Auth"],
            ["Provider", provider.name.value],
        ]
        for param in provider.params:
            val = params.get(param.name, "")
            display_val = _mask_secret(val) if param.secret else val
            summary_rows.append([param.prompt, display_val])
        console.operation_summary(summary_rows, title="Auth Configuration")

        if provider.notes:
            console.info(provider.notes)

        mgr = _build_manager(context, namespace, timeout)
        _run_upgrade(
            mgr,
            release,
            namespace,
            flags,
            set_flags,
            dry_run,
            yes,
            console,
            chart=chart,
            devel=devel,
            reset_defaults=reset_defaults,
        )
        console.success(f"Authentication configured: {provider.name.value}")

    except IlumError as exc:
        console.handle_error(exc)
        raise typer.Exit(code=1) from exc


# ---------------------------------------------------------------------------
# ilum auth enable
# ---------------------------------------------------------------------------


@auth_app.command()
def enable(
    provider_name: str = typer.Argument(..., help="Provider name (e.g. google, hydra, oidc)."),
    client_id: str | None = typer.Option(None, "--client-id", help="OAuth2 Client ID."),
    client_secret: str | None = typer.Option(None, "--client-secret", help="OAuth2 Client Secret."),
    domain: str | None = typer.Option(None, "--domain", help="Domain (Hydra UI domain or Okta)."),
    protocol: str | None = typer.Option(None, "--protocol", help="Protocol (Hydra: http/https)."),
    url: str | None = typer.Option(None, "--url", help="Provider URL (GitLab, Keycloak)."),
    realm: str | None = typer.Option(None, "--realm", help="Keycloak realm name."),
    region: str | None = typer.Option(None, "--region", help="AWS region (Cognito)."),
    user_pool_id: str | None = typer.Option(None, "--user-pool-id", help="Cognito User Pool ID."),
    tenant_id: str | None = typer.Option(None, "--tenant-id", help="Azure AD Tenant ID."),
    issuer_uri: str | None = typer.Option(None, "--issuer-uri", help="OIDC Issuer URI."),
    scope: str | None = typer.Option(None, "--scope", help="OAuth2 scopes."),
    release: str | None = typer.Option(
        None, "--release", "-r", show_default="from profile", help="Helm release name."
    ),
    namespace: str | None = typer.Option(
        None, "--namespace", "-n", show_default="from profile", help="Kubernetes namespace."
    ),
    context: str | None = typer.Option(
        None, "--context", show_default="from profile", help="Kubernetes context."
    ),
    set_flags: list[str] | None = typer.Option(  # noqa: B008
        None, "--set", help="Additional Helm --set flag (repeatable)."
    ),
    chart: str = typer.Option(
        "", "--chart", help="Chart reference or local path (default: ilum/ilum)."
    ),
    devel: bool = typer.Option(False, "--devel", help="Include pre-release chart versions."),
    reset_defaults: bool = typer.Option(
        False,
        "--reset-defaults",
        help="Reset to new chart defaults before applying user values."
        " Use when upgrading across chart versions with breaking changes.",
    ),
    yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
    dry_run: bool = typer.Option(False, "--dry-run", help="Preview without executing."),
    timeout: str = typer.Option("10m", "--timeout", help="Helm timeout."),
) -> None:
    """Enable authentication with a specific provider."""
    release, namespace, context = resolve_profile_defaults(release, namespace, context)
    console = output_mod.console

    try:
        ensure_tools(["helm"], console, interactive=not yes)
        provider = get_provider(provider_name)

        # Build params dict from CLI options
        option_map: dict[str, str | None] = {
            "client-id": client_id,
            "client-secret": client_secret,
            "domain": domain,
            "protocol": protocol,
            "url": url,
            "realm": realm,
            "region": region,
            "user-pool-id": user_pool_id,
            "tenant-id": tenant_id,
            "issuer-uri": issuer_uri,
        }

        params: dict[str, str] = {}
        for param in provider.params:
            value = option_map.get(param.name)
            if value is not None:
                params[param.name] = value
            elif param.default:
                params[param.name] = param.default
            elif param.required:
                raise AuthError(
                    f"Missing required parameter: --{param.name}",
                    suggestion=f"Use: ilum auth enable {provider_name} --{param.name} <value>",
                    error_code="ILUM-061",
                )

        flags = generate_enable_flags(provider, params, scope=scope or "")

        # Show masked summary
        summary_rows = [
            ["Action", "Enable Auth"],
            ["Provider", provider.name.value],
        ]
        for param in provider.params:
            val = params.get(param.name, "")
            display_val = _mask_secret(val) if param.secret else val
            summary_rows.append([param.prompt, display_val])
        console.operation_summary(summary_rows, title="Auth Configuration")

        if provider.notes:
            console.info(provider.notes)

        mgr = _build_manager(context, namespace, timeout)
        _run_upgrade(
            mgr,
            release,
            namespace,
            flags,
            set_flags,
            dry_run,
            yes,
            console,
            chart=chart,
            devel=devel,
            reset_defaults=reset_defaults,
        )
        console.success(f"Authentication configured: {provider.name.value}")

    except IlumError as exc:
        console.handle_error(exc)
        raise typer.Exit(code=1) from exc


# ---------------------------------------------------------------------------
# ilum auth disable
# ---------------------------------------------------------------------------


@auth_app.command()
def disable(
    release: str | None = typer.Option(
        None, "--release", "-r", show_default="from profile", help="Helm release name."
    ),
    namespace: str | None = typer.Option(
        None, "--namespace", "-n", show_default="from profile", help="Kubernetes namespace."
    ),
    context: str | None = typer.Option(
        None, "--context", show_default="from profile", help="Kubernetes context."
    ),
    set_flags: list[str] | None = typer.Option(  # noqa: B008
        None, "--set", help="Additional Helm --set flag (repeatable)."
    ),
    chart: str = typer.Option(
        "", "--chart", help="Chart reference or local path (default: ilum/ilum)."
    ),
    devel: bool = typer.Option(False, "--devel", help="Include pre-release chart versions."),
    reset_defaults: bool = typer.Option(
        False,
        "--reset-defaults",
        help="Reset to new chart defaults before applying user values."
        " Use when upgrading across chart versions with breaking changes.",
    ),
    yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
    dry_run: bool = typer.Option(False, "--dry-run", help="Preview without executing."),
    timeout: str = typer.Option("10m", "--timeout", help="Helm timeout."),
) -> None:
    """Disable OAuth and revert to internal authentication."""
    release, namespace, context = resolve_profile_defaults(release, namespace, context)
    console = output_mod.console

    try:
        ensure_tools(["helm"], console, interactive=not yes)
        mgr = _build_manager(context, namespace, timeout)
        flags = generate_disable_flags()

        summary_rows = [
            ["Action", "Disable Auth"],
            ["Release", release],
            ["Mode", "internal"],
        ]
        console.operation_summary(summary_rows, title="Auth Configuration")

        _run_upgrade(
            mgr,
            release,
            namespace,
            flags,
            set_flags,
            dry_run,
            yes,
            console,
            chart=chart,
            devel=devel,
            reset_defaults=reset_defaults,
        )
        console.success("Authentication reverted to internal.")

    except IlumError as exc:
        console.handle_error(exc)
        raise typer.Exit(code=1) from exc


# ---------------------------------------------------------------------------
# ilum auth status
# ---------------------------------------------------------------------------


@auth_app.command()
def status(
    release: str | None = typer.Option(
        None, "--release", "-r", show_default="from profile", help="Helm release name."
    ),
    namespace: str | None = typer.Option(
        None, "--namespace", "-n", show_default="from profile", help="Kubernetes namespace."
    ),
    context: str | None = typer.Option(
        None, "--context", show_default="from profile", help="Kubernetes context."
    ),
    timeout: str = typer.Option("10m", "--timeout", help="Helm timeout."),
) -> None:
    """Show current authentication configuration."""
    release, namespace, context = resolve_profile_defaults(release, namespace, context)
    console = output_mod.console

    try:
        ensure_tools(["helm"], console)
        mgr = _build_manager(context, namespace, timeout)
        live = mgr.fetch_computed_values(release)
        mode, details = detect_auth_mode(live)

        from ilum.cli.formatters import CommandResult, OutputFormat, ResultFormatter

        fmt = OutputFormat(console.output_format)

        data = {"mode": mode.value, **details}

        if fmt != OutputFormat.TABLE:
            result = CommandResult(data=data, summary=f"Auth mode: {mode.value}")
            ResultFormatter().format(result, fmt, console)
            return

        rows = [["Mode", mode.value]]
        for key, value in details.items():
            if key == "provider":
                continue
            display_key = key.replace("_", " ").title()
            rows.append([display_key, str(value)])

        console.table(
            f"Auth Status (release: {release})",
            ["Setting", "Value"],
            rows,
        )

    except IlumError as exc:
        console.handle_error(exc)
        raise typer.Exit(code=1) from exc


# ---------------------------------------------------------------------------
# ilum auth list
# ---------------------------------------------------------------------------


@auth_app.command("list")
def list_cmd() -> None:
    """List available authentication providers."""
    console = output_mod.console
    providers = list_providers()

    from ilum.cli.formatters import CommandResult, OutputFormat, ResultFormatter

    fmt = OutputFormat(console.output_format)

    if fmt != OutputFormat.TABLE:
        data = [
            {
                "name": p.name.value,
                "description": p.description,
                "mode": p.auth_mode.value,
                "required_params": [param.name for param in p.params if param.required],
            }
            for p in providers
        ]
        result = CommandResult(data=data, summary=f"{len(providers)} providers")
        ResultFormatter().format(result, fmt, console)
        return

    rows = [
        [
            p.name.value,
            p.description,
            p.auth_mode.value,
            ", ".join(f"--{param.name}" for param in p.params if param.required),
        ]
        for p in providers
    ]
    console.table(
        "Available Auth Providers",
        ["Provider", "Description", "Mode", "Required Flags"],
        rows,
    )
