from __future__ import annotations

from dataclasses import replace
from functools import wraps
from typing import Any
from typing import Callable

import click


# from https://stackoverflow.com/questions/56185880/commands-with-multiple-common-options-going-into-one-argument-using-custom-decor
# This drags in scipy and pandas and numpy from imports above
# *and* depends on atomicProperties via getNr which drags in numpy again


def setting_options(func: Callable[..., Any]) -> Callable[..., Any]:
    # don't want to import .utils since this will drag in scipy, numpy etc
    atoms = ["C", "H", "O", "N", "P", "S"]

    @wraps(func)
    @click.option(
        "-e",
        "--element",
        "labelledElement",
        type=click.Choice(atoms),
        help="heavy isotope",
    )
    @click.option(
        "--nr",
        "labelledIsotopeNumber",
        type=click.IntRange(1),
        help="isotope number",
    )
    @click.option(
        "--mztol",
        "mzTolerance",
        type=click.FloatRange(0.0),
        help="m/z tolerance in ppm",
    )
    @click.option(
        "--rttol",
        "rtTolerance",
        type=click.FloatRange(0.0),
        help="retention time tolerance in seconds",
    )
    def distill_settings(
        labelledElement: str,
        labelledIsotopeNumber: int,
        mzTolerance: float,
        rtTolerance: float,
        **kwargs: Any,
    ) -> Any:
        # this function is being called so we can import now
        from .utils import PeptideSettings, getNr, okNr

        settings = PeptideSettings()

        def rep(**kwargs: Any) -> PeptideSettings:
            return replace(settings, **kwargs)

        if labelledElement is not None:
            settings = rep(
                labelledElement=labelledElement,
                labelledIsotopeNumber=getNr(labelledElement),
            )
        if labelledIsotopeNumber is not None:
            if labelledIsotopeNumber not in okNr(settings.labelledElement):
                raise click.BadParameter(
                    f"unknown isotope {settings.labelledElement}[{labelledIsotopeNumber}]",
                    param_hint="labelledIsotopeNumber",
                )

            settings = rep(
                labelledIsotopeNumber=labelledIsotopeNumber,
            )
        if mzTolerance is not None:
            settings = rep(mzTolerance=mzTolerance / 1.0e6)

        if rtTolerance is not None:
            settings = rep(rtTolerance=rtTolerance)

        kwargs["settings"] = settings
        return func(**kwargs)

    return distill_settings
