from datetime import timedelta
from plone import api
from plone.app.event.base import localized_now
from plone.memoize.view import memoize
from plone.protect.utils import safeWrite
from ploneintranet.core import _
from ploneintranet.layout.interfaces import IDiazoDashboardTemplate
from ploneintranet.notifications.mails.base import BaseEmail
from Products.Five import BrowserView
from uuid import uuid4
from zope.annotation.interfaces import IAnnotations
from zope.interface import implementer
from zope.publisher.interfaces import IPublishTraverse


@implementer(IDiazoDashboardTemplate)
class EmailVerificationSentView(BrowserView):
    """The page that claims a verification email has been sent to the user."""


class EmailVerificationSendTokenView(BaseEmail):
    """The view used to send an email to a user
    with the verification token that will be used to login.
    """

    minutes = 10
    token_storage_key = "ploneintranet.userprofile.email_verification_token"
    translatable_subject = _("Please complete your authentication")

    @property
    def recipient_user(self):
        return self.context

    @property
    @memoize
    def verification_link(self):
        """Generate the verification link"""
        token = uuid4().hex
        IAnnotations(self.context)[self.token_storage_key] = {
            "token": token,
            "valid_until": localized_now() + timedelta(minutes=self.minutes),
            "came_from": self.request.get("came_from", ""),
        }
        return f"{self.context.absolute_url()}/@@email-verification-check-token/{token}"

    @property
    @memoize
    def password_reset_link(self):
        """Generate the password reset link"""
        return f"{api.portal.get().absolute_url()}/@@mail_password_form"


@implementer(IPublishTraverse)
class EmailVerificationCheckToken(BrowserView):
    """The page that verifies the token received by the user, and logs the user in."""

    token_storage_key = "ploneintranet.userprofile.email_verification_token"

    @property
    @memoize
    def token_storage(self):
        return IAnnotations(self.context).get(self.token_storage_key, {})

    def clear_token(self):
        """Clear the token from the annotations"""
        safeWrite(self.token_storage)
        self.token_storage.clear()

    def redirect(self, as_logged_in=False):
        """Redirect to the target URL"""
        if as_logged_in:
            msg = _("You are now logged in")
        else:
            msg = _("Invalid token")

        api.portal.show_message(msg, self.request)

        if not as_logged_in:
            return self.request.response.redirect(
                f"{api.portal.get().absolute_url()}/require_login"
            )

        # Delegate to the default login view the redirect target
        # it needs to have the userid set to the user id in the request form
        # to properly work
        self.request.form.update({"userid": self.context.getId()})
        login_view = api.content.get_view("login", api.portal.get(), self.request)
        with api.env.adopt_user(self.context.getId()):
            is_initial_login = login_view._post_login()
        came_from = self.token_storage.get("came_from")
        login_view.redirect_after_login(came_from, is_initial_login)

    def publishTraverse(self, request, name):
        """Traverse to the next view"""
        request.set("token", name)
        return self

    def verify_token(self):
        """Verify the token"""
        token_storage = self.token_storage
        if not token_storage:
            return False
        token = token_storage.get("token")
        if not token:
            return False
        if token_storage.get("valid_until") < localized_now():
            return False
        return token == self.request.get("token")

    def login_user(self):
        """Login the user"""
        membership_tool = api.portal.get_tool("portal_membership")
        with api.env.adopt_user(username=self.context.username):
            membership_tool.loginUser(self.request)

    def __call__(self):
        if not self.verify_token():
            return self.redirect(as_logged_in=False)

        self.login_user()
        self.redirect(as_logged_in=True)
        # Finally clear the token
        self.clear_token()
