# coding: utf-8
import logging

from django.conf import settings
from django.core.cache import caches
from django.db import DatabaseError, InterfaceError
from django.db.transaction import get_connection
from django.http import HttpRequest
from django.utils import translation, timezone
from django.utils.deprecation import MiddlewareMixin
from django.utils.functional import cached_property
from django.utils.translation.trans_real import (
    parse_accept_lang_header,
    language_code_re,
    get_supported_language_variant,
)
from django_idm_api.middleware import MigrateFromCertificateToTVMMiddleware
from django_replicated.middleware import ReplicationMiddleware, ReadOnlyMiddleware
from django_replicated.utils import routers
from django_yauth.middleware import YandexAuthMiddleware, YandexAuthTestMiddleware

from idm import VERSION
from idm.framework.authentication import IDMUserDescriptor, IDMTestUserDescriptor
from idm.utils.tvm import get_tvm_client

log = logging.getLogger(__name__)


class I18NMiddleware(MiddlewareMixin):
    """Дополнительный процессор для установки языка залогиненного пользователя
    в соответствии с блекбоксом
    """

    def process_request(self, request):
        lang = get_language_from_user(getattr(request, 'yauser', None))

        if lang is None:
            lang = get_language_from_request(request)

        if lang is None:
            lang = 'ru'
        if lang != 'ru':
            lang = 'en'
        translation.activate(lang)
        request.LANGUAGE_CODE = lang

    def process_response(self, request, response):
        language = translation.get_language()
        if 'Content-Language' not in response:
            response['Content-Language'] = language
        return response


def get_language_from_user(user):
    if user and getattr(user, 'fields', None) is not None:
        return user.fields.get('language')
    else:
        return None


def get_language_from_request(request):
    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
    for accept_lang, unused in parse_accept_lang_header(accept):
        if accept_lang == '*':
            break

        if not language_code_re.search(accept_lang):
            continue

        try:
            return get_supported_language_variant(accept_lang)
        except LookupError:
            continue
    return None


class IdmReplicationMiddleware(ReplicationMiddleware):
    """Отличается от базовой только возможностью вручную включить режим read_only,
    для чего используется свойство forced_state"""

    def process_request(self, request):
        self.forced_state = None
        result = super(IdmReplicationMiddleware, self).process_request(request)
        request.db_forced_state = self.forced_state
        request.db_state = routers.state()
        request._replication_middleware_processed = True
        return result


class IdmReadOnlyFlagMiddleware(ReadOnlyMiddleware):
    """
    При отвалившемся мастере выставляем флаг read_only и переводим
    все запросы на слейв
    """

    def is_service_read_only(self):
        cache = caches[settings.READONLY_CACHE_NAME]
        is_service_readonly = cache.get(settings.READONLY_CACHE_KEY)
        last_exc = None
        if is_service_readonly is None:
            for _ in range(settings.READONLY_MIDDLEWARE_RETRIES):
                try:
                    is_service_readonly = super().is_service_read_only()
                except (InterfaceError, DatabaseError) as last_exc:
                    log.warning('Exception in `is_service_read_only`', exc_info=True)
                    connection = get_connection()
                    connection.close()
                    connection.ensure_connection()
                else:
                    break

            if last_exc is not None:
                raise last_exc

            if not is_service_readonly:
                cache.set(settings.READONLY_CACHE_KEY, False, settings.READONLY_CACHE_TTL)

        return is_service_readonly

    def process_request(self, request):
        super().process_request(request)
        is_read_only = request.service_is_readonly
        if is_read_only:
            log.info('Service is in automatic read-only')


class InfoHeadersMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        service_is_readonly = getattr(request, 'service_is_readonly', None)
        response['X-IDM-READONLY'] = str(service_is_readonly).lower() if service_is_readonly else None
        response['X-IDM-VERSION'] = VERSION
        return response


class TimezoneMiddleware(MiddlewareMixin):
    def process_request(self, request):
        try:
            timezone.activate(request.user.timezone)
        except Exception:
            pass


class IDMYandexAuthMiddleware(YandexAuthMiddleware):
    def assign_user(self, request: HttpRequest):
        request.__class__.user = IDMUserDescriptor()


class IDMYandexAuthTestMiddleware(YandexAuthTestMiddleware):
    def assign_user(self, request: HttpRequest):
        request.__class__.user = IDMTestUserDescriptor()


class DjangoIdmTvmMiddleware(MigrateFromCertificateToTVMMiddleware):
    @cached_property
    def tvm_client(self):
        return get_tvm_client()
