# -*- coding: utf-8 -*-
import re
import blackbox
import logging
import types

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.template import Context, Template
from django.template.defaultfilters import register
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from functools import wraps
from ids.exceptions import BackendError

from events.common_app.blackbox_requests import (
    CachedBlackbox as Blackbox,
    JsonBlackbox,
)
from events.common_app.sender.client import SenderClient, Address
from events.common_app.utils import (
    generate_code,
    get_lang_with_fallback,
    get_user_ip_address,
)
from events.common_app.directory import CachedDirectoryClient
from events.staff.client import get_staff_repository


logger = logging.getLogger(__name__)

UID_TYPE_INTERNAL = 'internal'
UID_TYPE_EXTERNAL = 'external'
UID_TYPE_CLOUD = 'cloud'
UID_TYPE_ANONYMOUS = 'anonymous'


def get_next_username():
    username = 'gu_%s' % generate_code()[:20]

    return username


class myodict(blackbox.odict):
    def __init__(self, *args, **kwargs):
        super(myodict, self).__init__(*args, **kwargs)
        self['login_rule'] = 0

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    def is_already_rewrited(self):
        """Индикатор того, что odict уже переписан"""
        return True


def patch_login_rule(f):
    """Патчит проверку на login_rule, выставляет значение в 0"""
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not hasattr(blackbox.odict, 'is_already_rewrited'):
            blackbox.odict = myodict
        return f(*args, **kwargs)
    return wrapper


def get_blackbox():
    return getattr(settings, 'YAUTH_BLACKBOX_INSTANCE') or Blackbox(url=blackbox.BLACKBOX_URL)


def get_user_ip(request):
    return request.META.get('REMOTE_ADDR', None)


def find_yauser_by_login(login):
    return get_blackbox().userinfo(
        login,
        get_user_ip_address(),
        by_login=True,
        dbfields=list(settings.YAUTH_PASSPORT_FIELDS),
        **settings.YAUTH_BLACKBOX_PARAMS
    )

GENDER_MALE = '1'
GENDER_FEMALE = '2'


class PersonalData:
    def __init__(self, **kwargs):
        self.personal_data = dict(kwargs)

    def _get_translated(self, value):
        if isinstance(value, str):
            return value
        if isinstance(value, dict):
            lang, fallback_lang = get_lang_with_fallback()
            translated = value.get(lang)
            if not translated:
                translated = value.get(fallback_lang)
            return translated

    @property
    def name(self):
        return self.personal_data.get('name')

    @property
    def surname(self):
        return self.personal_data.get('surname')

    @property
    def patronymic(self):
        return self.personal_data.get('patronymic')

    @property
    def birth_date(self):
        return self.personal_data.get('birth_date')

    @property
    def gender(self):
        gender = self.personal_data.get('gender')
        if gender == GENDER_MALE:
            return _('Мужской')
        elif gender == GENDER_FEMALE:
            return _('Женский')

    @property
    def email(self):
        return self.personal_data.get('email')

    @property
    def phone(self):
        return self.personal_data.get('phone')

    @property
    def login(self):
        return self.personal_data.get('login')

    @property
    def position(self):
        return self.personal_data.get('position')

    @property
    def job_place(self):
        return self.personal_data.get('job_place')

    @property
    def manager(self):
        return self.personal_data.get('manager')

    @property
    def karma(self):
        return self.personal_data.get('karma')

    @property
    def karma_status(self):
        return self.personal_data.get('karma_status')

    @property
    def groups(self):
        return self.personal_data.get('groups')

    @property
    def public_id(self):
        return self.personal_data.get('public_id')

    def __repr__(self):
        return repr(self.personal_data)


class PassportPersonalData(PersonalData):
    def __init__(self, uid):
        self.uid = uid
        self.personal_data = {}
        if self.uid:
            self.init_personal_data()

    def init_personal_data(self):
        self.params = {
            'dbfields': (bb_name for bb_name, _ in settings.YAUTH_PASSPORT_FIELDS),
            'emails': 'getdefault',
            'userip': get_user_ip_address(),
            'get_public_id': 'yes',
        }
        kwargs = self.get_passport_kwargs()
        self.bb = JsonBlackbox(**kwargs)
        self.personal_data = self._get_data()

    def get_passport_kwargs(self):
        if self.is_internal_uid(self.uid):
            return settings.INTERNAL_SITE_BLACKBOX_KWARGS
        return settings.EXTERNAL_SITE_BLACKBOX_KWARGS

    def is_internal_uid(self, uid):
        return re.match(r'^112\d{13}$', str(uid)) is not None

    def _get_user_info(self, uid):
        return self.bb.userinfo(uid=uid, **self.params)

    def _get_data(self):
        response = self._get_user_info(self.uid)
        users = response.get('users')
        if not users:
            return {}
        content = users[0]
        emails = content.get('address-list') or []
        fields = content.get('dbfields') or {}
        fio = (fields.get('account_info.fio.uid') or '').split(' ', 1)
        return {
            'login': fields.get('accounts.login.uid'),
            'gender': fields.get('account_info.sex.uid'),
            'birth_date': fields.get('account_info.birth_date.uid'),
            'email': self._get_email(emails),
            'name': fio[-1] if fio[-1] else None,
            'surname': fio[0] if len(fio) == 2 else None,
            'karma': self._get_karma(content),
            'karma_status': self._get_karma_status(content),
            'public_id': content.get('public_id'),
        }

    def _get_karma(self, response):
        return response.get('karma', {}).get('value')

    def _get_karma_status(self, response):
        return response.get('karma_status', {}).get('value')

    def _get_email(self, emails):
        email = None
        for it in emails:
            email = it.get('address')
            if it.get('default'):
                break
        return email

    @property
    def patronymic(self):
        return None

    @property
    def phone(self):
        return None

    @property
    def position(self):
        return None

    @property
    def job_place(self):
        return None

    @property
    def manager(self):
        return None

    @property
    def groups(self):
        pass


class DirPersonalData(PersonalData):
    def __init__(self, uid, cloud_uid, dir_id):
        self.uid = uid
        self.cloud_uid = cloud_uid
        self.dir_id = dir_id
        self.personal_data = {}
        if (self.uid or self.cloud_uid) and self.dir_id:
            self.init_personal_data()

    def init_personal_data(self):
        org_type = self._get_org_type(self.dir_id)
        if org_type and org_type != 'portal':
            self.personal_data = self._get_data()

    def _get_data(self):
        response = self._get_user_info(self.uid, self.cloud_uid, self.dir_id) or {}
        fio = response.get('name') or {}
        department = response.get('department') or {}
        contacts = response.get('contacts') or []
        return {
            'birth_date': response.get('birthday'),
            'gender': self._get_gender(response.get('gender')),
            'name': self._get_name(fio),
            'surname': self._get_surname(fio),
            'patronymic': self._get_patronymic(fio),
            'phone': self._get_phone(contacts),
            'email': self._get_email(contacts),
            'login': response.get('nickname'),
            'position': response.get('position'),
            'job_place': department.get('name'),
            'manager': lambda: self._get_manager(department.get('id'), self.dir_id),
            'groups': lambda: self._get_groups(response.get('groups'), self.dir_id),
        }

    def _get_org_type(self, dir_id):
        response = self._get_org_info(dir_id) or {}
        return response.get('organization_type')

    def _get_org_info(self, dir_id):
        client = CachedDirectoryClient()
        return client.get_organization(dir_id, fields='organization_type')

    def _get_user_info(self, uid, cloud_uid, dir_id):
        fields = 'name,email,nickname,birthday,gender,contacts,department.name,position,groups,groups.name'
        client = CachedDirectoryClient()
        return client.get_user(dir_id, uid, cloud_uid=cloud_uid, fields=fields)

    def _get_department_info(self, department_id, dir_id):
        client = CachedDirectoryClient()
        return client.get_department(dir_id, department_id, fields='head.name')

    def _get_group_info(self, group_ids, dir_id):
        client = CachedDirectoryClient()
        return [
            group
            for group in client.get_groups(dir_id)
            if str(group['id']) in group_ids
        ]

    def _get_manager(self, department_id, dir_id):
        if department_id:
            response = self._get_department_info(department_id, dir_id) or {}
            head = response.get('head') or {}
            if head:
                fio = head.get('name') or {}
                return ' '.join([
                    self._get_name(fio) or '',
                    self._get_surname(fio) or '',
                ])

    def _get_groups(self, groups, dir_id):
        if groups:
            group_ids = set(
                str(it.get('id'))
                for it in groups or []
            )
            if group_ids:
                return ', '.join(
                    self._get_translated(group.get('name'))
                    for group in self._get_group_info(group_ids, dir_id) or []
                )

    def _get_fio_field(self, fio, field_name):
        field = fio.get(field_name)
        return self._get_translated(field)

    def _get_name(self, fio):
        if isinstance(fio, str):
            return fio
        return self._get_fio_field(fio, 'first')

    def _get_surname(self, fio):
        if isinstance(fio, str):
            return ''
        return self._get_fio_field(fio, 'last')

    def _get_patronymic(self, fio):
        if isinstance(fio, str):
            return ''
        return self._get_fio_field(fio, 'middle')

    def _get_phone(self, contacts):
        phone = None
        for it in contacts:
            if it.get('type') == 'phone':
                phone = it.get('value')
                if it.get('main'):
                    break
        return phone

    def _get_email(self, contacts):
        email = None
        for it in contacts:
            if it.get('type') == 'email':
                email = it.get('value')
                if it.get('main'):
                    break
        return email

    def _get_gender(self, gender):
        if gender:
            if gender == 'male':
                return GENDER_MALE
            elif gender == 'female':
                return GENDER_FEMALE

    @property
    def karma(self):
        return None

    @property
    def karma_status(self):
        return None

    @property
    def manager(self):
        value = self.personal_data.get('manager')
        if isinstance(value, types.LambdaType):
            value = value()
            self.personal_data['manager'] = value
        return value

    @property
    def groups(self):
        value = self.personal_data.get('groups')
        if isinstance(value, types.LambdaType):
            value = value()
            self.personal_data['groups'] = value
        return value


def is_anonymous(profile_or_user):
    obj_id = getattr(profile_or_user, 'pk', None)
    obj_uid = getattr(profile_or_user, 'uid', None)
    return obj_uid is None or obj_id == settings.MOCK_PROFILE_ID


def get_external_user_login(user_uid):
    staff_repository = get_staff_repository('person')
    try:
        data = staff_repository.get_one(
            lookup={
                'uid': user_uid,
                '_fields': 'yandex',
            },
        )
        return data.get('yandex', {}).get('login')
    except (BackendError, KeyError):
        pass


def _get_blackbox(uid_type):
    if uid_type == UID_TYPE_EXTERNAL:
        return JsonBlackbox(**settings.EXTERNAL_SITE_BLACKBOX_KWARGS)
    elif uid_type == UID_TYPE_INTERNAL:
        return JsonBlackbox(**settings.INTERNAL_SITE_BLACKBOX_KWARGS)


def get_user_uid_by_login(login, uid_type):
    bb = _get_blackbox(uid_type)
    if bb:
        response = bb.userinfo(login=login, dbfields=[], userip=get_user_ip_address())
        for user in response.get('users') or []:
            if user.get('login') == login:
                return user['id']


def get_external_user_uid_by_login(login):
    return get_user_uid_by_login(login, UID_TYPE_EXTERNAL)


def get_external_uid(internal_uid):
    external_login = get_external_user_login(internal_uid)
    if external_login:
        return get_external_user_uid_by_login(external_login)


def is_yandex_user(uid):
    if not uid:
        return None

    staff_repository = get_staff_repository('person')
    try:
        data = staff_repository.get(
            lookup={
                'uid': uid,
                '_fields': 'official.affiliation',
            },
        )
    except BackendError:
        logger.exception('Got exception from staff-api')
        return False
    else:
        if len(data) == 0:
            return False
        aff = data[0]['official']['affiliation']
        if aff == 'yandex':
            return True
        else:
            return False


class TransitionEmail:
    class _ContentType:
        def __init__(self, ct):
            self.ct = ct
            self.name = ct.model_class()._meta.verbose_name_plural
            self.objects = []

        def add_object(self, object_pk):
            try:
                obj = self.ct.get_object_for_this_type(pk=object_pk)
            except ObjectDoesNotExist:
                pass
            else:
                self.objects.append(obj)

        def is_collected(self):
            return bool(self.objects)

    class _User:
        def __init__(self, user):
            self.username = user.username
            self.first = self.username[0]
            self.tail = self.username[1:]
            self.content_types = []

        def add_content_type(self, content_type):
            self.content_types.append(content_type)

        def is_collected(self):
            return bool(self.content_types)

    @staticmethod
    @register.filter(name='username_url')
    def username_url(user):
        first = user.username[0]
        others = user.username[1:]
        return mark_safe(
            f'<a href="https://staff.yandex-team.ru/{user.username}">'
            f'<b><span style="color: #ff0000">{first}</span>{others}</b>'
            '</a>'
        )

    def get_users(self, objects):
        users = []
        for user, ct_dict in objects.items():
            _user = self._User(user)
            for ct, obj_list in ct_dict.items():
                _content_type = self._ContentType(ct)
                for object_pk in obj_list:
                    _content_type.add_object(object_pk)
                if _content_type.is_collected():
                    _user.add_content_type(_content_type)
            if _user.is_collected():
                users.append(_user)
        return users

    def send_email(self, new_user, objects):
        users = self.get_users(objects)
        if not users:
            return

        subject = 'Передача прав на объекты Яндекс.Форм'
        template = Template('''
        <p>
            Вам как руководителю переданы права на объекты,
            созданные уволенными сотрудниками в Яндекс.Формах.
        </p>

        {% for user in users %}
            {% for ct in user.content_types %}
                <p>
                    {{ct.name}}, созданные {{user|username_url}}:
                    <ul>
                        {% for obj in ct.objects %}
                            <li><a href="{{obj.admin_url}}">{{obj.name}}</a></li>
                        {% endfor %}
                    </ul>
                </p>
            {% endfor %}
        {% endfor %}

        <p>
            <div>---</div>
            <div>Яндекс.Формы</div>
        </p>
        ''')
        context = Context({'users': users})
        body = template.render(context)
        sender = SenderClient(has_ugc=False)
        sender.add_address(Address(new_user.email))
        return sender.send_email('internal', {'subject': subject, 'body': body})
