import logging
from os import path

from requests.exceptions import HTTPError

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.http import HttpResponseForbidden
from django.utils.functional import SimpleLazyObject

from .client import tvm2_client

User = get_user_model()


try:
    from django.utils.deprecation import MiddlewareMixin
except ImportError:
    MiddlewareMixin = object


log = logging.getLogger(__name__)


class TVMMiddleware(AuthenticationMiddleware):
    """
    Проверяет TVM-тикет в запросе
    """

    service_ticket_headers = [
        "HTTP_X_YA_SERVICE_TICKET",
        "HTTP_X_TVM2_TICKET",
    ]

    user_ticket_header = "HTTP_X_YA_USER_TICKET"

    @property
    def is_required(self):
        return getattr(settings, "TVM_TICKET_REQUIRED", True)

    def get_service_ticket_headers(self):
        header = getattr(settings, "TVM_SERVICE_TICKET_HEADER", None)
        if header:
            return [header]

        return self.service_ticket_headers

    def get_service_ticket(self, request):
        for header in self.get_service_ticket_headers():
            ticket = request.META.get(header)
            if ticket:
                return ticket

        return None

    def get_user_ticket(self, request):
        ticket_header = getattr(
            settings, "TVM_USER_TICKET_HEADER", self.user_ticket_header
        )
        return request.META.get(ticket_header)

    def check_tickets(self, request):
        service_ticket = self.get_service_ticket(request)
        if service_ticket:
            # check service ticket
            parsed_service_ticket = tvm2_client.parse_service_ticket(service_ticket)
            if parsed_service_ticket:
                request.tvm_service_id = getattr(parsed_service_ticket, "src", None)
            else:
                err_msg = "Unknown TVM service ticket"
                log.warning(err_msg)
                return HttpResponseForbidden(err_msg)

            # check user ticket
            user_ticket = self.get_user_ticket(request)
            if user_ticket:
                try:
                    parsed_user_ticket = tvm2_client.parse_user_ticket(user_ticket)
                    if parsed_user_ticket:
                        request.tvm_uid = getattr(
                            parsed_user_ticket, "default_uid", None
                        )
                    else:
                        err_msg = "Unknown TVM user ticket"
                        log.error(err_msg)
                        return HttpResponseForbidden(err_msg)

                except HTTPError:
                    err_msg = "TVM check ticket error"
                    log.exception(err_msg)
                    return HttpResponseForbidden(err_msg)

            return

        if self.is_required:
            err_msg = "TVM Service ticket is required, but not provided"
            log.error(err_msg)
            return HttpResponseForbidden(err_msg, status=401)

    def get_user(self, request):
        from django.contrib.auth.models import AnonymousUser

        if not hasattr(request, "_cached_user"):
            user = None
            user_uid = getattr(request, "tvm_uid", None)
            if user_uid:
                try:
                    user = User.objects.get(**{User.YAUID_FIELD: user_uid})
                except User.DoesNotExist:
                    pass

            request._cached_user = user or AnonymousUser()

        return request._cached_user

    def process_request(self, request):
        request.tvm_service_id = None
        request.tvm_uid = None

        normal_path = path.normpath(request.path) + "/"

        if any(map(normal_path.startswith, getattr(settings, "TVM_EXCLUDE_PATHS", []))):
            return

        http_accept = request.META.get("HTTP_ACCEPT", "")
        if settings.DEBUG and "text/html" in http_accept:
            log.debug(
                "TVM checks is skipped. DEBUG=%s, HTTP_ACCEPT=%s",
                settings.DEBUG,
                http_accept,
            )
            # eсли DEBUG включен, то из браузера можно попасть куда угодно
            return

        if not hasattr(request, "user"):
            request.user = SimpleLazyObject(lambda: self.get_user(request))

        return self.check_tickets(request)


class TVMDebugMiddleware(TVMMiddleware):
    debug_uid_header = "HTTP_DEBUG_UID"
    debug_service_id_header = "HTTP_DEBUG_SERVICE_ID"

    def get_debug_service_id(self, request):
        service_id = getattr(settings, "DEBUG_TVM_SERVICE_ID", None)
        service_id = request.META.get(self.debug_service_id_header, service_id)

        if service_id:
            log.debug("TVM_SERVICE_ID=%s", service_id)
            return int(service_id)

        return None

    def get_debug_uid(self, request):
        debug_uid = getattr(settings, "DEBUG_TVM_UID", None)
        debug_uid = request.META.get(self.debug_uid_header, debug_uid)

        if debug_uid:
            log.debug("TVM_UID=%s", debug_uid)

        return debug_uid

    def check_tickets(self, request):
        request.tvm_service_id = self.get_debug_service_id(request)
        request.tvm_uid = self.get_debug_uid(request)

        if request.tvm_service_id is None:
            return super().check_tickets(request)
