from typing import overload

from .typing import Number


class TetrisMetricsBaseWithPPS:
    def __init__(self, pps: Number, precision: int) -> None:
        self._pps = pps
        self.precision = precision

    @property
    def _lpm(self) -> Number:
        return self._pps * 24

    @property
    def lpm(self) -> Number:
        return round(self._lpm, self.precision)

    @property
    def pps(self) -> Number:
        return round(self._pps, self.precision)


class TetrisMetricsBaseWithLPM:
    def __init__(self, lpm: Number, precision: int) -> None:
        self._lpm = lpm
        self.precision = precision

    @property
    def lpm(self) -> Number:
        return round(self._lpm, self.precision)

    @property
    def _pps(self) -> Number:
        return self._lpm / 24

    @property
    def pps(self) -> Number:
        return round(self._pps, self.precision)


class TetrisMetricsBaseWithVS:
    def __init__(self, vs: Number, precision: int) -> None:
        self._vs = vs
        self.precision = precision

    @property
    def vs(self) -> Number:
        return round(self._vs, self.precision)

    @property
    def _adpm(self) -> Number:
        return self._vs * 0.6

    @property
    def adpm(self) -> Number:
        return round(self._adpm, self.precision)


class TetrisMetricsBaseWithADPM:
    def __init__(self, adpm: Number, precision: int) -> None:
        self._adpm = adpm
        self.precision = precision

    @property
    def _vs(self) -> Number:
        return self._adpm / 0.6

    @property
    def vs(self) -> Number:
        return round(self._vs, self.precision)

    @property
    def adpm(self) -> Number:
        return round(self._adpm, self.precision)


class TetrisMetricsBasicWithPPS(TetrisMetricsBaseWithPPS):
    def __init__(self, pps: Number, apm: Number, precision: int) -> None:
        super().__init__(pps=pps, precision=precision)
        self._apm = apm

    @property
    def apm(self) -> Number:
        return round(self._apm, self.precision)

    @property
    def apl(self) -> Number:
        return round(self._apm / self._lpm, self.precision)


class TetrisMetricsBasicWithLPM(TetrisMetricsBaseWithLPM):
    def __init__(self, lpm: Number, apm: Number, precision: int):
        super().__init__(lpm=lpm, precision=precision)
        self._apm = apm

    @property
    def apm(self) -> Number:
        return round(self._apm, self.precision)

    @property
    def apl(self) -> Number:
        return round(self._apm / self._lpm, self.precision)


class TetrisMetricsProWithPPSVS(TetrisMetricsBasicWithPPS, TetrisMetricsBaseWithVS):
    def __init__(self, pps: Number, apm: Number, vs: Number, precision: int) -> None:
        super().__init__(pps=pps, apm=apm, precision=precision)
        super(TetrisMetricsBaseWithPPS, self).__init__(vs=vs, precision=precision)

    @property
    def adpl(self) -> Number:
        return round(self._adpm / self._lpm, self.precision)


class TetrisMetricsProWithPPSADPM(TetrisMetricsBasicWithPPS, TetrisMetricsBaseWithADPM):
    def __init__(self, pps: Number, apm: Number, adpm: Number, precision: int) -> None:
        super().__init__(pps=pps, apm=apm, precision=precision)
        super(TetrisMetricsBaseWithPPS, self).__init__(adpm=adpm, precision=precision)

    @property
    def adpl(self) -> Number:
        return round(self._adpm / self._lpm, self.precision)


class TetrisMetricsProWithLPMVS(TetrisMetricsBasicWithLPM, TetrisMetricsBaseWithVS):
    def __init__(self, lpm: Number, apm: Number, vs: Number, precision: int) -> None:
        super().__init__(lpm=lpm, apm=apm, precision=precision)
        super(TetrisMetricsBaseWithLPM, self).__init__(vs=vs, precision=precision)

    @property
    def adpl(self) -> Number:
        return round(self._adpm / self._lpm, self.precision)


class TetrisMetricsProWithLPMADPM(TetrisMetricsBasicWithLPM, TetrisMetricsBaseWithADPM):
    def __init__(self, lpm: Number, apm: Number, adpm: Number, precision: int) -> None:
        super().__init__(lpm=lpm, apm=apm, precision=precision)
        super(TetrisMetricsBaseWithLPM, self).__init__(adpm=adpm, precision=precision)

    @property
    def adpl(self) -> Number:
        return round(self._adpm / self._lpm, self.precision)


@overload
def get_metrics(
    *,
    pps: Number,
    lpm: None = None,
    apm: None = None,
    vs: None = None,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsBaseWithPPS:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: Number,
    apm: None = None,
    vs: None = None,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsBaseWithLPM:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: None = None,
    apm: None = None,
    vs: Number,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsBaseWithVS:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: None = None,
    apm: None = None,
    vs: None = None,
    adpm: Number,
    precision: int = 2,
) -> TetrisMetricsBaseWithADPM:
    ...


@overload
def get_metrics(
    *,
    pps: Number,
    lpm: None = None,
    apm: Number,
    vs: None = None,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsBasicWithPPS:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: Number,
    apm: Number,
    vs: None = None,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsBasicWithLPM:
    ...


@overload
def get_metrics(
    *,
    pps: Number,
    lpm: None = None,
    apm: Number,
    vs: Number,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsProWithPPSVS:
    ...


@overload
def get_metrics(
    *,
    pps: Number,
    lpm: None = None,
    apm: Number,
    vs: None = None,
    adpm: Number,
    precision: int = 2,
) -> TetrisMetricsProWithPPSADPM:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: Number,
    apm: Number,
    vs: Number,
    adpm: None = None,
    precision: int = 2,
) -> TetrisMetricsProWithLPMVS:
    ...


@overload
def get_metrics(
    *,
    pps: None = None,
    lpm: Number,
    apm: Number,
    vs: None = None,
    adpm: Number,
    precision: int = 2,
) -> TetrisMetricsProWithLPMADPM:
    ...


def get_metrics(  # noqa: PLR0911, PLR0912, PLR0913
    *,
    pps: Number | None = None,
    lpm: Number | None = None,
    apm: Number | None = None,
    vs: Number | None = None,
    adpm: Number | None = None,
    precision: int = 2,
) -> (
    TetrisMetricsBaseWithPPS
    | TetrisMetricsBaseWithLPM
    | TetrisMetricsBaseWithVS
    | TetrisMetricsBaseWithADPM
    | TetrisMetricsBasicWithPPS
    | TetrisMetricsBasicWithLPM
    | TetrisMetricsProWithPPSVS
    | TetrisMetricsProWithLPMVS
    | TetrisMetricsProWithPPSADPM
    | TetrisMetricsProWithLPMADPM
):
    if apm is None:
        if pps is not None:
            return TetrisMetricsBaseWithPPS(pps, precision=precision)
        if lpm is not None:
            return TetrisMetricsBaseWithLPM(lpm, precision=precision)
        if vs is not None:
            return TetrisMetricsBaseWithVS(vs, precision=precision)
        if adpm is not None:
            return TetrisMetricsBaseWithADPM(adpm, precision=precision)
    elif vs is None and adpm is None:
        if pps is not None:
            return TetrisMetricsBasicWithPPS(pps, apm, precision=precision)
        if lpm is not None:
            return TetrisMetricsBasicWithLPM(lpm, apm, precision=precision)
    else:
        if vs is not None:
            if pps is not None:
                return TetrisMetricsProWithPPSVS(pps, apm, vs, precision=precision)
            if lpm is not None:
                return TetrisMetricsProWithLPMVS(lpm, apm, vs, precision=precision)
        if adpm is not None:
            if pps is not None:
                return TetrisMetricsProWithPPSADPM(pps, apm, adpm, precision=precision)
            if lpm is not None:
                return TetrisMetricsProWithLPMADPM(lpm, apm, adpm, precision=precision)

    raise TypeError
