from datetime import date
from pydantic import BaseModel, validator, constr, conlist, root_validator

from intranet.trip.src.config import settings
from intranet.trip.src.api.schemas.common import RoutePoint, RoutePointCreate
from intranet.trip.src.api.schemas.person import Person
from intranet.trip.src.api.schemas.purpose import Purpose
from intranet.trip.src.api.schemas.service import Service, AviaService, RailService, HotelService
from intranet.trip.src.lib.utils import unify_values
from intranet.trip.src.models.person_trip import PersonTripActions

from intranet.trip.src.enums import (
    ServiceStatus,
    Provider,
    PTStatus,
    CombinedStatus,
    TripDaysOffCompensations,
    ConferenceParticiationType,
)


class TravelDetailsBase(BaseModel):
    is_created_on_provider: bool
    need_visa_assistance: bool


class TravelDetailsLite(BaseModel):
    tracker_issue: str = None


class TravelDetails(TravelDetailsBase):
    trip_id: int
    person_id: int
    tracker_issue: str = None
    is_created_on_provider: bool
    need_visa_assistance: bool
    taxi_date_from: date = None
    taxi_date_to: date = None
    is_taxi_activated: bool
    is_drive_activated: bool
    taxi_access_phone: str = None

    comment: constr(max_length=256) = None

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


class TravelDetailsUpdate(TravelDetailsBase):
    city_id: int = None
    comment: str = None

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


class PersonConfDetailsBase(BaseModel):
    role: ConferenceParticiationType
    presentation_topic: constr(max_length=128) = None
    is_hr_approved: bool = False
    is_paid_by_host: bool = False
    price: constr(max_length=256) = None
    promo_code: constr(max_length=256) = None
    discount: constr(max_length=256) = None
    badge_position: constr(max_length=128) = None
    badge_name: constr(max_length=128) = None
    comment: constr(max_length=256) = None
    is_another_city: bool = False

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


class PersonConfDetailsLite(BaseModel):
    role: ConferenceParticiationType
    tracker_issue: str = None


class PersonConfDetails(PersonConfDetailsBase):
    trip_id: int
    person_id: int
    tracker_issue: str = None


class PersonConfDetailsUpdate(PersonConfDetailsBase):
    price: constr(max_length=256)
    promo_code: constr(max_length=256)
    discount: constr(max_length=256)


class PersonConfDetailsCreateInTrip(PersonConfDetailsBase):
    pass


class PersonTripBase(BaseModel):
    description: str
    gap_date_from: date = None
    gap_date_to: date = None
    with_days_off: bool
    compensation_type: TripDaysOffCompensations = None
    aeroclub_trip_id: int = None
    aeroclub_journey_id: int = None
    provider_trip_id: int = None
    provider_journey_id: int = None

    @root_validator(pre=True)
    def set_provider_ids(cls, values):
        values = unify_values(values, ['provider_trip_id', 'aeroclub_trip_id'])
        values = unify_values(values, ['provider_journey_id', 'aeroclub_journey_id'])
        return values


class PersonTripLite(PersonTripBase):
    """Персональная командировка для списковых ручек"""
    person: Person = None
    provider: Provider
    status: PTStatus
    is_approved: bool
    combined_status: CombinedStatus = CombinedStatus.under_approval
    is_hidden: bool
    is_offline: bool
    travel_details: TravelDetailsLite = None
    conf_details: PersonConfDetailsLite = None
    actions: PersonTripActions = None
    is_authorized: bool = None
    aeroclub_url: str = None

    need_warning_on_cancel: bool = False
    route: list[RoutePoint] = None

    @validator('need_warning_on_cancel', always=True)
    def calculate_need_warning_on_cancel(cls, v, values):
        status = values.get('status')
        return status in (PTStatus.executed, PTStatus.executing)

    @validator('combined_status', always=True)
    def calculate_combined_status(cls, v, values):
        status = values.get('status')
        is_approved = values.get('is_approved')

        if status == PTStatus.draft:
            return CombinedStatus.draft
        elif status == PTStatus.executed:
            return CombinedStatus.executed
        elif status == PTStatus.cancelled:
            return CombinedStatus.cancelled
        elif status == PTStatus.closed:
            return CombinedStatus.closed
        elif is_approved:
            if status == PTStatus.new:
                return CombinedStatus.approved
            elif status == PTStatus.verification:
                return CombinedStatus.verification
            elif status == PTStatus.executing:
                return CombinedStatus.executing
        else:
            return CombinedStatus.under_approval

    @validator('aeroclub_url', always=True)
    def generate_aeroclub_url(cls, v, values):
        trip_id = values.get('aeroclub_trip_id')
        journey_id = values.get('aeroclub_journey_id')
        if journey_id and trip_id:
            return f'{settings.trip_base_url}/api/aeroclub/journeys/{journey_id}/trips/{trip_id}'
        return v


class PersonTrip(PersonTripLite):
    """Персональная командировка для ручек детализации"""
    purposes: list[Purpose]
    travel_details: TravelDetails = None
    conf_details: PersonConfDetails = None
    documents: list[int]
    services: list[Service]
    need_visa: bool = False
    chat_id: str = None

    @validator('documents', pre=True, always=True)
    def get_documents(cls, v):
        return [i['document_id'] for i in v] if v else []

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

    @validator('services', pre=True, always=True)
    def get_services(cls, v):
        if not v:
            return []
        return [s for s in v if s['status'] != ServiceStatus.deleted]


class PersonTripUpdateBase(BaseModel):
    description: str = None
    is_hidden: bool = False
    with_days_off: bool = False
    compensation_type: TripDaysOffCompensations = None
    need_visa: bool = False
    city_from: constr(max_length=128) = ''
    country_from: constr(max_length=128) = ''
    gap_date_from: date = None
    gap_date_to: date = None


class PersonTripUpdate(PersonTripUpdateBase):
    """
    Используется для редактирования в ручке PUT
    """
    purposes: list[int]
    travel_details: TravelDetailsUpdate = None
    conf_details: PersonConfDetailsUpdate = None
    documents: list[int]
    route: conlist(RoutePointCreate, min_items=2) = None


class PersonTripPartialUpdate(PersonTripUpdateBase):
    """
    Используется для редактирования в ручке PATCH
    """
    purposes: list[int] = None
    travel_details: TravelDetailsUpdate = None
    conf_details: PersonConfDetailsUpdate = None
    documents: list[int] = None
    route: conlist(RoutePointCreate, min_items=2) = None


class PersonTripCreateInTrip(PersonTripUpdateBase):
    """
    Используется при создании Trip, чтобы сразу передать поля, специфичные для каждого PersonTrip
    Остальные поля будут заполнены на основе самого Trip
    """
    person_uid: str
    conf_details: PersonConfDetailsCreateInTrip = None


class PersonTripTrackerNotification(BaseModel):
    issue_key: str
    manager_login: str = None
    login: str = None
    staff_trip_uuid: str = None


class PersonTripCancel(BaseModel):
    cancellation_reason: constr(strip_whitespace=True, min_length=3)


class ManagerChatId(BaseModel):
    manager_chat_id: str


class ChatId(BaseModel):
    chat_id: str


class PersonTripServices(BaseModel):
    services: list[Service]
    avia_services: list[AviaService]
    rail_services: list[RailService]
    hotel_services: list[HotelService]
