'Attempt to figure out the correct target model, thread_id,
custom_values and user_id to use for an incoming message.
Multiple values may be returned, if a message had multiple
recipients matching existing mail.aliases, for example.
The following heuristics are used, in this order:
* if the message replies to an existing thread by having a Message-Id
that matches an existing mail_message.message_id, we take the original
message model/thread_id pair and ignore custom_value as no creation will
take place
* if the message replies to an existing thread by having In-Reply-To or
References matching odoo model/thread_id Message-Id and if this thread
has messages without message_id, take this model/thread_id pair and
ignore custom_value as no creation will take place (6.1 compatibility)
* look for a mail.alias entry matching the message recipients and use the
corresponding model, thread_id, custom_values and user_id. This could
lead to a thread update or creation depending on the alias
* fallback on provided ``model``, ``thread_id`` and ``custom_values``
* raise an exception as no route has been found
:param string message: an email.message instance
:param dict message_dict: dictionary holding parsed message variables
:param string model: the fallback model to use if the message does not match
any of the currently configured mail aliases (may be None if a matching
alias is supposed to be present)
:type dict custom_values: optional dictionary of default field values
to pass to ``message_new`` if a new record needs to be created.
Ignored if the thread record already exists, and also if a matching
mail.alias was found (aliases define their own defaults)
:param int thread_id: optional ID of the record/thread from ``model`` to
which this mail should be attached. Only used if the message does not
reply to an existing thread and does not match any mail alias.
:return: list of routes [(model, thread_id, custom_values, user_id, alias)]
:raises: ValueError, TypeError'

@api.model
def message_route(self, message, message_dict, model=None, thread_id=None, custom_values=None):

    if (not isinstance(message, Message)):
        raise TypeError('message    must    be    an    email.message.Message    at    this    point')
    MailMessage = self.env['mail.message']
    (Alias, dest_aliases) = (self.env['mail.alias'], self.env['mail.alias'])
    bounce_alias = self.env['ir.config_parameter'].get_param('mail.bounce.alias')
    fallback_model = model
    local_hostname = socket.gethostname()
    message_id = message.get('Message-Id')
    references = tools.decode_message_header(message, 'References')
    in_reply_to = tools.decode_message_header(message, 'In-Reply-To').strip()
    thread_references = (references or in_reply_to)
    (reply_match, reply_model, reply_thread_id, reply_hostname, reply_private) = tools.email_references(thread_references)
    email_from = tools.decode_message_header(message, 'From')
    email_from_localpart = (tools.email_split(email_from) or [''])[0].split('@', 1)[0].lower()
    email_to = tools.decode_message_header(message, 'To')
    email_to_localpart = (tools.email_split(email_to) or [''])[0].split('@', 1)[0].lower()
    rcpt_tos = ','.join([tools.decode_message_header(message, 'Delivered-To'), tools.decode_message_header(message, 'To'), tools.decode_message_header(message, 'Cc'), tools.decode_message_header(message, 'Resent-To'), tools.decode_message_header(message, 'Resent-Cc')])
    rcpt_tos_localparts = [e.split('@')[0].lower() for e in tools.email_split(rcpt_tos)]
    if (bounce_alias and (bounce_alias in email_to_localpart)):
        bounce_re = re.compile(('%s\\+(\\d+)-?([\\w.]+)?-?(\\d+)?' % re.escape(bounce_alias)), re.UNICODE)
        bounce_match = bounce_re.search(email_to)
        if bounce_match:
            (bounced_mail_id, bounced_model, bounced_thread_id) = (bounce_match.group(1), bounce_match.group(2), bounce_match.group(3))
            email_part = next((part for part in message.walk() if (part.get_content_type() == 'message/rfc822')), None)
            dsn_part = next((part for part in message.walk() if (part.get_content_type() == 'message/delivery-status')), None)
            (partners, partner_address) = (self.env['res.partner'], False)
            if (dsn_part and (len(dsn_part.get_payload()) > 1)):
                dsn = dsn_part.get_payload()[1]
                final_recipient_data = tools.decode_message_header(dsn, 'Final-Recipient')
                partner_address = final_recipient_data.split(';', 1)[1].strip()
                if partner_address:
                    partners = partners.sudo().search([('email', 'like', partner_address)])
                    for partner in partners:
                        partner.message_receive_bounce(partner_address, partner, mail_id=bounced_mail_id)
            mail_message = self.env['mail.message']
            if email_part:
                email = email_part.get_payload()[0]
                bounced_message_id = tools.mail_header_msgid_re.findall(tools.decode_message_header(email, 'Message-Id'))
                mail_message = MailMessage.sudo().search([('message_id', 'in', bounced_message_id)])
            if (partners and mail_message):
                notifications = self.env['mail.notification'].sudo().search([('mail_message_id', '=', mail_message.id), ('res_partner_id', 'in', partners.ids)])
                notifications.write({'email_status': 'bounce'})
            if ((bounced_model in self.env) and hasattr(self.env[bounced_model], 'message_receive_bounce') and bounced_thread_id):
                self.env[bounced_model].browse(int(bounced_thread_id)).message_receive_bounce(partner_address, partners, mail_id=bounced_mail_id)
            _logger.info('Routing    mail    from    %s    to    %s    with    Message-Id    %s:    bounced    mail    from    mail    %s,    model:    %s,    thread_id:    %s:    dest    %s    (partner    %s)', email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id, partner_address, partners)
            return []
    if ((message.get_content_type() == 'multipart/report') or (email_from_localpart == 'mailer-daemon')):
        _logger.info('Routing    mail    with    Message-Id    %s:    not    routing    bounce    email    from    %s    to    %s', message_id, email_from, email_to)
        return []
    msg_references = [ref for ref in tools.mail_header_msgid_re.findall(thread_references) if ('reply_to' not in ref)]
    mail_messages = MailMessage.sudo().search([('message_id', 'in', msg_references)], limit=1)
    is_a_reply = bool(mail_messages)
    if (reply_model and reply_thread_id):
        other_alias = Alias.search(['&', ('alias_name', '!=', False), ('alias_name', '=', email_to_localpart)])
        if (other_alias and (other_alias.alias_model_id.model != reply_model)):
            is_a_reply = False
    if is_a_reply:
        (model, thread_id) = (mail_messages.model, mail_messages.res_id)
        if (not reply_private):
            dest_aliases = Alias.search([('alias_name', 'in', rcpt_tos_localparts)], limit=1)
        route = self.message_route_verify(message, message_dict, (model, thread_id, custom_values, self._uid, dest_aliases), update_author=True, assert_model=reply_private, create_fallback=True, allow_private=reply_private, drop_alias=True)
        if route:
            _logger.info('Routing    mail    from    %s    to    %s    with    Message-Id    %s:    direct    reply    to    msg:    model:    %s,    thread_id:    %s,    custom_values:    %s,    uid:    %s', email_from, email_to, message_id, model, thread_id, custom_values, self._uid)
            return [route]
        elif (route is False):
            return []
    if rcpt_tos_localparts:
        message_dict.pop('parent_id', None)
        dest_aliases = Alias.search([('alias_name', 'in', rcpt_tos_localparts)])
        if dest_aliases:
            routes = []
            for alias in dest_aliases:
                user_id = alias.alias_user_id.id
                if (not user_id):
                    user_id = self._uid
                    _logger.info('No    matching    user_id    for    the    alias    %s', alias.alias_name)
                route = (alias.alias_model_id.model, alias.alias_force_thread_id, safe_eval(alias.alias_defaults), user_id, alias)
                route = self.message_route_verify(message, message_dict, route, update_author=True, assert_model=True, create_fallback=True)
                if route:
                    _logger.info('Routing    mail    from    %s    to    %s    with    Message-Id    %s:    direct    alias    match:    %r', email_from, email_to, message_id, route)
                    routes.append(route)
            return routes
    if fallback_model:
        message_dict.pop('parent_id', None)
        route = self.message_route_verify(message, message_dict, (fallback_model, thread_id, custom_values, self._uid, None), update_author=True, assert_model=True)
        if route:
            _logger.info('Routing    mail    from    %s    to    %s    with    Message-Id    %s:    fallback    to    model:%s,    thread_id:%s,    custom_values:%s,    uid:%s', email_from, email_to, message_id, fallback_model, thread_id, custom_values, self._uid)
            return [route]
    raise ValueError(('No    possible    route    found    for    incoming    message    from    %s    to    %s    (Message-Id    %s:).    Create    an    appropriate    mail.alias    or    force    the    destination    model.' % (email_from, email_to, message_id)))
