from AccessControl import Unauthorized
from base64 import encodebytes
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from plone import api
from plone.app.contenttypes.interfaces import IFile
from plone.app.contenttypes.interfaces import IImage
from plone.memoize.view import memoize
from ploneintranet import api as pi_api
from ploneintranet.core import _
from ploneintranet.userprofile.content.userprofile import IUserProfile
from ploneintranet.workspace.browser.cart_actions.base import BaseCartView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.MailHost.MailHost import MailHostError
from zope.deprecation import deprecate

import logging

log = logging.getLogger(__name__)


class MailView(BaseCartView):
    title = _("Batch send")
    form_data_pat_inject = None
    form_data_pat_validation = "disable-selector: #form-buttons-send"

    def confirm(self):
        index = ViewPageTemplateFile("templates/mail_send_confirmation.pt")
        return index(self)

    @property
    @memoize
    @deprecate("Use the `items` property instead of calling the `cart_objs` method")
    def cart_objs(self):
        """The objects we have on the cart"""
        return self.items

    @property
    @memoize
    def message(self):
        """Return the message we want to send"""
        message = self.request.form.get("message", "")
        message += "\n\n"
        message += api.portal.translate(
            _("The following items have been shared with you:")
        )
        for obj in self.items:
            message += f"\n* {obj.absolute_url()}/view"
        return message

    @property
    @memoize
    def recipient_userprofiles(self):
        """The list of userprofiles that will receive the email"""
        recipients = self.request.form.get("recipients", "")
        if not recipients:
            return set()

        pas_view = api.content.get_view("pas_view", self.context, self.request)
        uids = set(
            filter(
                None,
                (pas_view.principalname2uid(name) for name in recipients.split(",")),
            )
        )
        uids |= pas_view.successors_for_uids(uids)

        userprofiles = set()
        for uid in uids:
            try:
                userprofile = pas_view.resolvepasuid(uid)
            except Unauthorized:
                continue
            if IUserProfile.providedBy(userprofile) and userprofile.display_name_email:
                userprofiles.add(userprofile)
        return userprofiles

    def send_receipt(self):
        """Send a receipt to the user when bulk sending is successful"""
        translate = api.portal.translate
        recipient = pi_api.userprofile.get_current().display_name_email
        if not recipient:
            return
        subject = _(
            "You have just sent out the following email from ${site}",
            mapping={"site": api.portal.get().Title()},
        )
        text = _(
            "msg_batch_send_receipt_txt",
            default="You have just sent out the following email to these users:\n\n - ${users}\n\n${message}",  # noqa: E501
            mapping={
                "users": "\n - ".join(
                    sorted(r.fullname for r in self.recipient_userprofiles)
                ),
                "message": self.message,
            },
        )
        api.portal.send_email(
            subject=translate(subject), body=translate(text), recipient=recipient
        )

    def send(self):
        isValidEmail = api.portal.get_tool("portal_registration").isValidEmail

        # Extract from the request the list of selected valid email addresses
        recipient_addresses = {
            email.lower()
            for email in self.request.form.get("recipients", "").split(",")
            if email and isValidEmail(email)
        }

        # Get the recipients we requested to notify
        recipients = self.recipient_userprofiles

        # Remove duplications
        for recipient in recipients:
            email = (recipient.email or "").lower()
            if email in recipient_addresses:
                recipient_addresses.remove(email)
            recipient_addresses.add(recipient.display_name_email)

        if not recipient_addresses:
            # Should we show an error notification?
            log.debug("No recipients were selected, no email has been sent.")
            return

        msg = MIMEMultipart()

        reply_to_address = pi_api.userprofile.get_current().display_name_email
        msg["Reply-To"] = Header(reply_to_address, "utf-8")

        to_addresses = ", ".join(recipient_addresses)
        msg["To"] = Header(to_addresses, "utf-8")

        subject = api.portal.translate(_("Some items have been sent to you."))
        msg["Subject"] = Header(subject, "utf-8")

        body = MIMEText(self.message, "plain", "utf-8")
        msg.attach(body)

        for obj in self.attachable_objs():
            file_obj = None
            if hasattr(obj, "file"):
                file_obj = obj.file
            elif hasattr(obj, "image"):
                file_obj = obj.image
            if file_obj is None:
                continue
            att = MIMEBase(*file_obj.contentType.split("/"))
            att.set_payload(encodebytes(file_obj.data))
            att.add_header("Content-Transfer-Encoding", "base64")
            att.add_header(
                "Content-Disposition", "attachment", filename=file_obj.filename
            )
            msg.attach(att)

        try:
            api.portal.send_email(
                recipient=to_addresses,
                subject=subject,
                body=msg.as_string(),
            )
            mail_sent = True
        except MailHostError as e:
            mail_sent = False
            log.error(e.message)

        if mail_sent:
            api.portal.show_message(
                message=_("Email sent."), request=self.request, type="success"
            )
            self.send_receipt()
        else:
            api.portal.show_message(
                message=_("Error sending the email."),
                request=self.request,
                type="error",
            )
        return self.redirect()

    def attachable_objs(self):
        """
        We can only attach files. When implemented, we will also attach the PDF
        version of Documents
        """
        objs = []
        for obj in self.items:
            if IFile.providedBy(obj) or IImage.providedBy(obj):
                objs.append(obj)
        return objs

    def get_previews(self, obj):
        if IImage.providedBy(obj):
            return {
                "class": "",
                "url": f"{obj.absolute_url()}/@@images/image",
                "page_count": 1,
                "has_preview": True,
            }

        preview_details = {"class": "not-generated", "has_preview": False}
        if not pi_api.previews.is_allowed_document_type(obj):
            preview_details["class"] = "not-possible"
        elif pi_api.previews.has_previews(obj):
            preview_urls = pi_api.previews.get_preview_urls(obj)
            if preview_urls:
                preview_details = {
                    "class": "",
                    "url": preview_urls[0],
                    "page_count": len(preview_urls),
                    "has_preview": True,
                }
        return preview_details
