from http import HTTPStatus

from fastapi import APIRouter, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import or_

import aurweb.config
from aurweb import cookies, db
from aurweb.auth import requires_auth, requires_guest
from aurweb.exceptions import handle_form_exceptions
from aurweb.l10n import get_translator_for_request
from aurweb.models import User
from aurweb.templates import make_variable_context, render_template

router = APIRouter()


async def login_template(request: Request, next: str, errors: list = None):
    """Provide login-specific template context to render_template."""
    context = await make_variable_context(request, "Login", next)
    context["errors"] = errors
    context["url_base"] = f"{request.url.scheme}://{request.url.netloc}"
    return render_template(request, "login.html", context)


@router.get("/login", response_class=HTMLResponse)
async def login_get(request: Request, next: str = "/"):
    return await login_template(request, next)


@db.retry_deadlock
def _retry_login(request: Request, user: User, passwd: str, cookie_timeout: int) -> str:
    return user.login(request, passwd, cookie_timeout)


@router.post("/login", response_class=HTMLResponse)
@handle_form_exceptions
@requires_guest
async def login_post(
    request: Request,
    next: str = Form(...),
    user: str = Form(default=str()),
    passwd: str = Form(default=str()),
    remember_me: bool = Form(default=False),
):
    # TODO: Once the Origin header gets broader adoption, this code can be
    # slightly simplified to use it.
    login_path = aurweb.config.get("options", "aur_location") + "/login"
    referer = request.headers.get("Referer")
    if not referer or not referer.startswith(login_path):
        _ = get_translator_for_request(request)
        raise HTTPException(
            status_code=HTTPStatus.BAD_REQUEST, detail=_("Bad Referer header.")
        )

    user = (
        db.query(User)
        .filter(
            or_(
                User.Username == user,
                User.Email == user,
            )
        )
        .first()
    )
    if not user:
        return await login_template(request, next, errors=["Bad username or password."])

    if user.Suspended:
        return await login_template(request, next, errors=["Account Suspended"])

    cookie_timeout = cookies.timeout(remember_me)
    sid = _retry_login(request, user, passwd, cookie_timeout)
    if not sid:
        return await login_template(request, next, errors=["Bad username or password."])

    response = RedirectResponse(url=next, status_code=HTTPStatus.SEE_OTHER)

    secure = aurweb.config.getboolean("options", "disable_http_login")
    response.set_cookie(
        "AURSID",
        sid,
        max_age=cookie_timeout,
        secure=secure,
        httponly=secure,
        samesite=cookies.samesite(),
    )
    response.set_cookie(
        "AURTZ",
        user.Timezone,
        secure=secure,
        httponly=secure,
        samesite=cookies.samesite(),
    )
    response.set_cookie(
        "AURLANG",
        user.LangPreference,
        secure=secure,
        httponly=secure,
        samesite=cookies.samesite(),
    )
    response.set_cookie(
        "AURREMEMBER",
        remember_me,
        secure=secure,
        httponly=secure,
        samesite=cookies.samesite(),
    )
    return response


@db.retry_deadlock
def _retry_logout(request: Request) -> None:
    request.user.logout(request)


@router.post("/logout")
@handle_form_exceptions
@requires_auth
async def logout(request: Request, next: str = Form(default="/")):
    if request.user.is_authenticated():
        _retry_logout(request)

    # Use 303 since we may be handling a post request, that'll get it
    # to redirect to a get request.
    response = RedirectResponse(url=next, status_code=HTTPStatus.SEE_OTHER)
    response.delete_cookie("AURSID")
    response.delete_cookie("AURTZ")
    return response
