"""This module declareds the pydantic schema representation for devices."""

from datetime import datetime
from abc import ABC
from decimal import Decimal

from pydantic import BaseModel, validator

from backend.models.records import AvpuScore, RespirationScore


class AbstractRecordCreate(BaseModel, ABC):
    """Base class containing fields common to all vitals records during creation."""

    measured: datetime

    @validator('measured')
    def assert_measured_is_valid(cls, measured: datetime) -> datetime:
        if measured >= datetime.now(tz=measured.tzinfo):
            raise ValueError("Time of measurement cannot be in the future.")
        return measured

class AbstractRecord(BaseModel, ABC):
    """Base class containing fields common to all vitals records for display."""

    id: int
    measured: datetime
    device_id: int

    @validator('measured')
    def assert_measured_is_valid(cls, measured: datetime) -> datetime:
        if measured >= datetime.now(tz=measured.tzinfo):
            raise ValueError("Time of measurement cannot be in the future.")
        return measured

    class Config:
        orm_mode = True


class AbstractHeartRateRecord(BaseModel, ABC):
    value: int

    @validator('value')
    def assert_value_is_valid(cls, value):
        if value < 0:
            raise ValueError("Value cannot be negative.")
        if value >= 32767:
            raise ValueError("Value is too large.")
        return value

class HeartRateRecordCreate(AbstractRecordCreate, AbstractHeartRateRecord):
    pass

class HeartRateRecord(AbstractRecord, AbstractHeartRateRecord):
    pass


class AbstractAvpuScoreRecord(BaseModel, ABC):
    value: AvpuScore

class AvpuScoreRecordCreate(AbstractRecordCreate, AbstractAvpuScoreRecord):
    pass

class AvpuScoreRecord(AbstractRecord, AbstractAvpuScoreRecord):
    pass


class AbstractBodyTemperatureRecord(BaseModel, ABC):
    value: Decimal

    @validator('value')
    def assert_value_is_valid(cls, value: Decimal) -> Decimal:
        if value < 0:
            raise ValueError("Value cannot be negative.")
        if value >= 100:
            raise ValueError("Value cannot exceed '99.99'.")
        if len(value.as_tuple().digits) > 4:
            raise ValueError("Value can have at most two digits after the decimal point.")
        return value

class BodyTemperatureRecordCreate(AbstractRecordCreate, AbstractBodyTemperatureRecord):
    pass

class BodyTemperatureRecord(AbstractRecord, AbstractBodyTemperatureRecord):
    pass


class AbstractBloodPressureRecord(BaseModel, ABC):
    value_systolic: int
    value_diastolic: int

    @validator('value_systolic')
    def assert_value_systolic_is_valid(cls, value_systolic):
        if value_systolic < 0:
            raise ValueError("Value (systolic) cannot be negative.")
        if value_systolic >= 32767:
            raise ValueError("Value (systolic) is too large.")
        return value_systolic

    @validator('value_diastolic')
    def assert_value_diastolic_is_valid(cls, value_diastolic):
        if value_diastolic < 0:
            raise ValueError("Value (diastolic) cannot be negative.")
        if value_diastolic >= 32767:
            raise ValueError("Value (diastolic) is too large.")
        return value_diastolic

class BloodPressureRecordCreate(AbstractRecordCreate, AbstractBloodPressureRecord):
    pass

class BloodPressureRecord(AbstractRecord, AbstractBloodPressureRecord):
    pass


class AbstractBloodOxygenRecord(BaseModel, ABC):
    value: Decimal

    @validator('value')
    def assert_value_is_valid(cls, value: Decimal) -> Decimal:
        if value < 0:
            raise ValueError("Value cannot be negative.")
        if value > 100:
            raise ValueError("Value cannot exceed '100.00'.")
        if len(value.as_tuple().digits) > 5:
            raise ValueError("Value can have at most two digits after the decimal point.")
        return value

class BloodOxygenRecordCreate(AbstractRecordCreate, AbstractBloodOxygenRecord):
    pass

class BloodOxygenRecord(AbstractRecord, AbstractBloodOxygenRecord):
    pass


class AbstractRespirationScoreScoreRecord(BaseModel, ABC):
    value: RespirationScore

class RespirationScoreRecordCreate(AbstractRecordCreate, AbstractRespirationScoreScoreRecord):
    pass

class RespirationScoreRecord(AbstractRecord, AbstractRespirationScoreScoreRecord):
    pass
