import logging
from datetime import date
from typing import Union, Any, Optional

from intranet.trip.src.enums import (
    TripStatus,
    PTStatus,
    Provider,
    TripDaysOffCompensations,
    TripType,
    ConferenceParticiationType,
)
from intranet.trip.src.lib.utils import safe_getitem
from intranet.trip.src.models import (
    Trip,
    PersonTrip,
    Person,
)
from intranet.trip.src.models.full import TripFull, PersonTripFull, Purpose


logger = logging.getLogger(__name__)

# TODO: Нужны остальные статусы
TRACKER_TRIP_STATUS_MAPPING = {
    'open': TripStatus.new,
    'closed': TripStatus.closed,
    'recieved': TripStatus.closed,
    'cancelled': TripStatus.cancelled,
}


TRACKER_RESOLUTION_TRIP_STATUS_MAPPING = {
    "fixed": TripStatus.closed,
    "won'tFix": TripStatus.cancelled,
}


TRACKER_PT_STATUS_MAPPING = {
    'open': PTStatus.new,
    'closed': PTStatus.closed,
    'cancelled': PTStatus.cancelled,
    'recieved': PTStatus.closed,
}


TRACKER_RESOLUTION_PT_STATUS_MAPPING = {
    "fixed": PTStatus.closed,
    "won'tFix": PTStatus.cancelled,
}


class StaffToTripConverter:
    """
    Конвертирует командировку Стаффа в модель Trip
    """
    def convert(
            self,
            staff_trips: list[dict],
            persons_map: dict[int, Person],
    ) -> list[Trip]:
        self.persons_map = persons_map
        for person_instance in self.persons_map.values():
            person_instance.person_id = person_instance.person_id or 0

        trips = [
            self._get_trip(item)
            for item in staff_trips
            if self._get_event_type(item) in ('trip', 'trip_conf')
        ]
        return trips

    def _get_event_type(self, item: dict) -> str:
        return item['event_type']

    def _get_main_issue(self, item: dict) -> Optional[dict]:
        if 'conf_issue' in item:
            return item['conf_issue']
        if 'trip_issue' in item:
            return item['trip_issue']
        return None

    def _get_issue_key(self, item: dict) -> Optional[str]:
        issue = self._get_main_issue(item)
        return safe_getitem(issue, ['status', 'key'])

    def _get_issue_resolution(self, item: dict) -> Optional[str]:
        issue = self._get_main_issue(item)
        return safe_getitem(issue, ['resolution', 'key'])

    def _get_trip_status(self, item: dict) -> TripStatus:
        issue_resolution = self._get_issue_resolution(item)
        if issue_resolution:
            return TRACKER_RESOLUTION_TRIP_STATUS_MAPPING.get(issue_resolution, TripStatus.closed)
        issue_key = self._get_issue_key(item)
        return TRACKER_TRIP_STATUS_MAPPING.get(issue_key, TripStatus.new)

    def _get_person_trip_status(self, item: dict) -> PTStatus:
        issue_resolution = self._get_issue_resolution(item)
        if issue_resolution:
            return TRACKER_RESOLUTION_PT_STATUS_MAPPING.get(issue_resolution, PTStatus.closed)
        issue_key = self._get_issue_key(item)
        return TRACKER_PT_STATUS_MAPPING.get(issue_key, PTStatus.new)

    def _get_city_from(self, item: dict) -> Optional[str]:
        return safe_getitem(item, ['city_list', 0, 'city'])

    def _get_city_to(self, item: dict) -> Optional[str]:
        return safe_getitem(item, ['city_list', 1, 'city'])

    def _get_date(self, data: dict, path: list) -> Optional[str]:
        result = safe_getitem(data, path)
        if result:
            result = result.split('T')[0]
        return result

    def _get_person_trip(self, employee: dict) -> PersonTrip:
        return PersonTrip(
            person=self.persons_map.get(int(employee['employee'])),
            status=self._get_person_trip_status(employee),
            is_approved=bool(employee.get('approvementStatus')),
            description=employee['comment'],
            gap_date_from=self._get_date(employee, ['gap', 'from']),
            gap_date_to=self._get_date(employee, ['gap', 'till']),
            is_hidden=employee['is_private'],
            with_days_off=employee.get('has_holidays', False),
            compensation_type=TripDaysOffCompensations.money,  # TODO: пока не понял откуда брать
            is_offline=False,
            provider=Provider.aeroclub,
        )

    def _get_trip(self, staff_trip: dict) -> Trip:
        return Trip(
            trip_id=0,
            staff_trip_uuid=staff_trip['uuid'],
            status=self._get_trip_status(staff_trip),
            city_from=self._get_city_from(staff_trip),
            city_to=self._get_city_to(staff_trip),
            person_trips=[
                self._get_person_trip(employee) for employee in staff_trip['employee_list']
            ],
            purposes=[],  # TODO: взять из тикетов
            date_from=self._get_date(staff_trip, ['trip_date_from']),
            date_to=self._get_date(staff_trip, ['trip_date_to']),
            issue_travel=staff_trip.get('trip_issue', {}).get('key'),
            issue_conf=staff_trip.get('conf_issue', {}).get('key'),
            author=self.persons_map[int(staff_trip['author'])],
        )


class TripToStaffConverter:
    """
    Конвертирует командировку в данные,
    которые мы отправляем в Стафф для создания/редактирования командировки
    """
    def __init__(self, trip: TripFull):
        self.trip = trip

    def as_edit_json(self) -> dict[str, Any]:
        return self.as_create_json()

    def as_create_json(self) -> dict[str, Any]:
        if self.trip.event_type == TripType.trip:
            return self._as_trip()
        if self.trip.event_type == TripType.conf:
            return self._as_conf()
        if self.trip.event_type == TripType.trip_conf:
            return self._as_trip_conf()

    def _as_trip(self) -> dict[str, Union[str, list]]:
        return {
            'city_list': self._city_list(),
            'purpose': self._purposes(self.trip.purposes),
            'objective': self.trip.comment or ' ',
            'receiver_side': 'yandex',
            'employee_list': [
                self._trip_employee(person_trip)
                for person_trip in self.trip.person_trips
            ],
        }

    def _get_base_conf_fields(self) -> dict[str, Union[str, list]]:
        event_cost = (
            f'\nстоимость: {self.trip.conf_details.price or 0}\n'
            f'промокод: {self.trip.conf_details.promo_code or ""}\n'
            f'**Тип нужного билета**: {self.trip.conf_details.ticket_type or ""}\n'
            f'**Сайт**: {self.trip.conf_details.conference_url or ""}\n'
            f'**Программа мероприятия**: {self.trip.conf_details.program_url or ""}'
        )
        return {
            'event_cost': event_cost,
            'event_date_from': self.trip.conf_details.conf_date_from.strftime('%Y-%m-%d'),
            'event_date_to': self.trip.conf_details.conf_date_to.strftime('%Y-%m-%d'),
            'event_name': self.trip.conf_details.conference_name or 'placeholder:conf_name',
            'purpose': self._purposes(self.trip.purposes),
            'objective': self.trip.conf_details.participation_terms or ' ',
        }

    def _as_conf(self) -> dict[str, Union[str, list]]:
        result = self._get_base_conf_fields()
        result['employee_list'] = [self._conf_employee(pt) for pt in self.trip.person_trips]
        return result

    def _as_trip_conf(self) -> dict[str, Union[str, list]]:
        employee_list = []
        for pt in self.trip.person_trips:
            employee = self._trip_employee(pt)
            employee.update(self._conf_employee(pt))
            employee_list.append(employee)

        result = self._get_base_conf_fields()
        result.update({
            'employee_list': employee_list,
            'city_list': self._city_list(),
            'receiver_side': 'yandex',
        })
        return result

    def _city_list(self):
        # в командировках маршрут хранится в виде
        # [(city1, date1), (city2, date2), (city3, date3), ..., (cityN, dateN)]
        # в стафф нужно передать маршрут в виде
        # [(city1, date1), (city2, date1), (city3, date2), ..., (cityN, dateN-1), (city1, dateN)]
        city_list = []
        for index, point in enumerate(self.trip.route):
            departure_date = self.trip.route[max(index - 1, 0)].date
            city_list.append(self._city(
                city_name=point.city.name.ru,
                country_name=point.country.name.ru,
                departure_date=departure_date,
            ))
        city_list.append(self._city(
            city_name=self.trip.route[0].city.name.ru,
            country_name=self.trip.route[0].country.name.ru,
            departure_date=self.trip.route[-1].date,
            is_return_route=True,
        ))
        return city_list

    def _city(
            self,
            departure_date: date,
            city_name: str,
            country_name: str = 'Россия',
            is_return_route: bool = False,
    ) -> dict[str, Any]:
        return {
            'country': country_name,
            'city': city_name,
            # todo: брать из сервисов (aircraft если есть в сервисах хоть один самолёт)
            'transport': 'aircraft',
            'city_arrive_date_type': 'departure',  # всегда departue
            'departure_date': departure_date.strftime('%Y-%m-%d'),
            'time_proposal': '',
            'baggage': 'hand',  # дефолт ручная кладь, если есть сервис, то оттуда
            'fare': 'most_economical',  # дефолт. Потом посмотрим.
            'has_tickets': False,
            'tickets_cost': '',  # Зачем это поле? service_avia.price \ service_railway.??
            'tickets_cost_currency': 'RUB',  # service_avia.currency
            'car_rent': False,  # Буду арендовать автомобиль
            'need_hotel': True,  # Нужна гостиница
            'hotel': '',  # todo: из service_hotel
            # Готов доплатить для повышения класса проезда/гостиницы
            'ready_to_upgrade': False,
            # Желаемый уровень услуги, разницу удержать из зп, доплата наличными на месте
            'upgrade_comment': '',
            # коммент к услуге про отель - будем формировать по шаблону сами
            'comment': '',
            'is_return_route': is_return_route,
        }

    def _purposes(self, purposes: list[Purpose]) -> list[int]:
        return [p.purpose_id for p in purposes or []]

    def _trip_employee(self, person_trip: PersonTripFull) -> dict[str, Any]:
        person = person_trip.person
        mobile_number_for_taxi = (
            getattr(person_trip.travel_details, 'taxi_access_phone', None) or person.phone_number
        )

        if person_trip.documents:
            document = person_trip.documents[0]
            passport_number = f'{document.series} {document.number}'
            passport_name = f'{document.first_name} {document.last_name}'
        else:
            logger.warning('Person trip does not have any passport')
            passport_number = '0000 000000'
            passport_name = f'{person.first_name} {person.last_name}'

        return {
            'employee': person.staff_id,
            'passport_number': passport_number,
            'passport_name': passport_name,
            'employee_assignment': person_trip.employee_assignment,

            # поля про мобильную связь пока пропускаем
            'mobile_packages': False,
            'mobile_date_to': '',
            'mobile_date_from': '',
            'corporate_mobile_no': '',
            'need_mobile_additional_packages': '',

            'need_taxi': bool(mobile_number_for_taxi),
            'mobile_number_for_taxi': mobile_number_for_taxi,

            'need_copy_of_insurance': False,  # неоткуда брать
            'need_visa': person_trip.need_visa,
            'has_holidays': person_trip.with_days_off,
            'holidays_comment': '',
            'compensation': getattr(person_trip.compensation_type, 'value', None),

            'custom_dates': bool(person_trip.gap_date_from),
            'departure_date': person_trip.gap_date_from.strftime('%Y-%m-%d'),
            'return_date': person_trip.gap_date_to.strftime('%Y-%m-%d'),
            'is_private': person_trip.is_hidden,
            'trip_info': '',

            'interested_user_list': [],

            'comment': getattr(person_trip.travel_details, 'comment', ''),
        }

    def _conf_employee(self, person_trip: PersonTripFull) -> dict[str, Any]:
        comment_parts = [self.trip.comment]

        travel_details = person_trip.travel_details
        if travel_details and travel_details.comment:
            comment_parts.append(travel_details.comment)

        conf_details = person_trip.conf_details
        if conf_details and conf_details.comment:
            comment_parts.append(conf_details.comment)

        if conf_details:
            comment_parts.append(
                'Должность для бейджа: {position}\nИмя для бейджа: {name}'.format(
                    position=person_trip.conf_details.badge_position,
                    name=person_trip.conf_details.badge_name,
                )
            )
            event_role = person_trip.conf_details.role.value
        else:
            event_role = ConferenceParticiationType.listener.value
        return {
            'employee': person_trip.person.staff_id or person_trip.person.person_id,
            'comment': '\n'.join(comment_parts),
            'event_role': event_role,
        }
