import logging
from typing import Optional

from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import (
    HttpResponseRedirect,
    HttpResponse,
    HttpResponseForbidden,
    HttpRequest,
)

from django_yauth.middleware import YandexAuthMiddleware

from staff.users.models import User
from staff.lib.auth import auth_mechanisms as auth
from staff.lib.auth.exceptions import NoRealIP


logger = logging.getLogger(__name__)


def set_auth_mechanism(request: HttpRequest) -> None:
    """
    Проставляет в запрос 'cookie', 'oauth', 'tvm', 'tvm-user', 'center', 'anonymous'
    либо None в случае если и не пытались
    """

    request.auth_mechanism = (
        request.yauser.authenticated_by
        and request.yauser.authenticated_by.mechanism_name
    )

    if request.auth_mechanism == auth.TVM and getattr(request.yauser, 'is_impersonated', False):
        request.auth_mechanism = auth.TVM_USER


def set_user(request: HttpRequest) -> None:
    """
    Проставляет юзера по uid через Staff, чтобы не ходить в бб за логином
    """
    user = None
    if request.yauser.is_authenticated() and request.yauser.uid is not None:
        user = User.objects.select_related('staff').filter(staff__uid=request.yauser.uid).first()
    request.user = user or AnonymousUser()


def set_real_user_ip(request: HttpRequest) -> None:
    """raises NoRealIP on error"""
    request.real_user_ip = extract_user_ip(request)


def response_redirect_to_passport(request: HttpRequest, log_message: str = None) -> HttpResponse:
    current_url = request.build_absolute_uri()
    url_where_to_redirect = f'{settings.LOGIN_URL}&{settings.REDIRECT_FIELD_NAME}={current_url}'
    logger.warning('AuthMiddleware. Redirecting to passport. Reason: "%s"', log_message or '')
    return HttpResponseRedirect(url_where_to_redirect)


def response_forbidden(log_message: str = None) -> HttpResponse:
    logger.warning('AuthMiddleware. Denied access. Reason: "%s"', log_message or '')
    return HttpResponseForbidden('Access denied.')


class AuthMiddleware(YandexAuthMiddleware):
    """
    Мидлварь
    1. выбирает и применяет механизм аутентификации
    2. получает IP пользователя
    3. отсекает запросы с неудачной попыткой аутентификации
    """

    def process_request(self, request: HttpRequest) -> Optional[HttpResponse]:
        """
        Наполнить request нужными данными
        Отбросить явные случаи, когда точно нельзя пускать. И пропустить дальше в AccessMiddleware
        """
        try:
            set_real_user_ip(request)
        except NoRealIP:
            logger.error('Could not define user ip, blackbox will fail')
            return HttpResponse(
                ('You cannot be authorized, because the web server'
                 ' does not pass your IP address to the app.\n'),
                status=401,
            )

        response = super(AuthMiddleware, self).process_request(request)
        if response:
            return response

        set_auth_mechanism(request)
        set_user(request)

        if not request.yauser.is_authenticated():

            if request.auth_mechanism is None:
                return response_redirect_to_passport(request, 'No auth mechanism matched')

            if request.auth_mechanism == auth.COOKIE:
                return response_forbidden('Wrong cookie')

            return response_forbidden(
                f'Denied unauthorised access with {request.auth_mechanism} authorisation'
            )

        if request.yauser.is_authenticated():
            person = getattr(request.user, 'get_profile', lambda: None)()

            if person is not None and person.is_dismissed:
                return response_forbidden(f'Dissmissed user `{person.login}` auth attempt')

            if person is None and request.auth_mechanism not in auth.USERLESS_AUTH_MECHANISMS:
                return response_forbidden(
                    f'Unauthorised access is not suitable with {request.auth_mechanism} authorisation'
                )

        logger.debug('Auth middleware passed with %s mechanism', request.auth_mechanism)
        return None


def extract_user_ip(request):
    for header_name in settings.YAUTH_REAL_IP_HEADERS:
        ip = request.META.get(header_name)
        if ip:
            return ip.split(",")[-1].strip()
    raise NoRealIP
