# -*- coding: utf-8 -*-
# TODO
# pylint: disable=old-api7-method-defined,invalid-commit
import base64
import logging
import re
from email.utils import formataddr

from openerp import SUPERUSER_ID, tools
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException
from openerp.osv import osv
from openerp.tools.safe_eval import safe_eval
from openerp.tools.translate import _

_logger = logging.getLogger(__name__)


class MailMail(osv.Model):
    _inherit = "mail.mail"

    def send(
        self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None
    ):
        # copy-paste from addons/mail/mail_mail.py
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """

        # NEW STUFF
        catchall_alias = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.alias_from", context=context
        )
        catchall_alias_name = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.name_alias_from", context=context
        )
        catchall_domain = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.domain", context=context
        )

        correct_email_from = r"@%s>?\s*$" % catchall_domain
        default_email_from = "{}@{}".format(catchall_alias, catchall_domain)

        context = dict(context or {})
        ir_mail_server = self.pool.get("ir.mail_server")
        ir_attachment = self.pool["ir.attachment"]
        for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
            try:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model_id = self.pool["ir.model"].search(
                        cr, SUPERUSER_ID, [("model", "=", mail.model)], context=context
                    )[0]
                    model = self.pool["ir.model"].browse(
                        cr, SUPERUSER_ID, model_id, context=context
                    )
                else:
                    model = None
                if model:
                    context["model_name"] = model.name

                # load attachment binary data with a separate read(), as prefetching all
                # `datas` (binary field) could bloat the browse cache, triggerring
                # soft/hard mem limits with temporary data.
                attachment_ids = [a.id for a in mail.attachment_ids]
                attachments = [
                    (a["datas_fname"], base64.b64decode(a["datas"]))
                    for a in ir_attachment.read(
                        cr, SUPERUSER_ID, attachment_ids, ["datas_fname", "datas"]
                    )
                ]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(
                        self.send_get_email_dict(cr, uid, mail, context=context)
                    )
                for partner in mail.recipient_ids:
                    email_list.append(
                        self.send_get_email_dict(
                            cr, uid, mail, partner=partner, context=context
                        )
                    )
                # headers
                headers = {}
                bounce_alias = self.pool["ir.config_parameter"].get_param(
                    cr, uid, "mail.bounce.alias", context=context
                )
                catchall_domain = self.pool["ir.config_parameter"].get_param(
                    cr, uid, "mail.catchall.domain", context=context
                )
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers["Return-Path"] = "%s-%d-%s-%d@%s" % (
                            bounce_alias,
                            mail.id,
                            mail.model,
                            mail.res_id,
                            catchall_domain,
                        )
                    else:
                        headers["Return-Path"] = "%s-%d@%s" % (
                            bounce_alias,
                            mail.id,
                            catchall_domain,
                        )
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({"state": "exception"})
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:

                    # NEW STUFF
                    email_from = mail.email_from
                    if re.search(correct_email_from, email_from) is None:
                        email_from = default_email_from
                    if catchall_alias_name:
                        email_from = formataddr((catchall_alias_name, email_from))

                    msg = ir_mail_server.build_email(
                        email_from=email_from,  # NEW STUFF
                        email_to=email.get("email_to"),
                        subject=email.get("subject"),
                        body=email.get("body"),
                        body_alternative=email.get("body_alternative"),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ("{}-{}".format(mail.res_id, mail.model)),
                        subtype="html",
                        subtype_alternative="plain",
                        headers=headers,
                    )
                    try:
                        res = ir_mail_server.send_email(
                            cr,
                            uid,
                            msg,
                            mail_server_id=mail.mail_server_id.id,
                            context=context,
                        )
                    except AssertionError as error:
                        if str(error) == ir_mail_server.NO_VALID_RECIPIENT:
                            # No valid recipient found for this particular
                            # mail item -> ignore error to avoid blocking
                            # delivery to next recipients, if any. If this is
                            # the only recipient, the mail will show as failed.
                            _logger.warning(
                                "Ignoring invalid recipients for mail.mail %s: %s",
                                mail.message_id,
                                email.get("email_to"),
                            )
                        else:
                            raise
                if res:
                    mail.write({"state": "sent", "message_id": res})
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
                if mail_sent:
                    _logger.info(
                        "Mail with ID %r and Message-Id %r successfully sent",
                        mail.id,
                        mail.message_id,
                    )
                self._postprocess_sent_message(
                    cr, uid, mail, context=context, mail_sent=mail_sent
                )
            except MemoryError:
                # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
                # instead of marking the mail as failed
                _logger.exception(
                    "MemoryError while processing mail with ID %r and Msg-Id %r. "
                    "Consider raising the --limit-memory-hard startup option",
                    mail.id,
                    mail.message_id,
                )
                raise
            except Exception as e:
                _logger.exception("failed sending mail.mail %s", mail.id)
                mail.write({"state": "exception"})
                self._postprocess_sent_message(
                    cr, uid, mail, context=context, mail_sent=False
                )
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = ". ".join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"), value)
                    raise

            if auto_commit is True:
                cr.commit()
        return True