import datetime

from decimal import Decimal
from pydantic import BaseModel, validator

from intranet.trip.src.enums import (
    TripStatus,
    PTStatus,
    PurposeKind,
    DocumentType,
    Gender,
    CurrencyType,
    TripDaysOffCompensations,
    TripType,
    Citizenship,
    ConferenceParticiationType,
)
from intranet.trip.src.models.common import RoutePoint


class Person(BaseModel):
    person_id: int
    staff_id: int = None
    company_id: int = None
    uid: str
    external_uid: str = None
    gender: Gender
    date_of_birth: datetime.date = None
    phone_number: str = None
    login: str = None

    first_name: str
    last_name: str
    middle_name: str = None
    first_name_en: str = None
    last_name_en: str = None
    middle_name_en: str = None

    @validator('first_name_en', 'last_name_en', pre=True, always=True)
    def stub_en_names(cls, v):
        """Пока не все поля есть возможность заполнить, проставляю в модели дефолты"""
        return v or ''

    @validator('gender', pre=True, always=True)
    def stub_gender(cls, v):
        """Пока не все поля есть возможность заполнить, проставляю в модели дефолты"""
        return v or Gender.female


class Document(BaseModel):  # person_document
    document_id: int
    document_type: DocumentType
    series: str
    number: str
    issued_on: datetime.date = None
    expires_on: datetime.date
    citizenship: Citizenship = Citizenship.RU
    first_name: str
    last_name: str
    middle_name: str = None

    @validator('issued_on', pre=True, always=True)
    def stub_issued_on_field(cls, v):
        """Пока не все поля есть возможность заполнить, проставляю в модели дефолты"""
        if v is None:
            return datetime.date(year=1988, month=1, day=1)
        return v


class Purpose(BaseModel):  # list_purpose
    purpose_id: int
    name: str
    name_en: str
    kind: PurposeKind
    aeroclub_grade: int


class City(BaseModel):  # list_city
    # city_id: int
    code: str
    name: str = ''


class AviaService(BaseModel):
    # service_id: int
    ordering: int
    has_tickets: bool

    airline_id: int  # todo
    aircraft_id: int  # todo
    flight_number: str

    departure_at: datetime.datetime
    arrival_at: datetime.datetime

    departure_airport_id: int  # todo
    arrival_airport_id: int  # todo

    seat: str
    boarding_pass: str

    price: Decimal = Decimal(0.0)
    currency: CurrencyType
    overpaid: bool = False


class RailwayService(BaseModel):
    # service_id: int
    ordering: int
    has_tickets: bool

    departure_station_id: int  # todo
    arrival_station_id: int  # todo

    train_number: str
    boarding_pass: str

    price: Decimal = Decimal(0.0)
    currency: CurrencyType
    overpaid: bool = False


class HotelService(BaseModel):
    # service_id: int
    ordering: int

    has_hotel: bool
    boarding_pass: str
    overpaid: bool = False
    comment: str = None

    @validator('comment', pre=True, always=True)
    def get_nullable_str_value(cls, v):
        return v or ''


class TripConfDetails(BaseModel):  # trip_conf_details
    # trip_id: int
    tracker_issue: str = None

    conf_date_from: datetime.date
    conf_date_to: datetime.date

    conference_url: str = None
    program_url: str = None
    conference_name: str = None
    price: str = None
    promo_code: str = None
    ticket_type: str = None

    participation_terms: str = None
    cancellation_terms: str = None


class TravelDetails(BaseModel):  # trip_travel_details
    # trip_id: int
    # person_id: int
    city: City = None
    tracker_issue: str = None
    is_created_on_provider: bool
    need_visa_assistance: bool

    taxi_date_from: datetime.date = None
    taxi_date_to: datetime.date = None
    is_taxi_activated: bool = False
    is_drive_activated: bool = False

    taxi_access_phone: str = None
    comment: str = None

    @validator('comment', pre=True, always=True)
    def get_nullable_str_value(cls, v):
        return v or ''


class ConfDetails(BaseModel):  # person_trip_conf_details
    # trip_id: int
    # person_id: int
    tracker_issue: str = None
    role: ConferenceParticiationType
    price: str = None
    promo_code: str = None
    discount: str = None

    badge_position: str = None
    badge_name: str = None

    comment: str = None

    @validator('comment', pre=True, always=True)
    def get_nullable_str_value(cls, v):
        return v or ''


class PersonTripFull(BaseModel):
    """Настройки командировки одного сотрудника"""
    # trip_id: int
    person: Person
    documents: list[Document] = None  # может быть только один
    purposes: list[Purpose]
    status: PTStatus

    travel_details: TravelDetails = None  # trip_travel_details
    conf_details: ConfDetails = None  # person_trip_conf_details

    avia_services: list[AviaService] = None
    railroad_services: list[RailwayService] = None
    hotel_services: list[HotelService] = None

    gap_date_from: datetime.date
    gap_date_to: datetime.date
    is_hidden: bool = False

    with_days_off: bool = False
    compensation_type: TripDaysOffCompensations = None

    need_visa: bool = False
    employee_assignment: str = None

    @validator(
        'purposes', 'avia_services', 'railroad_services', 'hotel_services',
        pre=True,
        always=True,
    )
    def get_nullable_list_value(cls, v):
        return v or []


class TripFull(BaseModel):
    """Модель для загрузки из базы полностью всего, что имеет отношение к одной командировке"""
    trip_id: int
    staff_trip_uuid: str = None

    date_from: datetime.date
    date_to: datetime.date
    status: TripStatus
    city_from: str = None
    city_to: str = None
    country_from: str = None
    country_to: str = None
    issue_travel: str = None
    issue_conf: str = None

    author: Person
    purposes: list[Purpose]
    person_trips: list[PersonTripFull]

    conf_details: TripConfDetails = None  # trip_conf_details
    comment: str

    route: list[RoutePoint] = None

    @validator('person_trips', 'purposes', pre=True, always=True)
    def get_list_fields(cls, v):
        return v or []

    @property
    def event_type(self) -> TripType:
        trip = bool(self.city_to) or bool(self.route)
        conf = bool(self.conf_details)
        assert trip or conf, 'Neither trip nor conf.'

        if trip and not conf:
            return TripType.trip
        if conf and not trip:
            return TripType.conf
        return TripType.trip_conf
