import logging
from typing import Optional
import urllib

from intranet.trip.src.config import settings
from intranet.trip.src.enums import Citizenship, ConferenceParticiationType
from intranet.trip.src.lib.aeroclub.api import aeroclub, aeroclub_fs
from intranet.trip.src.lib.aeroclub.enums import ACServiceType
from intranet.trip.src.lib.tracker.api import tracker_api
from intranet.trip.src.lib.tracker.notifications import notify_by_tracker_comment
from intranet.trip.src.models import PersonTrip, Trip
from intranet.trip.src.unit_of_work import UnitOfWork

logger = logging.getLogger(__name__)


async def update_conf_role_in_tracker_issue(
    uow: UnitOfWork,
    trip_id: int,
    person_id: int,
):
    person_trip = await uow.person_trips.get_detailed_person_trip(
        trip_id=trip_id,
        person_id=person_id,
    )
    mapping_role = {
        ConferenceParticiationType.listener: "Слушатель",
        ConferenceParticiationType.speaker: "Выступающий",
        ConferenceParticiationType.recruiter: "Рекрутёр",
    }
    context = {
        'role': mapping_role[person_trip.conf_details.role],
    }
    if person_trip.conf_tracker_issue is None:
        raise Exception("Missing tracker_issue")
    async with uow:
        uow.add_job(
            'notify_by_tracker_comment_task',
            issue=person_trip.conf_tracker_issue,
            template_name='change_conf_role.jinja2',
            context=context,
        )


async def update_speaker_details_in_tracker_issue(
    uow: UnitOfWork,
    trip_id: int,
    person_id: int,
):
    person_trip = await uow.person_trips.get_detailed_person_trip(
        trip_id=trip_id,
        person_id=person_id,
    )

    if not person_trip.conf_details:
        logger.error('Not a conference. trip_id=%s, person_id=%s', trip_id, person_id)
        return

    if person_trip.conf_details.role != ConferenceParticiationType.speaker:
        logger.error('Person is not speaker. trip_id=%s, person_id=%s', trip_id, person_id)
        return

    assert person_trip.conf_tracker_issue is not None, 'Missing tracker_issue'

    context = {
        'presentation_topic': person_trip.conf_details.presentation_topic,
        'is_hr_approved': person_trip.conf_details.is_hr_approved,
    }

    async with uow:
        uow.add_job(
            'notify_by_tracker_comment_task',
            issue=person_trip.conf_tracker_issue,
            template_name='speaker_details_updated.jinja2',
            context=context,
        )


async def update_bribe_warning_in_tracker_issue(
    uow: UnitOfWork,
    trip_id: int,
    person_id: int,
):
    person_trip = await uow.person_trips.get_detailed_person_trip(
        trip_id=trip_id,
        person_id=person_id,
    )

    if not person_trip.conf_details:
        logger.error('Not a conference. trip_id=%s, person_id=%s', trip_id, person_id)
        return

    assert person_trip.conf_tracker_issue is not None, 'Missing tracker_issue'

    async with uow:
        uow.add_job(
            'notify_by_tracker_comment_task',
            issue=person_trip.conf_tracker_issue,
            template_name='conference_bribe_warning.jinja2',
            context={},
        )


async def create_attachments(
    issue: str,
    attachments: list[dict],
) -> Optional[list[int]]:
    attachment_ids = []
    for attachment in attachments:
        if not attachment.get('url') or not attachment.get('name'):
            logger.error('Unable to retrieve file to issue: %s', issue)
            continue
        url = urllib.parse.urlparse(attachment['url'])
        file_data = await aeroclub_fs.get_file(
            path=url.path,
            params=urllib.parse.parse_qs(url.query),
        )
        attachment_info = await tracker_api.create_attachment(
            file_data=file_data,
            file_name=attachment['name'],
        )
        if not attachment_info or not attachment_info.get('id'):
            logger.error('Unable to upload file to issue: %s', issue)
            continue

        attachment_ids.append(int(attachment_info['id']))

    if not attachment_ids:
        logger.error('Not found files to send to issue: %s', issue)
        return None

    return attachment_ids


async def send_attachments_to_tracker(
    issue: str,
    text: str,
    attachments: list[dict],
    summonees: list[str] = None,
):
    attachment_ids = await create_attachments(issue, attachments)
    await tracker_api.create_comment(
        issue=issue,
        text=text,
        summonees=summonees,
        attachment_ids=attachment_ids,
    )


async def notify_about_service_execution(
    uow: UnitOfWork,
    trip_id: int,
    person_id: int,
    provider_order_id: int,
    provider_service_id: int,
    force: bool = False,
):
    """
    Отправить комментарий в трекер об успешно оформленой услуге.
    Комментарий отправляется только при наличии приложенных файлов (ваучеров/билетов).

    :param force: Отправлять комментарий даже при отсутствии приложенных файлов.
    """
    service = await aeroclub.get_service(
        order_id=provider_order_id,
        service_id=provider_service_id,
    )
    issue = await uow.person_trips.get_travel_tracker_issue(
        trip_id=trip_id,
        person_id=person_id,
    )
    attachments = [
        attachment
        for document in service['documents']
        for attachment in document['attachments']
    ]

    if not attachments and not force:
        raise Exception('Attachments were not found')

    attachment_ids = await create_attachments(issue, attachments)

    context = {
        'ACServiceType': ACServiceType,
        'aeroclub_service': service,
        'person_trip_url': settings.person_trip_url(
            trip_id=trip_id,
            person_id=person_id,
        ),
    }

    await notify_by_tracker_comment(
        issue=issue,
        template_name='trip_service_executed.jinja2',
        context=context,
        attachment_ids=attachment_ids,
    )


def get_context_for_belarus_offline_trip(
        trip: Trip,
        person_trip: PersonTrip,
        travel_issue: str,
) -> dict:
    route = person_trip.route
    segments = []
    for i in range(len(route)):
        j = (i + 1) % (len(route) - 1)
        segments.append({
            'city_from': route[i].city.name.ru,
            'country_from': route[i].country.name.ru,
            'city_to': route[j].city.name.ru,
            'country_to': route[j].country.name.ru,
            'date': route[i].date,
        })

    cities = [point.city.name.ru for point in route]
    cities.append(route[0].city.name.ru)

    person_trip_dict = person_trip.dict()
    return {
        'person_trip_url': person_trip.url,
        'travel_issue': travel_issue,
        'person': person_trip_dict['person'],
        'person_trip': person_trip_dict,
        'comment': trip.comment,
        'segments': segments,
        'cities': cities,
        'purposes': [purpose.name for purpose in trip.purposes],
    }


async def notify_offline_person_trip_created(uow, trip_id: int, person_id: int):
    trip = await uow.trips.get_detailed_trip(trip_id)
    person_trip = await uow.person_trips.get_detailed_person_trip(trip_id, person_id)

    if not person_trip.is_offline:
        logger.error('Person trip is not offline. Skip.')

    citizenship = person_trip.person.company.country
    travel_issue = person_trip.travel_details.tracker_issue
    if citizenship == Citizenship.BY:
        await notify_by_tracker_comment(
            issue=travel_issue,
            template_name='person_trip_belarus_created.jinja2',
            context=get_context_for_belarus_offline_trip(
                trip=trip,
                person_trip=person_trip,
                travel_issue=travel_issue,
            ),
        )
    else:
        await notify_by_tracker_comment(
            issue=travel_issue,
            template_name='person_trip_offline_created.jinja2',
            context={
                'person_trip_url': person_trip.url,
                'description': person_trip.description,
            },
        )
