from AccessControl import Unauthorized
from collective.workspace.interfaces import IWorkspace
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from logging import getLogger
from plone import api
from plone.memoize.view import memoize
from ploneintranet import api as pi_api
from ploneintranet.core import _
from ploneintranet.layout.browser.base import BasePanel
from ploneintranet.layout.browser.base import BaseView
from Products.Five import BrowserView
from zope.i18n import translate
from zope.interface import implementer
from zope.publisher.interfaces import IPublishTraverse

logger = getLogger(__name__)


class RequestAccessBaseEmail(BrowserView):
    """Helper view that renders an email for the workspace admins"""

    translatable_subject = ""

    def translate(self, message):
        """Translate an i18n message"""
        return translate(message, target_language=api.portal.get_current_language())

    @property
    @memoize
    def subject(self):
        return self.translate(self.translatable_subject)

    @property
    @memoize
    def user(self):
        return pi_api.userprofile.get_current()

    @property
    @memoize
    def recipient(self):
        return self.user.display_name_email

    def send_email(self):
        message = MIMEMultipart()
        body = self.index()
        message.attach(MIMEText(body.encode(), "html", _charset="utf8"))
        api.portal.send_email(
            recipient=self.recipient,
            subject=self.subject,
            body=message,
            immediate=False,
        )

    def __call__(self):
        return self


class RequestAccessReceiptEmail(RequestAccessBaseEmail):
    """This is email is sent as a receipt to the user who requested access
    to a private workspace
    """

    translatable_subject = _("Workspace access request has been sent")


class RequestAccessRequestEmail(RequestAccessBaseEmail):
    """This is the mail that contains the actual request"""

    @property
    def translatable_subject(self):
        # i18n INFO: This mail subject is intended for a admin person.
        return _(
            "subject_request_access_request",
            default="${fullname} requests access to workspace ${title}",
            mapping={
                "fullname": self.user.fullname or self.user.getId(),
                "title": self.context.title,
            },
        )

    def _mailsfromprincipalid(self, principalid):
        profiles = api.portal.get().profiles
        groups = api.portal.get().groups
        if principalid in profiles:
            mail = profiles[principalid].display_name_email
            if mail:
                yield mail
        elif principalid in groups:
            for memberid in groups[principalid].members:
                for mail in self._mailsfromprincipalid(memberid):
                    yield mail

    @property
    @memoize
    def recipient(self):
        admins_group = api.group.get("Admins:%s" % self.context.UID())
        mails = set()
        for principalid in admins_group.getMemberIds():
            mails.update(set(self._mailsfromprincipalid(principalid)))
        return sorted(mails)


class RequestAccessApprovalEmail(RequestAccessBaseEmail):
    """This is the mail received after the request has been approved"""

    translatable_subject = _("Workspace access request has been approved")

    @property
    @memoize
    def recipient(self):
        return self.request.form["recipient"]


class RequestAccessDenialEmail(RequestAccessBaseEmail):
    """This is the mail received after the request has been denied"""

    translatable_subject = _("Workspace access request has been denied")

    @property
    @memoize
    def recipient(self):
        return self.request.form["recipient"]


class RequestAccessPanel(BasePanel):
    """Panel to request access to secret workspaces that are marked as
    visible_to_intranet_users
    """

    _form_data_pat_inject_parts = ("#global-statusmessage; loading-class: ''",)
    panel_size = "medium"

    @property
    @memoize
    def admin_email(self):
        """Returns the system administrator email"""
        address = api.portal.get_registry_record("plone.email_from_address")
        if not address:
            return ""
        name = api.portal.get_registry_record("plone.email_from_name") or _(
            "System administrator"
        )
        return f"{name} <{address}>"

    def is_authorized(self):
        """Check if this user is authorized to request access"""
        return (
            api.content.get_state(self.context) == "secret"
            and self.context.visible_to_intranet_users
            and not api.user.has_permission("View", obj=self.context)
        )

    def request_access(self):
        errors = []
        for mail in ("request-access-request-email", "request-access-receipt-email"):
            try:
                view = api.content.get_view(mail, self.context, self.request)
                view.send_email()
            except Exception as e:
                logger.exception("Error sending the email")
                errors.append(mail)
                api.portal.show_message(str(e), self.request, "error")
        if not errors:
            msg = translate(
                _(
                    "msg_request_access_success",
                    default="A request for access for workspace ${title} "
                    "has been issued to the workspace administrators",
                    mapping={"title": self.context.title},
                ),
                target_language=api.portal.get_current_language(),
            )
            api.portal.show_message(msg, self.request, "success")
        self.redirect(self.context.aq_parent.absolute_url())

    def __call__(self):
        if not self.is_authorized():
            raise Unauthorized("You are not allowed to access this content")

        if self.is_posting():
            return self.request_access()
        return self.index()


@implementer(IPublishTraverse)
class RequestAccessManage(BaseView):
    form_method = "post"
    form_data_pat_inject = None
    panel_size = "medium"
    show_default_cancel_button = False
    userid = ""
    additional_body_classes = "modal-panel"

    @property
    def form_action(self):
        return self.request.getURL()

    @property
    @memoize
    def user(self):
        return api.portal.get().profiles[self.userid]

    @property
    def user_link(self):
        return '<a class="pat-inject" href="{url}#main">{fullname}</a>'.format(
            url=self.user.absolute_url(), fullname=self.user.fullname
        )

    def publishTraverse(self, request, name):
        self.userid = name
        return self

    def decline_access(self):
        """Decline access to the user"""
        self.request.form["recipient"] = self.user.display_name_email
        try:
            view = api.content.get_view(
                "request-access-denial-email", self.context, self.request
            )
            view.send_email()
        except Exception as e:
            logger.exception("Error sending the email")
            api.portal.show_message(str(e), self.request, "error")
        msg = _(
            "request_access_denied",
            default="${user_link} request to access workspace ${title} was declined",
            mapping={"user_link": self.user_link, "title": self.context.title},
        )
        self.redirect(msg=msg, msg_type="banner")

    def grant_access(self):
        """Grant access to the user"""
        IWorkspace(self.context).add_to_team(self.user.getId())
        self.request.form["recipient"] = self.user.display_name_email
        try:
            view = api.content.get_view(
                "request-access-approval-email", self.context, self.request
            )
            view.send_email()
        except Exception as e:
            logger.exception("Error sending the email")
            api.portal.show_message(str(e), self.request, "error")
        msg = _(
            "request_access_granted",
            default="${user_link} has now access to workspace ${title}",
            mapping={"user_link": self.user_link, "title": self.context.title},
        )
        self.redirect(msg=msg, msg_type="banner")

    def __call__(self):
        if not self.userid:
            msg = _("Invalid request")
            return self.redirect(msg=msg)
        if self.is_posting():
            if "form.buttons.decline" in self.request.form:
                return self.decline_access()
            elif "form.buttons.accept" in self.request.form:
                return self.grant_access()
            else:
                msg = _("An unknown action has been requested")
                return self.redirect(msg=msg)
        return super().__call__()
