# -*- coding: utf-8 -*-
"""
Pydantic schemas for TOAR database

"""

from typing import List, Any

from pydantic import BaseModel, Json, validator, Field
import datetime as dt
from toardb.generic.models import RS_enum, RC_enum
from toardb.contacts.schemas import Contact
from .models import DA_enum, SF_enum, AT_enum, DS_enum, MM_enum
from toardb.variables.schemas import Variable 
#from toardb.stationmeta.schemas import StationmetaCoreCreate
from toardb.stationmeta.schemas import StationmetaCoreBase
#from toardb.stationmeta.schemas import StationmetaCore
#from toardb.stationmeta.schemas import StationmetaCreate
#from toardb.stationmeta.schemas import StationmetaBase
#from toardb.stationmeta.schemas import Stationmeta

# ======== Timeseries =========

class TimeseriesCoreBaseStub(BaseModel):
    id: int = None
    label: str = Field(..., description="a short string to distinguish this timeseries from others with the same combination of station and variable")
    order: int = Field(..., description="indicates position of this timeseries in a list when several timeseries share the same station and variable combination")
    access_rights: str = Field(..., description="Access rights of timeseries data")
    sampling_frequency: str = Field(..., description="Sampling frequency of data in this timeseries")
    aggregation: str = Field(..., description="Aggregation type in this timeseries")
    source: str
    data_start_date: dt.datetime = Field(..., description="Start date of the variable data available for this station")
    data_end_date: dt.datetime = Field(..., description="End date of the variable data available for this station")
    measurement_method: str = Field(..., description="instrument principle of measurement")
    sampling_height: float = Field(..., description="Height above the ground of the inlet/instrument/sampler (in m)")
    date_added: dt.datetime = Field(..., description="Date of timeseries metadata entry into TOAR database")
    date_modified: dt.datetime = Field(..., description="Date of last timeseries metadata modification")
    additional_metadata: Json = Field(..., description="Additional information about the timeseries as JSON structure.")

# still missing: "Score values from automated data QA (5-star evaluation)"
# still missing: "detailed report of timeseries QA/QC evaluation"
# still missing: "arbitrary additional information about this timeseries"

    @validator('access_rights')
    def check_access_rights(cls, v):
        return tuple(filter(lambda x: x.value == int(v), DA_enum))[0].string

    @validator('sampling_frequency')
    def check_sampling_frequency(cls, v):
        return tuple(filter(lambda x: x.value == int(v), SF_enum))[0].string

    @validator('aggregation')
    def check_aggregation(cls, v):
        return tuple(filter(lambda x: x.value == int(v), AT_enum))[0].string

    @validator('source')
    def check_source(cls, v):
        return tuple(filter(lambda x: x.value == int(v), DS_enum))[0].string

    @validator('measurement_method')
    def check_measurement_method(cls, v):
        return tuple(filter(lambda x: x.value == int(v), MM_enum))[0].string


class TimeseriesCoreBase(TimeseriesCoreBaseStub):
    station_id: int
    variable_id: int
    programme_id: int = None
    

class TimeseriesCoreCreate(TimeseriesCoreBase):
    pass

    @validator('access_rights')
    def check_access_rights(cls, v):
        if tuple(filter(lambda x: x.string == v, DA_enum)):
            return v
        else:
            raise ValueError(f"data access rights not known: {v}")

    @validator('sampling_frequency')
    def check_sampling_frequency(cls, v):
        if tuple(filter(lambda x: x.string == v, SF_enum)):
            return v
        else:
            raise ValueError(f"sampling frequency not known: {v}")

    @validator('aggregation')
    def check_aggregation(cls, v):
        if tuple(filter(lambda x: x.string == v, AT_enum)):
            return v
        else:
            raise ValueError(f"aggregation type not known: {v}")

    @validator('source')
    def check_source(cls, v):
        if tuple(filter(lambda x: x.string == v, DS_enum)):
            return v
        else:
            raise ValueError(f"data source not known: {v}")

    @validator('measurement_method')
    def check_measurement_method(cls, v):
        if tuple(filter(lambda x: x.string == v, MM_enum)):
            return v
        else:
            raise ValueError(f"measurement method not known: {v}")


class TimeseriesCore(TimeseriesCoreBase):
    id: int

    class Config:
        orm_mode = True

# ======== TimeseriesRole =========

class TimeseriesRoleBase(BaseModel):
    id: int = None
    role: str
    status: str
    contact: Contact

    @validator('role')
    def check_role(cls, v):
        return tuple(filter(lambda x: x.value == int(v), RC_enum))[0].string

    @validator('status')
    def check_status(cls, v):
        return tuple(filter(lambda x: x.value == int(v), RS_enum))[0].string


class TimeseriesRoleCreate(TimeseriesRoleBase):
    pass

    @validator('role')
    def check_role(cls, v):
        if tuple(filter(lambda x: x.string == v, RC_enum)):
            return v
        else:
            raise ValueError(f"role code not known: {v}")

    @validator('status')
    def check_status(cls, v):
        if tuple(filter(lambda x: x.string == v, RS_enum)):
            return v
        else:
            raise ValueError(f"role status not known: {v}")


class TimeseriesRole(TimeseriesRoleBase):
    id: int
    contact: Contact

    class Config:
        orm_mode = True

# ======== TimeseriesAnnotation =========

class TimeseriesAnnotationBase(BaseModel):
    id: int = None
    kind: int
    text: str
    date_added: dt.datetime
    approved: bool
    contributor_id: int
    timeseries_id: int


class TimeseriesAnnotationCreate(TimeseriesAnnotationBase):
    pass


class TimeseriesAnnotation(TimeseriesAnnotationBase):
    id: int

    class Config:
        orm_mode = True

# ======== TimeseriesProgramme =========

class TimeseriesProgrammeBase(BaseModel):
    id: int = None
    name: str
    longname: str
    homepage: str
    description: str


class TimeseriesProgrammeCreate(TimeseriesProgrammeBase):
    pass


class TimeseriesProgramme(TimeseriesProgrammeBase):
    id: int

    class Config:
        orm_mode = True


# ======== TimeseriesChangelog =========

class TimeseriesChangelogBase(BaseModel):
    id: int
    datetime: dt.datetime
    description: str
    old_value: str
    new_value: str
    timeseries_id: int
    author_id: int
    type_of_change: int
    period_start: dt.datetime = None
    period_end: dt.datetime = None
    version: str = None


class TimeseriesChangelog(TimeseriesChangelogBase):
    id: int

    class Config:
        orm_mode = True



# ======== for nested view/upload =========

class TimeseriesBase(TimeseriesCoreBaseStub):
    roles: List[TimeseriesRole] = None
    annotations: List[TimeseriesAnnotation] = None
    variable: Variable
#   station: StationmetaCoreCreate
#   station: StationmetaCore
#   station: StationmetaCreate
#   station: StationmetaBase
#   station: Stationmeta
# Try, which one of the above is wanted for station
# The next one works:
    station: StationmetaCoreBase
    programme: TimeseriesProgramme
    changelog: List[TimeseriesChangelog] = None

    class Config:
        orm_mode = True


class TimeseriesPatch(BaseModel):
    label: str = None
    order: int = None
    access_rights: str = None
    sampling_frequency: str = None
    aggregation: str = None
    source: str = None
    data_start_date: dt.datetime = None
    data_end_date: dt.datetime = None
    measurement_method: str = None
    sampling_height: float = None
    date_added: dt.datetime = None
    date_modified: dt.datetime = None
    additional_metadata: Json = None
    roles: List[TimeseriesRole] = None
    annotations: List[TimeseriesAnnotation] = None
    variable: Variable = None
    station: StationmetaCoreBase = None
    programme: TimeseriesProgramme = None

    class Config:
        orm_mode = True


class TimeseriesCreate(TimeseriesCoreCreate):
    roles: List[TimeseriesRoleCreate] = None
    annotations: List[TimeseriesAnnotation] = None

    class Config:
        orm_mode = True


class Timeseries(TimeseriesBase):
    id: int

    class Config:
        orm_mode = True