from __future__ import annotations

import abc
from typing import Any

import numpy as np

from ..lib.utils import ArrayLike


class Distribution(abc.ABC):
    name = 'Abstract distribution'
    parameter = None
    bounds = None

    @classmethod
    def __repr__(cls):
        return cls.name

    @staticmethod
    @abc.abstractmethod
    def distribution(tau, tau0, *args):
        pass

    @staticmethod
    @abc.abstractmethod
    def correlation(t, tau, *args):
        pass

    @staticmethod
    @abc.abstractmethod
    def susceptibility(omega: ArrayLike, tau: ArrayLike, *args: Any):
        pass

    @classmethod
    def specdens(cls, omega: ArrayLike, tau: ArrayLike, *args: Any) -> np.ndarray | float:
        return cls.susceptibility(omega, tau, *args).imag / omega

    @classmethod
    def mean_value(cls, *args, mode='mean'):
        if mode == 'mean':
            return cls.mean(*args)
        if mode == 'logmean':
            return np.exp(cls.logmean(*args))
        if mode in ['max', 'peak']:
            return cls.max(*args)

        return args[0]

    @staticmethod
    def mean(*args):
        return args[0]

    @staticmethod
    def logmean(*args):
        """
        Return mean logarithmic tau

        Args:
            *args:

        Returns:

        """
        return np.log(args[0])

    @staticmethod
    def max(*args):
        return args[0]

    @classmethod
    def convert(cls, *args, from_='raw', to_='mean'):
        if from_ == to_:
            return args[0]

        if from_ == 'raw':
            return cls.mean_value(*args, mode=to_)
        else:
            # makes only sense as long as mean/peak is given by <x> = factor * x
            factor = cls.mean_value(1, *args[1:], mode=from_)
            try:
                raw = args[0] / factor
            except ZeroDivisionError:
                raise ZeroDivisionError('Conversion between mean values impossible')

            if to_ == 'raw':
                return raw
            else:
                return cls.mean_value(raw, *args[1:], mode=to_)
