import json
import logging
import waffle

from constance import config
from django.conf import settings
from django.template import Template, Context
from django.db.models import prefetch_related_objects

from intranet.femida.src.calendar.api import Event, update_event
from intranet.femida.src.candidates.considerations.controllers import (
    get_or_create_relevant_consideration,
    update_consideration_extended_status,
)
from intranet.femida.src.candidates.helpers import get_main_email
from intranet.femida.src.communications.choices import MESSAGE_TYPES
from intranet.femida.src.communications.controllers import update_or_create_external_message
from intranet.femida.src.communications.models import MessageTemplate
from intranet.femida.src.core.signals import post_bulk_create
from intranet.femida.src.core.switches import TemporarySwitch
from intranet.femida.src.interviews.choices import (
    INTERVIEW_TYPES,
    APPLICATION_RESOLUTIONS,
    APPLICATION_STATUSES,
    INTERVIEW_STATES,
    INTERVIEW_ROUND_TYPES,
)
from intranet.femida.src.interviews.models import Interview, Assignment
from intranet.femida.src.notifications.interviews import (
    InterviewReassignedNotification,
    notify_about_interview_create,
)
from intranet.femida.src.users.models import get_robot_femida

logger = logging.getLogger(__name__)


def _remove_inappropriate_fields(interview_type, fields):
    if interview_type == INTERVIEW_TYPES.hr_screening:
        fields.pop('application', None)
        fields.pop('is_code', None)

    if interview_type != INTERVIEW_TYPES.aa:
        fields.pop('aa_type', None)


def _update_application(interview):
    type_to_resolution_map = {
        INTERVIEW_TYPES.screening: APPLICATION_RESOLUTIONS.invited_to_preliminary_interview,
        INTERVIEW_TYPES.regular: APPLICATION_RESOLUTIONS.invited_to_onsite_interview,
        INTERVIEW_TYPES.final: APPLICATION_RESOLUTIONS.invited_to_final_interview,
    }
    if interview.type not in type_to_resolution_map:
        return
    application = interview.application
    application.status = APPLICATION_STATUSES.in_progress
    application.resolution = type_to_resolution_map[interview.type]
    application.save(update_fields=['status', 'resolution', 'modified'])


def _update_interview_event(interview: Interview, event: Event) -> None:
    if not event:
        return
    # Пока работает только при создании АА секций и только через новую форму
    event_update_enabled = (
        (interview.type == INTERVIEW_TYPES.aa and interview.is_new_form)
        or waffle.switch_is_active(TemporarySwitch.FORCE_ENABLE_EVENT_UPDATE)
    )
    if not event_update_enabled:
        return

    interview_url = f'{settings.FEMIDA_URL}interviews/{interview.id}/'
    description = f'Ссылка на интервью: {interview_url}'
    if event.conference_url:
        description += f'\nСсылка на встречу: {event.conference_url}'

    event.name = interview.section
    event.description = description
    event.attendees.append(interview.interviewer.email)
    if interview.optional_participant:
        event.optional_attendees.append(interview.optional_participant.email)

    event_data = event.to_calendar_data()
    event_id = event_data.pop('id')
    instance_start_ts = event_data.pop('instanceStartTs')
    update_event(
        event_id=event_id, data=event_data, strict=True, instance_start_ts=instance_start_ts,
    )


def create_interview(event=None, **fields):
    created_by = fields.get('created_by')
    candidate = fields.get('candidate')

    _remove_inappropriate_fields(fields['type'], fields)

    fields['consideration'] = get_or_create_relevant_consideration(
        candidate=candidate,
        initiator=created_by,
        candidate_data={
            'responsibles': [created_by],
        }
    )

    interview = Interview.objects.create(**fields)
    create_assignments_from_presets([interview])

    _update_application(interview)
    _update_interview_event(interview, event)
    update_consideration_extended_status(fields['consideration'])
    notify_about_interview_create(interview, created_by, event)
    notify_candidate_about_preparation(interview, candidate)
    return interview


def create_interviews_for_round(data, interview_round):
    potential_interviewers_lists = [
        interview.pop('potential_interviewers', [])
        for interview in data
    ]
    interviews = [
        Interview(
            state=INTERVIEW_STATES.draft,
            type=_get_interview_type(interview_round.type, interview['aa_type']),
            round=interview_round,
            candidate=interview_round.candidate,
            consideration=interview_round.consideration,
            created_by=interview_round.created_by,
            **interview,
        )
        for interview in data
    ]
    interviews = Interview.objects.bulk_create(interviews)
    post_bulk_create.send(sender=Interview, queryset=interviews)

    PotentialInterviewer = Interview.potential_interviewers.through
    potential_interviewer_instances = []
    for interview, potential_interviewers in zip(interviews, potential_interviewers_lists):
        potential_interviewer_instances.extend(
            PotentialInterviewer(interview=interview, user=user)
            for user in potential_interviewers
        )
    potential_interviewer_instances = PotentialInterviewer.objects.bulk_create(
        potential_interviewer_instances,
    )
    post_bulk_create.send(sender=PotentialInterviewer, queryset=potential_interviewer_instances)
    return interviews


def create_assignments_from_presets(interviews):
    interviews_with_presets = [i for i in interviews if i.preset_id]
    prefetch_related_objects(interviews_with_presets, 'preset__problem_links')
    assignments = []
    for interview in interviews_with_presets:
        for link in interview.preset.problem_links.all():
            assignments.append(Assignment(
                interview=interview,
                problem_id=link.problem_id,
                position=link.position,
            ))
    assignments = Assignment.objects.bulk_create(assignments)
    post_bulk_create.send(sender=Assignment, queryset=assignments)
    return assignments


def update_interview(instance, data, initiator):
    event = data.pop('event', None)
    old_interviewer = instance.interviewer

    _remove_inappropriate_fields(instance.type, data)

    for attr, value in data.items():
        setattr(instance, attr, value)
    instance.save()

    _update_interview_event(instance, event)

    if old_interviewer != instance.interviewer:
        notification = InterviewReassignedNotification(
            instance=instance,
            initiator=initiator,
            old_interviewer=old_interviewer,
        )
        notification.send()
    return instance


def _get_interview_type(round_type, aa_type):
    if round_type == INTERVIEW_ROUND_TYPES.screening:
        return INTERVIEW_TYPES.screening
    if round_type == INTERVIEW_ROUND_TYPES.onsite:
        return INTERVIEW_TYPES.aa if aa_type else INTERVIEW_TYPES.regular
    return INTERVIEW_TYPES.final


def _get_template_by_profession(profession_id):
    templates_by_profession = json.loads(config.SCREENING_INVITATION_TEMPLATES_BY_PROFESSION)
    m_template_id = templates_by_profession.get(profession_id)
    if m_template_id is None:
        return None
    try:
        m_template_id = int(m_template_id)
    except (TypeError, ValueError):
        logger.error(f'Wrong template_id for profession_id={profession_id}')
        return None
    message_template = (
        MessageTemplate.objects
        .filter(is_active=True, id=m_template_id)
        .first()
    )
    if not message_template:
        logger.error(f'Could not find message template by id={m_template_id}')
        return None
    return message_template


def notify_candidate_about_preparation(interview, candidate):
    """
    FEMIDA-6034
    Если создана предварительная секция в разработку в ПП (поисковый портал),
    кандидату отправляется письмо с инструкцией по подготовке
    """
    if not interview.is_screening:
        return
    application = interview.application
    if application is None:
        return
    department = application.vacancy.department
    if not department or not department.is_in_tree(settings.YANDEX_SEARCH_DEPARTMENT_ID):
        return

    email = get_main_email(candidate)
    if not email:
        return
    profession_id = str(application.vacancy.profession_id)
    message_template = _get_template_by_profession(profession_id)
    if not message_template:
        return
    context = Context({'candidate_name': candidate.first_name})
    body = Template(message_template.text).render(context)
    data = {
        'candidate': candidate,
        'text': body,
        'type': MESSAGE_TYPES.outcoming,  # письмо сразу отправляется
        'email': email,
        'subject': message_template.subject,
    }
    robot_femida = get_robot_femida()
    update_or_create_external_message(data, initiator=robot_femida)
