import math
from typing import Any

from reil.subjects.healthcare import Patient
from reil.subjects.healthcare.mathematical_models import HealthMathModel
from reil.datatypes import Feature
from reil.utils.functions import (random_categorical, random_truncated_lnorm,
                                  random_truncated_normal)


class WarfarinPatientRavvaz(Patient):
    feature_set = {
        'age': Feature.numerical(  # Aurora population
            lower=18.0, upper=100.0, mean=67.30, stdev=13.43,
            generator=random_truncated_normal),
        'weight': Feature.numerical(  # lb  - Aurora population
            lower=70.0, upper=500.0, mean=199.24, stdev=54.71,
            generator=random_truncated_normal),
        'height': Feature.numerical(  # in - Aurora population
            lower=45.0, upper=85.0, mean=66.78, stdev=4.31,
            generator=random_truncated_normal),
        'gender': Feature.categorical(  # Aurora population
            categories=('Female', 'Male'),
            probabilities=(0.5314, 0.4686),
            generator=random_categorical),
        'race': Feature.categorical(  # Aurora Avatar Population
            categories=('White', 'Black', 'Asian',
                        'American Indian', 'Pacific Islander'),
            probabilities=(0.9522, 0.0419, 0.0040, 0.0018, 1e-4),
            generator=random_categorical),
        'tobaco': Feature.categorical(  # Aurora Avatar Population
            categories=('No', 'Yes'),
            probabilities=(0.9067, 0.0933),
            generator=random_categorical),
        'amiodarone': Feature.categorical(  # Aurora Avatar Population
            categories=('No', 'Yes'),
            probabilities=(0.8849, 0.1151),
            generator=random_categorical),
        'fluvastatin': Feature.categorical(  # Aurora Avatar Population
            categories=('No', 'Yes'),
            probabilities=(0.9998, 0.0002),
            generator=random_categorical),
        'CYP2C9': Feature.categorical(  # Aurora Avatar Population
            categories=('*1/*1', '*1/*2', '*1/*3', '*2/*2', '*2/*3', '*3/*3'),
            probabilities=(0.6739, 0.1486, 0.0925, 0.0651, 0.0197, 2e-4),
            generator=random_categorical),
        'VKORC1': Feature.categorical(  # Aurora Avatar Population
            categories=('G/G', 'G/A', 'A/A'),
            probabilities=(0.3837, 0.4418, 0.1745),
            generator=random_categorical),

        'MTT_1': Feature.numerical(  # Hamberg PK/PD
            value=math.exp(math.log(11.6) + 0.141/2), mean=math.log(11.6), stdev=math.sqrt(0.141),
            generator=random_truncated_lnorm),
        'MTT_2': Feature.numerical(  # Hamberg PK/PD
            value=math.exp(math.log(120.0) + 1.02/2), mean=math.log(120.0), stdev=math.sqrt(1.02),
            generator=random_truncated_lnorm),
        'cyp_1_1': Feature.numerical(  # Hamberg PK/PD
            value=math.exp(math.log(0.314) + 0.31/2), mean=math.log(0.314), stdev=math.sqrt(0.31),
            generator=random_truncated_lnorm),
        'V1': Feature.numerical(  # Hamberg PK/PD
            value=math.exp(math.log(13.8) + 0.262/2), mean=math.log(13.8), stdev=math.sqrt(0.262),
            generator=random_truncated_lnorm),
        'V2': Feature.numerical(  # Hamberg PK/PD
            value=math.exp(math.log(6.59) + 0.991/2), mean=math.log(6.59), stdev=math.sqrt(0.991),
            generator=random_truncated_lnorm),

        'EC_50': Feature.numerical(  # Hamberg PK/PD
            stdev=math.sqrt(0.409),
            generator=random_truncated_lnorm)
    }

    def __init__(self, model: HealthMathModel, randomized: bool = True, **feature_values: Any) -> None:
        for f in self.feature_set.values():
            f.randomized = randomized

        # Since EC_50 is not set (it depends on other features), super().__init__()
        # fails to setup the model. I catch it, generate EC_50 and set up the model.
        try:
            super().__init__(model, EC_50=None, **feature_values)
        except TypeError:
            self._generate_EC_50()
            self._model.setup(**self.feature_set)

    def _generate_EC_50(self) -> None:
        if self.feature_set['VKORC1'].value == 'G/G':
            self.feature_set['EC_50'].value = math.log(4.61) + 0.409/2
            self.feature_set['EC_50'].mean = math.log(4.61)
        elif self.feature_set['VKORC1'].value in ['G/A', 'A/G']:
            self.feature_set['EC_50'].value = math.log(3.02) + 0.409/2
            self.feature_set['EC_50'].mean = math.log(3.02)
        elif self.feature_set['VKORC1'].value == 'A/A':
            self.feature_set['EC_50'].value = math.log(2.20) + 0.409/2
            self.feature_set['EC_50'].mean = math.log(2.20)

        self.feature_set['EC_50'].generate()

    def generate(self) -> None:
        super().generate()
        self._generate_EC_50()
        self._model.setup(**self.feature_set)
