# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, http
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
from odoo.addons.web.controllers.home import SIGN_UP_REQUEST_PARAMS
from odoo.exceptions import UserError
from odoo.http import request

# partner_firstname exposes `firstname` / `lastname` on res.users and
# res.partner; we need them as accepted qcontext params so the signup form
# roundtrips them transparently through `get_auth_signup_qcontext`.
SIGN_UP_REQUEST_PARAMS.add("firstname")
SIGN_UP_REQUEST_PARAMS.add("lastname")
# `accepted_terms` carries the "I agree to the terms & conditions"
# checkbox value from the signup form. Adding it here makes the value
# round-trip through `get_auth_signup_qcontext` so we can validate it
# server-side in `_prepare_signup_values`.
SIGN_UP_REQUEST_PARAMS.add("accepted_terms")

# Session keys used by the membership signup flow.
MEMBERSHIP_SIGNUP_SESSION_KEYS = ("membership_signup_active", "membership_signup_product_id")


class AuthSignupHomeMembership(AuthSignupHome):

    def _prepare_signup_values(self, qcontext):
        values = super()._prepare_signup_values(qcontext)
        # Legal terms acceptance: the signup form includes a mandatory
        # checkbox "I agree to the terms & conditions". The POST value is
        # 'accepted' when checked. Reject the submission with a UserError
        # (the core web_auth_signup controller catches UserError and exposes
        # it as `qcontext['error']` which the template renders as a red
        # alert above the form, so no extra plumbing is needed). The browser
        # also enforces it client-side via `required="required"`.
        #
        # `_prepare_signup_values` is also invoked by the core during the
        # password reset flow (`/web/reset_password`). That form does NOT
        # render the terms checkbox, so we must skip the validation there.
        # Existing users resetting their password already accepted the terms
        # when they signed up. We detect the reset flow primarily by the
        # request path and secondarily by the qcontext flag set by core.
        is_reset_password = (
            request.httprequest.path == "/web/reset_password"
            or qcontext.get("reset_password")
        )
        if not is_reset_password and qcontext.get("accepted_terms") != "accepted":
            raise UserError(_("You must accept the terms and conditions."))
        # partner_firstname: the signup form collects `firstname` / `lastname`
        # separately. We inject both into the signup values so the resulting
        # partner/user has them populated.
        #
        # We ALSO keep a composed `name` (firstname + " " + lastname) in the
        # values, on purpose. The core's `_create_user_from_template`
        # (`auth_signup/models/res_users.py`) rejects the signup with
        # `ValueError: Signup: no name or partner given for new user` if
        # neither `partner_id` nor `name` is present in the values — that
        # check runs BEFORE `template_user.copy(values)` reaches
        # `res.partner.create`, where `partner_firstname` would otherwise
        # recompute `name` from the split fields.
        #
        # By providing the composed `name`, the check passes; then, inside
        # `res.partner.create`, partner_firstname's override detects that both
        # `firstname` and `lastname` are in the vals and DISCARDS the composed
        # `name` (see `partner_firstname/models/res_partner.py:_name_fields_in_vals`
        # branch `del vals["name"]`), recomputing it from the split fields.
        # Net effect: the partner ends up with the proper `firstname`,
        # `lastname` and a recomputed `name`, exactly as intended.
        firstname = qcontext.get("firstname", "")
        lastname = qcontext.get("lastname", "")
        if firstname or lastname:
            values["firstname"] = firstname
            values["lastname"] = lastname
            partner_model = request.env["res.partner"]
            values["name"] = partner_model._get_computed_name(
                lastname, firstname
            )
        return values

    @http.route()
    def web_auth_signup(self, *args, **kw):
        response = super().web_auth_signup(*args, **kw)
        if request.session.uid:
            # Successful signup: if this is a membership flow, add the
            # pending membership product to the now-authenticated cart and
            # redirect straight to /shop/cart (instead of going back to the
            # product page as the core would, via the `redirect` query
            # param). Otherwise fall through to the regular response.
            redirect = self._membership_post_login_add_to_cart()
            if redirect is not None:
                return redirect
        return response

    @http.route()
    def web_login(self, *args, **kw):
        response = super().web_login(*args, **kw)
        # Same handling on explicit login: covers the "Already have an
        # account?" branch from the signup page (option B agreed with the
        # client): both signup and login auto-add the pending membership
        # product and go straight to the cart.
        if request.session.uid:
            redirect = self._membership_post_login_add_to_cart()
            if redirect is not None:
                return redirect
        return response

    def _membership_post_login_add_to_cart(self):
        """Add the pending membership product (if any, set by the
        add-to-cart interception on `/shop/cart/update`) to the
        now-authenticated user's cart and redirect to `/shop/cart`.

        Returns a Response (redirect to /shop/cart) when a membership
        product was pending in the session, or `None` when none was —
        in the latter case the caller should fall through to the
        original response (e.g., a normal login that did not originate
        from the membership flow).

        Side effect: always clears the membership session markers
        (`membership_signup_active`, `membership_signup_product_id`),
        whether a pending product was present or not, so they cannot
        leak into a later request.
        """
        product_id = request.session.get("membership_signup_product_id")
        if not product_id:
            for key in MEMBERSHIP_SIGNUP_SESSION_KEYS:
                request.session.pop(key, None)
            return None
        # Get (or create) the SO for the now-authenticated web user and
        # add the pending membership product to it.
        order = request.website.sale_get_order(force_create=True)
        order._cart_update(product_id=int(product_id), add_qty=1)
        # Keep the cart navbar badge in sync (the core sets this on its
        # own /shop/cart/update_json path; we bypass that here).
        request.session["website_sale_cart_quantity"] = order.cart_quantity
        # Drop the markers: the membership flow is now complete.
        for key in MEMBERSHIP_SIGNUP_SESSION_KEYS:
            request.session.pop(key, None)
        return request.redirect("/shop/cart")