from datetime import date, datetime
from abc import ABC

from django.core.exceptions import ValidationError


class DateValidator:
    @staticmethod
    def past_date(value: date, name: str = "date"):
        if value > date.today():
            raise ValidationError(f"The {name} cannot be in the future.")

    @staticmethod
    def past_datetime(value: datetime, name: str = "timestamp"):
        if value > datetime.now():
            raise ValidationError(f"The {name} cannot be in the future.")


class PersonValidator:
    @staticmethod
    def date_of_birth(value):
        DateValidator.past_date(value, "date of birth")


class AbstractRecordValidator(ABC):
    @staticmethod
    def recorded(value):
        DateValidator.past_datetime(value, "time when the value was recorded")


class BloodPressureRecordValidator(AbstractRecordValidator):
    MIN_VALUE_SYSTOLIC_MMHG = 0
    MAX_VALUE_SYSTOLIC_MMHG = 1000
    MIN_VALUE_DIASTOLIC_MMHG = 0
    MAX_VALUE_DIASTOLIC_MMHG = 1000

    @staticmethod
    def value_systolic_mmhg(value: int):
        if value < BloodPressureRecordValidator.MIN_VALUE_SYSTOLIC_MMHG:
            raise ValidationError(f"Systolic Blood Pressure cannot be below {BloodPressureRecordValidator.MIN_VALUE_SYSTOLIC_MMHG}")
        if value > BloodPressureRecordValidator.MAX_VALUE_SYSTOLIC_MMHG:
            raise ValidationError(f"Systolic Blood Pressure cannot be above {BloodPressureRecordValidator.MAX_VALUE_SYSTOLIC_MMHG}")

    @staticmethod
    def value_diastolic_mmhg(value: int):
        if value < BloodPressureRecordValidator.MIN_VALUE_DIASTOLIC_MMHG:
            raise ValidationError(f"Diastolic Blood Pressure cannot be below {BloodPressureRecordValidator.MIN_VALUE_DIASTOLIC_MMHG}")
        if value > BloodPressureRecordValidator.MAX_VALUE_DIASTOLIC_MMHG:
            raise ValidationError(f"Diastolic Blood Pressure cannot be above {BloodPressureRecordValidator.MAX_VALUE_DIASTOLIC_MMHG}")


class BodyTempRecordValidator(AbstractRecordValidator):
    MIN_VALUE_CELSIUS = 0
    MAX_VALUE_CELSIUS = 100

    @staticmethod
    def value_celsius(value: int):
        if value < BodyTempRecordValidator.MIN_VALUE_CELSIUS:
            raise ValidationError(f"Body Temperature cannot be below {BodyTempRecordValidator.MIN_VALUE_CELSIUS}")
        if value > BodyTempRecordValidator.MAX_VALUE_CELSIUS:
            raise ValidationError(f"Body Temperature cannot be above {BodyTempRecordValidator.MAX_VALUE_CELSIUS}")


class HeartRateRecordValidator(AbstractRecordValidator):
    MIN_VALUE_BPM = 0
    MAX_VALUE_BPM = 1000

    @staticmethod
    def value_bpm(value: int):
        if value < HeartRateRecordValidator.MIN_VALUE_BPM:
            raise ValidationError(f"Heart Rate cannot be below {HeartRateRecordValidator.MIN_VALUE_BPM}")
        if value > HeartRateRecordValidator.MAX_VALUE_BPM:
            raise ValidationError(f"Heart Rate cannot be above {HeartRateRecordValidator.MAX_VALUE_BPM}")


class RespirationScoreRecordValidator(AbstractRecordValidator):
    MIN_VALUE_SEVERITY = 0
    MAX_VALUE_SEVERITY = 2

    @staticmethod
    def value_severity(value: int):
        if value < RespirationScoreRecordValidator.MIN_VALUE_SEVERITY:
            raise ValidationError(f"Respiratory Inhibition Severity cannot be below {RespirationScoreRecordValidator.MIN_VALUE_SEVERITY}")
        if value > RespirationScoreRecordValidator.MAX_VALUE_SEVERITY:
            raise ValidationError(f"Respiratory Inhibition Severity cannot be above {RespirationScoreRecordValidator.MAX_VALUE_SEVERITY}")


class Spo2LevelRecordValidator(AbstractRecordValidator):
    MIN_VALUE_PERCENT = 0
    MAX_VALUE_PERCENT = 100

    @staticmethod
    def value_percent(value: int):
        if value < Spo2LevelRecordValidator.MIN_VALUE_PERCENT:
            raise ValidationError(f"SPO2 cannot be below {Spo2LevelRecordValidator.MIN_VALUE_PERCENT}")
        if value > Spo2LevelRecordValidator.MAX_VALUE_PERCENT:
            raise ValidationError(f"SPO2 cannot be above {Spo2LevelRecordValidator.MAX_VALUE_PERCENT}")


class MewsRecordValidator(AbstractRecordValidator):
    MIN_VALUE_N = 0
    MAX_VALUE_N = 100

    @staticmethod
    def value_n(value: int):
        if value < MewsRecordValidator.MIN_VALUE_N:
            raise ValidationError(f"MEWS cannot be below {MewsRecordValidator.MIN_VALUE_N}")
        if value > MewsRecordValidator.MAX_VALUE_N:
            raise ValidationError(f"MEWS cannot be above {MewsRecordValidator.MAX_VALUE_N}")
