# -*- coding: utf-8 -*-
import logging

from odoo import _, api, fields, models
from odoo.tools import email_normalize, html_escape

from .mail_sync_server import _extract_email_addresses

_logger = logging.getLogger(__name__)


class MailSyncMessage(models.Model):
    _name = 'mail.sync.message'
    _description = 'Synced Incoming Mail Message'
    _order = 'date_received desc, id desc'
    _rec_name = 'subject'

    server_id = fields.Many2one(
        'mail.sync.server',
        string='Mail Server',
        required=True,
        ondelete='cascade',
        index=True,
    )
    message_id = fields.Char(
        string='Message-ID',
        index=True,
        help='RFC Message-ID header used for deduplication.',
    )
    uid = fields.Integer(
        string='IMAP UID',
        required=True,
        index=True,
        help='IMAP UID on the mail server.',
    )
    email_from = fields.Char(string='From', index=True)
    email_to = fields.Char(string='To')
    email_cc = fields.Char(string='Cc')
    email_bcc = fields.Char(string='Bcc')
    subject = fields.Char(string='Subject', index=True)
    body_html = fields.Html(string='Body (HTML)', sanitize=True)
    body_text = fields.Text(string='Body (Text)')
    date_received = fields.Datetime(string='Received', index=True)
    attachment_ids = fields.Many2many(
        'ir.attachment',
        'mail_sync_message_attachment_rel',
        'message_id',
        'attachment_id',
        string='Attachments',
    )
    partner_ids = fields.Many2many(
        'res.partner',
        'mail_sync_message_partner_rel',
        'message_id',
        'partner_id',
        string='Matched Contacts',
        help='Contacts that received a log note for this message.',
    )
    mail_message_ids = fields.Many2many(
        'mail.message',
        'mail_sync_message_mail_message_rel',
        'sync_message_id',
        'mail_message_id',
        string='Posted Log Notes',
        readonly=True,
    )
    is_routed = fields.Boolean(
        string='Routed to Contact',
        default=False,
        index=True,
        readonly=True,
    )
    routed_date = fields.Datetime(string='Routed On', readonly=True)
    state = fields.Selection(
        [
            ('new', 'New'),
            ('read', 'Read'),
            ('archived', 'Archived'),
        ],
        string='Status',
        default='new',
        required=True,
        index=True,
    )
    raw_headers = fields.Text(string='Raw Headers')
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        related='server_id.company_id',
        store=True,
        readonly=True,
    )

    _sql_constraints = [
        (
            'unique_server_uid',
            'UNIQUE(server_id, uid)',
            'This IMAP UID already exists for this mail server.',
        ),
        (
            'unique_server_message_id',
            'UNIQUE(server_id, message_id)',
            'This Message-ID already exists for this mail server.',
        ),
    ]

    def _extract_participant_emails(self):
        """Collect normalized participant emails from From/To/Cc/Bcc headers."""
        self.ensure_one()
        emails = set()
        for header in (self.email_from, self.email_to, self.email_cc, self.email_bcc):
            emails.update(_extract_email_addresses(header))
        mailbox = (self.server_id.user or '').strip().lower()
        if mailbox:
            emails.discard(mailbox)
            normalized_mailbox = email_normalize(mailbox)
            if normalized_mailbox:
                emails.discard(normalized_mailbox)
        return list(emails)

    def _find_partners_for_emails(self, emails):
        """Return contacts matching any of the given email addresses."""
        Partner = self.env['res.partner']
        partners = Partner.browse()
        seen = set()
        for addr in emails:
            normalized = email_normalize(addr)
            if not normalized or normalized in seen:
                continue
            seen.add(normalized)
            found = Partner.search([
                '|',
                ('email_normalized', '=', normalized),
                ('email', '=ilike', addr),
            ])
            partners |= found
        return partners

    def _build_log_note_body(self):
        """Build HTML body for the internal log note on a contact."""
        self.ensure_one()
        base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url', '')
        link = '%s/web#id=%s&model=mail.sync.message&view_type=form' % (base_url, self.id)
        received = fields.Datetime.to_string(self.date_received) if self.date_received else ''
        meta = (
            '<p><strong>%s</strong> %s</p>'
            '<p><strong>%s</strong> %s</p>'
            '<p><strong>%s</strong> %s</p>'
            '<p><strong>%s</strong> %s</p>'
            '<p><strong>%s</strong> %s</p>'
            '<hr/>'
        ) % (
            _('From:'), self.email_from or '',
            _('To:'), self.email_to or '',
            _('Cc:'), self.email_cc or '',
            _('Received:'), received,
            _('Subject:'), self.subject or '',
        )
        if self.body_html:
            body = self.body_html
        elif self.body_text:
            body = '<pre>%s</pre>' % html_escape(self.body_text)
        else:
            body = ''
        footer = '<p><em>%s <a href="%s">Mail Inbox #%s</a></em></p>' % (
            _('Source:'), link, self.id,
        )
        return meta + body + footer

    def _copy_attachments_for_post(self):
        """Duplicate attachments so they can be linked to chatter messages."""
        attachment_ids = []
        for attachment in self.attachment_ids:
            copied = attachment.copy({
                'res_model': False,
                'res_id': False,
            })
            attachment_ids.append(copied.id)
        return attachment_ids

    def _post_log_note_to_partner(self, partner):
        """Post one internal log note on a contact; idempotent per partner."""
        self.ensure_one()
        if partner in self.partner_ids:
            return False
        attachment_ids = self._copy_attachments_for_post()
        posted = partner.message_post(
            body=self._build_log_note_body(),
            subject='[Inbox] %s' % (self.subject or ''),
            message_type='comment',
            subtype_xmlid='mail.mt_note',
            attachment_ids=attachment_ids,
        )
        mail_message = self.env['mail.message'].browse(posted) if isinstance(posted, int) else posted
        self.write({
            'partner_ids': [(4, partner.id)],
            'mail_message_ids': [(4, mail_message.id)],
        })
        return True

    def _route_all(self):
        """Route synced mail to all configured targets (contacts, CRM, etc.)."""
        self._route_to_contacts()

    def _route_to_contacts(self):
        """Match participant emails to contacts and post log notes."""
        for message in self:
            emails = message._extract_participant_emails()
            if not emails:
                continue
            partners = message._find_partners_for_emails(emails)
            if not partners:
                continue
            posted_any = False
            for partner in partners:
                try:
                    if message._post_log_note_to_partner(partner):
                        posted_any = True
                except Exception:
                    _logger.exception(
                        'Failed to post log note for sync message %s to partner %s',
                        message.id, partner.id,
                    )
            if posted_any:
                message.write({
                    'is_routed': True,
                    'routed_date': fields.Datetime.now(),
                })

    def action_route_contacts(self):
        """Manual action: route selected messages to contacts and CRM."""
        self._route_all()
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Routing Complete'),
                'message': _('Processed %s message(s) for routing.') % len(self),
                'type': 'success',
                'sticky': False,
            },
        }

    @api.model
    def action_route_all_unrouted(self):
        """Backfill: route messages not yet linked to a contact or CRM lead."""
        unrouted = self.search([
            '|',
            ('is_routed', '=', False),
            ('is_routed_lead', '=', False),
        ])
        unrouted._route_all()
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Backfill Complete'),
                'message': _('Routed %s message(s).') % len(unrouted),
                'type': 'success',
                'sticky': False,
            },
        }

    @api.model
    def mark_as_read(self):
        """Mark selected messages as read."""
        self.filtered(lambda m: m.state == 'new').write({'state': 'read'})

    def action_archive(self):
        self.write({'state': 'archived'})

    def action_mark_read(self):
        self.write({'state': 'read'})

    def action_mark_new(self):
        self.write({'state': 'new'})
