# coding: utf-8

from __future__ import unicode_literals

import datetime

from django import forms

from cab.core import widgets
from cab.utils import export
from cab.utils import paginate
from cab.utils import std
from cab.utils import validators

from . import utils
from .. import datafields, const


class FilterField(
    validators.ValidatorFieldMixin,
    forms.CharField
):
    data_field_name = 'filter_id'

    def post_handle(self, value):
        filter = utils.Filter.from_str(value)
        if filter.scope not in (
            'subordinates',
            'saved',
            'department',
            'service',
        ):
            raise forms.ValidationError('Invalid filter scope')
        return filter


class PersonsByFilterWithFieldsBase(widgets.BaseEndpointHandler):

    def handle(self, data):
        persons = self.get_persons_list(data)
        self.clean_persons_list(persons)
        if not persons:
            return []
        self.add_extra_data(data, persons)
        return self.build_response(data, persons)

    def get_persons_list(self, data):
        raise NotImplementedError

    def add_extra_data(self, data, persons):
        raise NotImplementedError

    def build_response(self, data, persons):
        raise NotImplementedError

    def clean_persons_list(self, persons):
        chosen_fields = [
            'id',
            'login',
            'first_name',
            'last_name',
        ]
        for person in persons:
            for key in set(person) - set(chosen_fields):
                del person[key]


class PersonsByFilter(PersonsByFilterWithFieldsBase):

    class Validator(
        paginate.PaginatorFormMixin,
        validators.WidgetValidator,
    ):
        filter = FilterField(required=True)

    def handle(self, data):
        utils.remember_controls(
            auth=self.auth,
            persons_filter=data['filter'],
        )
        return super(PersonsByFilter, self).handle(data)

    def get_persons_list(self, data):
        filter = data['filter']
        paginator = data['paginator']
        result_set = utils.get_result_set_for_filter(
            auth=self.auth,
            filter=filter,
            paginator=paginator,
        )
        paginator.total = result_set.total
        first_page = std.get_first_or_none(result_set.get_pages())
        persons_list = first_page and list(first_page) or []
        return persons_list

    def add_extra_data(self, data, persons):
        datafields.add_data(
            auth=self.auth,
            persons=persons,
            fields=set(const.PERSON_CARD_REQUIRED_FIELDS) | {'gap'}
        )

    @paginate.paginated
    def build_response(self, data, persons):
        paginator = data['paginator']
        paginator.result = persons
        return paginator


class PersonsWithFields(PersonsByFilterWithFieldsBase):

    class Validator(
        paginate.PaginatorFormMixin,
        validators.WidgetValidator,
    ):
        filter = FilterField(required=True)
        fields = validators.PersonFieldsMultipleChoiceField(
            fields_list=datafields.ALL_BULK_FIELDS
        )

    def get_persons_list(self, data):
        filter = data['filter']
        paginator = data['paginator']
        result_set = utils.get_result_set_for_filter(
            auth=self.auth,
            filter=filter,
            paginator=paginator,
        )
        paginator.total = result_set.total
        first_page = std.get_first_or_none(result_set.get_pages())
        persons_list = first_page and list(first_page) or []

        return persons_list

    def add_extra_data(self, data, persons):
        datafields.add_data(
            auth=self.auth,
            persons=persons,
            fields=data['fields'],
        )

    def build_response(self, data, persons):
        return {
            'fields': data['fields'],
            'result': {
                person['id']: person
                for person in persons
            }
        }


class PersonsWithFieldsExport(PersonsByFilterWithFieldsBase):

    class Validator(validators.WidgetValidator):
        filter = FilterField(required=True)
        fields = validators.PersonFieldsMultipleChoiceField(
            fields_list=datafields.ALL_BULK_FIELDS
        )

    EXPORT_FIELDS_ORDER = (
        'first_name',
        'last_name',
        'login',
    ) + const.PERSONS_AVAILABLE_FIELDS_FLAT

    def handle(self, data):
        return super(PersonsWithFieldsExport, self).handle(data)

    def get_persons_list(self, data):
        filter = data['filter']
        result_set = utils.get_result_set_for_filter(
            auth=self.auth,
            filter=filter,
            paginator=paginate.Paginator(page=1, limit=100)
        )
        persons_list = list(result_set)
        return persons_list

    def add_extra_data(self, data, persons):
        datafields.add_data(
            auth=self.auth,
            persons=persons,
            fields=data['fields'],
        )

    def build_response(self, data, persons):
        response_data = self._prepare_data_for_response(persons)
        return export.make_xlsx_response(
            file_name='persons',
            data=response_data,
        )

    def _prepare_data_for_response(self, persons):
        if not persons:
            return

        prepared = []

        person_fields = persons[0].keys()
        title_fields = sorted(
            person_fields,
            key=self.get_field_sort_position,
        )

        prepared.append(title_fields)
        for person in persons:
            sorted_pairs = sorted(
                person.items(),
                key=lambda pair: self.get_field_sort_position(pair[0])
            )
            prepared.append([
                self.prepare_field_for_export(key, val)
                for (key, val) in sorted_pairs
            ])
        return prepared

    @classmethod
    def prepare_field_for_export(cls, key, value):
        if key == 'marks':
            value = value is not None and ' '.join(value)
        elif key == 'salary' and isinstance(value, dict):
            value = '%s %s' % (value['value'], value['currency'])
        elif key == 'last_activity' and isinstance(value, dict):
            value = '%s %s ago' % (
                value['office'],
                datetime.timedelta(seconds=value['seconds_ago']),
            )
        elif key == 'grade' and isinstance(value, dict):
            value = '%s %s' % (value['grid_type'], value['level'])
        elif key == 'year_bonus_payment' and isinstance(value, dict):
            value = '%s %s' % (value['value'], value['currency'])
        elif 'options' in key and isinstance(value, list):
            value = cls._prepare_options_related_field(key, value)
        elif isinstance(value, dict):
            value = cls._prepare_dict_common(key, value)
        elif isinstance(value, list):
            value = cls._prepare_list_common(key, value)
        return value

    @staticmethod
    def _prepare_options_related_field(key, value):
        def fetch_something_for_display(grant_data):
            if 'amount' in grant_data:
                return grant_data['amount']
            if 'value' in grant_data:
                return '%s %s' % (grant_data['value'], grant_data['currency'])

        return ', '.join(
            '%s %s' % (
                const.OPTIONS_DISPLAY_NAME.get(
                    grant_data['grant_type'],
                    grant_data['grant_type'],
                ),
                fetch_something_for_display(grant_data)
            )
            for grant_data in value
        )

    @classmethod
    def _prepare_list_common(cls, key, value):
        return ', '.join(
            cls._prepare_dict_common(key, item) if isinstance(item, dict)
            else unicode(item)
            for item in value
        )

    @staticmethod
    def _prepare_dict_common(key, value):
        return ' '.join(map(unicode, value.values()))

    @classmethod
    def get_field_sort_position(cls, field):
        if field in cls.EXPORT_FIELDS_ORDER:
            return cls.EXPORT_FIELDS_ORDER.index(field)
        return len(cls.EXPORT_FIELDS_ORDER)
