import logging
import pytz

from datetime import date
from typing import Iterable, Optional

from dateutil.parser import parse
from django.conf import settings
from ids.exceptions import BackendError
from ids.registry import registry
from ids.repositories.base import RepositoryBase
from ids.resource import Resource
from ids.services.calendar_internal.connector import CalendarInternalError

from intranet.femida.src.calendar.helpers import (
    get_event_url,
    handle_calendar_ids_error,
    handle_calendar_bad_response,
)
from intranet.femida.src.utils.tvm2_client import get_service_ticket, get_robot_user_ticket


logger = logging.getLogger(__name__)


def _extract_rooms(data):
    rooms = []
    for resource in data.get('resources', []):
        if resource['resourceType'] not in ('room', 'protected-room'):
            continue
        rooms.append({
            'name': resource['name'],
            'url': get_event_url(resource),
            'email': resource['email'],
        })
    return rooms


def _extract_attendees_emails(attendees):
    result = set()
    for attend in attendees:
        if type(attend) is str:
            result.add(attend)
        if type(attend) is dict:
            result.add(attend.get('email', None))
    return list(result)


class Event:

    DT_FORMAT = '%Y-%m-%dT%H:%M:%SZ'

    @classmethod
    def from_calendar_response(cls, data):
        logger.info('from_calendar_response %s', data)
        rooms = _extract_rooms(data)
        start_time = parse(data['startTs']).astimezone(pytz.utc)
        end_time = parse(data['endTs']).astimezone(pytz.utc)

        return cls(
            id=data['id'],
            name=data['name'],
            description=data['description'],
            start_time=start_time,
            end_time=end_time,
            rooms=rooms,
            attendees=data.get('attendees', []),
            optional_attendees=data.get('optionalAttendees', []),
            conference_url=data.get('conferenceUrl', None),
            others_can_view=data.get('othersCanView', True),
            instance_start_ts=data.get('instanceStartTs', None),
        )

    def __init__(self, id, name, description, start_time, end_time, rooms, attendees, **kwargs):
        self.id = id
        self.name = name
        self.description = description
        self.start_time = start_time
        self.end_time = end_time
        self.rooms = rooms
        self.attendees = attendees
        self.instance_start_ts = kwargs.get('instance_start_ts', None)
        self.optional_attendees = kwargs.get('optional_attendees', [])
        self.conference_url = kwargs.get('conference_url')
        self.others_can_view = kwargs.get('others_can_view', True)
        self.resources = []

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'start_time': self.start_time.strftime(self.DT_FORMAT),
            'end_time': self.end_time.strftime(self.DT_FORMAT),
            'rooms': self.rooms,
            'attendees': self.attendees,
        }

    def to_calendar_data(self):
        attendees = _extract_attendees_emails(self.attendees)
        resources = _extract_attendees_emails(self.resources)
        optional_attendees = _extract_attendees_emails(self.optional_attendees)

        calendar_data = {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'startTs': self.start_time.strftime(self.DT_FORMAT),
            'endTs': self.end_time.strftime(self.DT_FORMAT),
            'instanceStartTs': self.instance_start_ts,
            'attendees': attendees + resources,
            'optionalAttendees': optional_attendees,
            'othersCanView': self.others_can_view,
            'conferenceUrl': self.conference_url,
        }
        logger.info(
            'calendar_data %s',
            calendar_data
        )
        return calendar_data

    @property
    def rooms_str(self):
        return ', '.join(room['name'] for room in self.rooms)


def _get_calendar_repository(resource_type: str, user_ticket: str = None) -> RepositoryBase:
    return registry.get_repository(
        service='calendar_internal',
        resource_type=resource_type,
        user_agent='femida',
        service_ticket=get_service_ticket(settings.TVM_CALENDAR_CLIENT_ID),
        user_ticket=user_ticket or get_robot_user_ticket(),
    )


def get_holidays(date_from: date, date_to: date,
                 uid: str = None, country: str = 'rus') -> Iterable:
    repo = _get_calendar_repository('holidays')
    params = {
        'from': date_from.isoformat(),
        'to': date_to.isoformat(),
        'for': country,
        'outMode': 'holidays',
    }
    if uid:
        params['uid'] = uid
    return repo.getiter(params)


def get_event(event_id: int, strict: bool = False,
              uid: str = None, user_ticket: str = None,
              instance_start_ts: str = None) -> Optional[Event]:
    """
    https://wiki.yandex-team.ru/calendar/api/new-web/#get-event
    """
    repo = _get_calendar_repository('event', user_ticket=user_ticket)
    params = {
        'eventId': event_id,
        'lang': 'ru',
        'dateFormat': 'zoned',
        'uid': uid or settings.FEMIDA_ROBOT_UID,
    }
    if instance_start_ts:
        params['instanceStartTs'] = instance_start_ts

    try:
        data = repo.getiter(params)
    except BackendError as exc:
        handle_calendar_ids_error(exc, strict, event_id)
        return
    if 'error' in data:
        handle_calendar_bad_response(data['error'], strict, event_id)
        return

    return Event.from_calendar_response(data)


def update_event(event_id: int, data: dict, strict: bool = False,
                 uid: str = None, user_ticket: str = None, instance_start_ts: str = None) -> None:
    """
    https://wiki.yandex-team.ru/calendar/api/new-web/#update-event

    :param data: Тело POST. Поля startTs и endTs обязательны.
    """
    repo = _get_calendar_repository('event', user_ticket=user_ticket)
    params = {
        'id': event_id,
        'uid': uid or settings.FEMIDA_ROBOT_UID,
    }
    if instance_start_ts:
        params['instanceStartTs'] = instance_start_ts
        params['applyToFuture'] = False

    resource = Resource(params)

    try:
        repo.update(resource=resource, fields=data)
    except BackendError as exc:
        handle_calendar_ids_error(exc, strict, event_id)
        return
    except CalendarInternalError as exc:
        error = {'name': exc.name, 'message': exc.message}
        handle_calendar_bad_response(error, strict, event_id)
