# -*- coding: utf-8 -*-
import json
import logging

from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from requests.exceptions import HTTPError, Timeout as RequestTimeout

from events.accounts.org_ctx import NoOrgCtxException
from events.common_app.startrek.client import get_startrek_client
from events.common_app.utils import not_empty_values
from events.common_storages.proxy_storages import ProxyStorage
from events.common_storages.storage import ReadError
from events.surveyme_integration.services.base.action_processors import ActionProcessorBase
from events.surveyme_integration.exceptions import (
    AuthError,
    STARTREK_CHECK_FIELDS_MESSAGE,
    TrackerConfigError,
    TrackerPermissionError,
    TrackerTaskNotExistError,
    TrackerTimeoutError,
)
from events.common_app.directory import CachedDirectoryClient, DirectoryUserNotFoundError

STARTREK_FIELD_TAGS = _('Тэги')
STARTREK_FIELD_FOLLOWERS = _('Наблюдатели')
STARTREK_FIELD_ASSIGNEE = _('Исполнитель')
STARTREK_FIELD_AUTHOR = _('Автор')
STARTREK_FIELD_SUMMARY = _('Заголовок')
STARTREK_FIELD_DESCRIPTION = _('Описание')
STARTREK_FIELD_QUEUE = _('Очередь')
STARTREK_FIELD_TYPE = _('Тип')
STARTREK_FIELD_PRIORITY = _('Приоритет')

STARTREK_TEMPLATE_QUEUE = 'FORMSREQUESTS'
STARTREK_TEMPLATE_QUEUE_NAME = _('Заявки из Яндекс.Форм')
STARTREK_TEMPLATE_QUEUE_DESCRIPTION = _(
    'Очередь создана автоматически сервисом '
    'Яндекс.Формы для работы с заявками из форм'
)

logger = logging.getLogger(__name__)


class StartrekBaseActionProcessor(ActionProcessorBase):
    issue_actions = []
    startrek_field_names = {
        'tags': STARTREK_FIELD_TAGS,
        'followers': STARTREK_FIELD_FOLLOWERS,
        'assignee': STARTREK_FIELD_ASSIGNEE,
        'author': STARTREK_FIELD_AUTHOR,
        'summary': STARTREK_FIELD_SUMMARY,
        'description': STARTREK_FIELD_DESCRIPTION,
        'queue': STARTREK_FIELD_QUEUE,
        'type': STARTREK_FIELD_TYPE,
        'priority': STARTREK_FIELD_PRIORITY,
    }

    def do_action(self):
        dir_id = self.data.get('org_dir_id')
        if settings.IS_BUSINESS_SITE and not dir_id:
            raise NoOrgCtxException()

        try:
            self.client = get_startrek_client(dir_id)
            issue = self.run_issue_actions()
        except Exception as e:
            self.handle_error(e)
        return {
            'status': 'success',
            'response': {
                'issue': issue,
            }
        }

    def handle_error(self, e):
        if isinstance(e, RequestTimeout):
            raise TrackerTimeoutError(cause=e) from e
        elif isinstance(e, HTTPError):
            if e.response.status_code == 401:
                raise AuthError(cause=e) from e
            elif e.response.status_code == 403:
                raise TrackerPermissionError(cause=e) from e
            elif e.response.status_code == 404:
                raise TrackerTaskNotExistError(cause=e) from e
            elif e.response.status_code in (400, 422):
                extra = e.response.json()
                errors = extra.get('errors', {})
                field_names = [
                    str(self.startrek_field_names.get(field_name, field_name))
                    for field_name in errors.keys()
                ]
                if field_names:
                    msg = STARTREK_CHECK_FIELDS_MESSAGE % {
                        'fields': ','.join(field_names),
                    }
                else:
                    msg = ' '.join(extra.get('errorMessages') or [])
                raise TrackerConfigError(msg, e) from e
        elif isinstance(e, DirectoryUserNotFoundError):
            msg = STARTREK_CHECK_FIELDS_MESSAGE % {
                'fields': str(self.startrek_field_names.get('author')),
            }
            raise TrackerConfigError(msg) from e
        raise

    def run_issue_actions(self):
        for action in self.issue_actions:
            if action == 'update':
                issue = self.update_issue()
                if issue:
                    return issue
            elif action == 'create':
                issue = self.create_issue()
                if issue:
                    return issue

    def create_issue(self):
        try:
            data = self.get_data_for_insert()
            return self.client.create_issue(data)
        except HTTPError as e:
            # on conflict check header `X-Ticket-Key`
            if e.response.status_code == 409:
                issue_key = e.response.headers.get('X-Ticket-Key')
                if issue_key:
                    return {
                        'key': issue_key,
                    }
            raise

    def get_author(self):
        author = self.data.get('author')
        if author:
            return author

        yandexuid = self.data.get('yandexuid')
        cloud_uid = self.data.get('cloud_uid')
        dir_id = self.data.get('org_dir_id')

        if settings.IS_BUSINESS_SITE and dir_id:
            result = {}
            if yandexuid:
                if self.is_uid_in_org(dir_id, yandexuid):
                    result['uid'] = yandexuid
            elif cloud_uid:
                if self.is_cloud_uid_in_org(dir_id, cloud_uid):
                    result['cloudUid'] = cloud_uid
            return result
        elif settings.IS_INTERNAL_SITE:
            return yandexuid

    def is_cloud_uid_in_org(self, dir_id, cloud_uid):
        client = CachedDirectoryClient()
        user = client.get_user(dir_id, None, cloud_uid=cloud_uid)
        if not user:
            return None
        return user['nickname']

    def is_uid_in_org(self, dir_id, uid):
        client = CachedDirectoryClient()
        user = client.get_user(dir_id, uid)
        if not user:
            return None
        return user['nickname']

    def get_data_for_insert(self):
        data = {
            'queue': self.data['queue'],
            'unique': self.data.get('unique'),
            'type': {'id': self.data.get('type')},
            'priority': {'id': self.data.get('priority')},
            'tags': not_empty_values(self.data.get('tags')),
            'followers': not_empty_values(self.data.get('followers')),
        }
        if self.data.get('parent'):
            data['parent'] = self.data.get('parent')

        author = self.get_author()
        if author:
            data['author'] = author

        if self.data.get('assignee'):
            data['assignee'] = self.data.get('assignee')

        data.update(self.get_data_for_update())
        return data

    def get_data_for_update(self, issue_key=None):
        data = {
            'summary': self.data.get('summary'),
            'description': self.data.get('description'),
        }
        fields = self.data.get('fields') or {}
        data.update(fields)

        data_attachments = self.data.get('attachments')
        if data_attachments:
            attachments = []
            existing_attachments = set()
            if issue_key:
                for attachment in self.client.get_attachments(issue_key) or []:
                    existing_attachments.add(attachment.get('name'))
            for attachment in data_attachments:
                if attachment['filename'] not in existing_attachments:
                    try:
                        namespace = attachment['namespace']
                        body = ProxyStorage(namespace).open(attachment['path'])
                    except ReadError as e:
                        logger.warn(e)
                    else:
                        result = self.client.create_attachment(
                            file_name=attachment['filename'],
                            file_content=body
                        )
                        attachments.append(result.get('id'))
            data['attachmentIds'] = attachments
        return data

    def update_issue(self):
        if 'subscription_id' in self.data:
            issue_key = self.get_existing_issue_for_subscription_and_answer(
                subscription_id=self.data['subscription_id'],
                answer_id=self.data['answer_id'],
                notification_unique_id=self.data['notification_unique_id']
            )
            if issue_key:
                data = self.get_data_for_update(issue_key)
                return self.client.update_issue(issue_key, data)

    def get_existing_issue_for_subscription_and_answer(self,
                                                       subscription_id,
                                                       answer_id,
                                                       notification_unique_id):
        from events.surveyme_integration.models import HookSubscriptionNotification
        notification = (
            HookSubscriptionNotification.objects.using(settings.DATABASE_ROLOCAL)
            .filter(subscription_id=subscription_id, answer_id=answer_id, status='success')
            .exclude(id=notification_unique_id)
            .order_by('-date_finished')
            .first()
        )
        if notification:
            issue = notification.response.get('issue') or {}
            if isinstance(issue, str):
                issue = json.loads(issue)
            if isinstance(issue, dict):
                return issue.get('key')


class StartrekCreateTicketActionProcessor(StartrekBaseActionProcessor):
    issue_actions = ['create']


class StartrekUpdateOrCreateTicketActionProcessor(StartrekBaseActionProcessor):
    issue_actions = ['update', 'create']
