import enum
import sys
import traceback
from collections.abc import Callable
from functools import wraps
from typing import Any, Protocol, cast

import click

from . import __version__


class EnumChoice[Enum: enum.Enum](click.Choice):
    values: type[Enum]

    def __init__(self, values: type[Enum]) -> None:
        super().__init__(
            [value.replace("_", "-") for value in values.__members__],
            case_sensitive=False,
        )
        self.values = values

    def convert(
        self, value: Any, param: click.Parameter | None, ctx: click.Context | None
    ) -> Enum:
        if isinstance(value, self.values):
            return value

        value = super().convert(value, param, ctx).replace("-", "_")
        return cast(Enum, getattr(self.values, value))


class AcceptingVerbose[T, **P](Protocol):
    def __call__(self, verbose: bool, *args: P.args, **kwargs: P.kwargs) -> T: ...


def error_reporting[T, **P](fun: Callable[P, T], /) -> AcceptingVerbose[T, P]:
    @click.option(
        "--verbose/--no-verbose",
        "-v/-V",
        default=False,
        help="Run command in verbose mode",
    )
    @wraps(fun)
    @click.pass_context
    def wrapper(
        ctx: click.Context,
        /,
        verbose: bool = False,
        *args: P.args,
        **kwargs: P.kwargs,
    ) -> T:
        try:
            return ctx.invoke(
                fun,
                *args,
                **kwargs,
            )
        except Exception as e:
            if verbose:
                click.secho(f"An error occurred: `{e!r}`", fg="red")
                for line in traceback.format_exception(e):
                    click.secho(f"{line}", nl=False, fg="red")
            else:
                click.secho(
                    f"An error occurred: `{e}` (re-run with -v for stacktrace)",
                    fg="red",
                )
            sys.exit(-1)

    return wrapper


versioned = click.version_option(__version__)
