#pyright: reportPrivateImportUsage=false
from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Sequence, cast

import click


@dataclass(frozen=True)
class SelectOption:
    """An option for interactive selection."""

    value: Any
    label: str


def fuzzy_select(prompt: str, options: Sequence[SelectOption], *, default: Any | None = None) -> Any:
    """Interactive searchable select (fuzzy search), copied from upstream behavior.

    This mirrors the UX in the upstream CLI:
    - bordered fuzzy selector with limited height
    - consistent keybindings (esc / ctrl-c / ctrl-d abort)
    - supports a "default" without filtering results (moves cursor internally)
    """
    if not options:
        raise ValueError("fuzzy_select requires at least one option")

    # Local imports keep base CLI import-light.
    from InquirerPy import inquirer
    from InquirerPy import utils as inquirer_utils
    from InquirerPy.base.control import Choice

    try:
        # Upstream-style prompt theming.
        style = inquirer_utils.get_style({"fuzzy_prompt": "#e5c07b"}, False)

        # Upstream-style keybindings: escape / ctrl-c / ctrl-d abort.
        keybindings: Any = {
            "interrupt": [{"key": "escape"}, {"key": "c-c"}, {"key": "c-d"}],
        }

        choices = [Choice(value=o.value, name=o.label) for o in options]

        # NOTE: Using InquirerPy's builtin `default` filters results for fuzzy prompts.
        # Upstream avoids that by moving the cursor to the default via internals.
        default_name: str | None = None
        if default is not None:
            for opt in options:
                if opt.value == default:
                    default_name = opt.label
                    break

        prompt_obj = cast(Any, inquirer).fuzzy(  # type: ignore[attr-defined]
            message=prompt,
            choices=choices,
            max_height=8,
            border=True,
            style=style,
            keybindings=keybindings,
        )

        # Best-effort cursor positioning to the default choice without filtering.
        if default_name:
            try:
                prompt_obj._content_control._get_choices(prompt_obj._content_control.choices, default_name)  # type: ignore[attr-defined]
            except Exception:
                pass

        result = prompt_obj.execute()
    except KeyboardInterrupt as e:
        raise click.Abort() from e
    except Exception as e:
        # InquirerPy / prompt_toolkit sometimes raises this on Ctrl+C instead of
        # propagating KeyboardInterrupt (seen in the wild).
        msg = str(e)
        if "Return value already set" in msg and "Application.exit()" in msg:
            raise click.Abort() from e
        raise

    # InquirerPy generally returns the Choice.value when `Choice` is used, but keep
    # this robust in case behavior changes.
    if isinstance(result, Choice):
        return result.value
    return result

