# copyright 2017-2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# copyright 2014-2016 UNLISH S.A.S. (Montpellier, FRANCE), all rights reserved.
#
# contact https://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <https://www.gnu.org/licenses/>.

""" Provide login views that reproduce a classical CubicWeb behavior"""
import logging
import warnings

from urllib.parse import urlparse

from pyramid import security
from pyramid.httpexceptions import HTTPSeeOther, HTTPNotFound
from pyramid.settings import asbool
from pyramid.view import view_config

import cubicweb
from cubicweb_web.utils import url_path_starts_with_prefix, remove_prefix_from_url_path
from cubicweb_web.bwcompat import render_view

logger = logging.getLogger(__name__)


@view_config(route_name="login")
def login_form(request):
    """Default view for the 'login' route.

    Display the 'login' CubicWeb view, which is should be a login form"""
    request.response.text = render_view(request, "login")
    return request.response


@view_config(
    route_name="login", request_param=("__login", "__password"), request_method="POST"
)
def login_password_login(request):
    """Handle POST of __login/__password on the 'login' route.

    The authentication itself is delegated to the CubicWeb repository.

    Request parameters:

    :param __login: The user login (or email if :confval:`allow-email-login` is
                    on.
    :param __password: The user password
    :param __setauthcookie: (optional) If defined and equal to '1', set the
                            authentication cookie maxage to 1 week.

                            If not, the authentication cookie is a session
                            cookie.
    """
    repo = request.registry["cubicweb.repository"]

    login = request.params["__login"]
    password = request.params["__password"]

    try:
        with repo.internal_cnx() as cnx:
            user = repo.authenticate_user(cnx, login, password=password)
            user_eid = user.eid
    except cubicweb.AuthenticationError:
        if repo.vreg.config.get("language-mode") != "":
            lang = request.cw_request.negotiated_language()
            if lang is not None:
                request.cw_request.set_language(lang)
        request.cw_request.set_message(
            request.cw_request._(
                "Authentication failed. Please check your credentials."
            )
        )
        request.cw_request.post = dict(request.params)
        del request.cw_request.post["__password"]
        request.response.status_code = 403
        return login_form(request)

    headers = security.remember(
        request,
        user_eid,
        persistent=asbool(request.params.get("__setauthcookie", False)),
    )

    new_path = request.params.get("postlogin_path", "")
    base_url = repo.vreg.config["base-url"]
    route_prefix = urlparse(base_url).path.lstrip("/")

    if repo.vreg.config["receives-base-url-path"] and route_prefix:
        if not url_path_starts_with_prefix(new_path, route_prefix):
            logger.warning(
                f"Error 404: login view tries to redirect to the path '{new_path}' but it doesn't "
                f"start with the route_prefix '{route_prefix}'"
            )
            raise HTTPNotFound()
        new_path = remove_prefix_from_url_path(new_path, route_prefix)
    else:
        if route_prefix:
            warnings.warn(
                "in your configuration your base url "
                f"'{repo.vreg.config['base-url']}' has a route prefix: "
                f"'{route_prefix}' but the option 'receives-base-url-path' is set to False.\n\n"
                "You should set it to True and avoid relying on nginx/apache for url rewritting."
            )

        new_path = new_path.lstrip("/")

    if new_path == "login":
        new_path = ""

    url = request.cw_request.build_url(new_path)
    raise HTTPSeeOther(url, headers=headers)


@view_config(route_name="login", effective_principals=security.Authenticated)
def login_already_loggedin(request):
    """'login' route view for Authenticated users.

    Simply redirect the user to '/'."""
    raise HTTPSeeOther(request.params.get("postlogin_path", "/"))


def includeme(config):
    """Create the 'login' route ('/login') and load this module views"""
    cwconfig = config.registry["cubicweb.config"]
    config.add_route("login", "/login")
    if cwconfig.get("language-mode") == "url-prefix":
        config.add_route("login-lang", "/{lang}/login")
        config.add_view(
            login_already_loggedin,
            route_name="login-lang",
            effective_principals=security.Authenticated,
        )
        config.add_view(login_form, route_name="login-lang")
        config.add_view(
            login_password_login,
            route_name="login-lang",
            request_param=("__login", "__password"),
            request_method="POST",
        )
    config.scan(".login")
