import logging

from odoo import _, api, fields, models

_logger = logging.getLogger(__name__)


class EncoachTicket(models.Model):
    _name = 'encoach.ticket'
    _description = 'Support Ticket'
    _inherit = ['mail.thread']
    _order = 'id desc'

    subject = fields.Char('Subject', required=True, tracking=True)
    description = fields.Text('Description')
    type = fields.Selection([
        ('bug', 'Bug'),
        ('feature', 'Feature'),
        ('help', 'Help'),
        ('support', 'Support'),
        ('feedback', 'Feedback'),
    ], string='Type', default='support', required=True, tracking=True)
    status = fields.Selection([
        ('open', 'Open'),
        ('in_progress', 'In Progress'),
        ('resolved', 'Resolved'),
        ('closed', 'Closed'),
    ], string='Status', default='open', required=True, tracking=True)
    source = fields.Selection([
        ('platform', 'Platform'),
        ('webpage', 'Webpage'),
        ('email', 'Email'),
    ], string='Source', default='platform')

    page_context = fields.Char('Page Context')
    reporter_id = fields.Many2one('res.users', string='Reporter', required=True,
                                  default=lambda self: self.env.user.id)
    assignee_id = fields.Many2one('res.users', string='Assignee')
    entity_id = fields.Many2one('encoach.entity', string='Entity')

    resolved_at = fields.Datetime('Resolved At')

    STATUS_LABELS = {
        'open': 'Open',
        'in_progress': 'In Progress',
        'resolved': 'Resolved',
        'closed': 'Closed',
    }

    def _notify_ticket_event(self, event, *, old_status=None, new_status=None,
                             old_assignee=None, new_assignee=None):
        """Post a chatter message and push a bus event for status/assignee changes.

        Uses :class:`bus.bus` when available so both the reporter and the new
        assignee see near real-time updates in the frontend, and always falls
        back to ``mail.thread`` chatter so the change is auditable even when
        the bus is unavailable (e.g. during cron jobs or tests).
        """
        self.ensure_one()
        try:
            if event == 'status':
                body = _("Status: %s → %s") % (
                    self.STATUS_LABELS.get(old_status, old_status or '—'),
                    self.STATUS_LABELS.get(new_status, new_status or '—'),
                )
            elif event == 'assignee':
                old_name = old_assignee.display_name if old_assignee else _('Unassigned')
                new_name = new_assignee.display_name if new_assignee else _('Unassigned')
                body = _("Assignee: %s → %s") % (old_name, new_name)
            else:
                return
            self.message_post(body=body, subtype_xmlid='mail.mt_note')
        except Exception:
            _logger.exception("ticket %s: chatter notify failed", self.id)

        try:
            bus = self.env['bus.bus'].sudo()
            recipients = set()
            if self.reporter_id:
                recipients.add(('res.partner', self.reporter_id.partner_id.id))
            for user in (old_assignee, new_assignee, self.assignee_id):
                if user and user.partner_id:
                    recipients.add(('res.partner', user.partner_id.id))
            payload = {
                'type': 'encoach.ticket',
                'event': event,
                'ticket_id': self.id,
                'subject': self.subject,
                'status': self.status,
                'assignee_id': self.assignee_id.id or False,
                'old_status': old_status,
                'new_status': new_status,
            }
            for channel in recipients:
                bus._sendone(channel, 'encoach.ticket/notify', payload)
        except Exception:
            _logger.debug("ticket %s: bus push skipped", self.id, exc_info=True)

    def write(self, vals):
        tracked = {}
        if vals.get('status') or vals.get('assignee_id') is not None:
            for rec in self:
                tracked[rec.id] = {
                    'status': rec.status,
                    'assignee': rec.assignee_id,
                }
        res = super().write(vals)
        if not tracked:
            return res

        now = fields.Datetime.now()
        for rec in self:
            before = tracked.get(rec.id) or {}
            old_status = before.get('status')
            old_assignee = before.get('assignee')
            if 'status' in vals and old_status != rec.status:
                if rec.status in ('resolved', 'closed') and not rec.resolved_at:
                    rec.resolved_at = now
                rec._notify_ticket_event(
                    'status', old_status=old_status, new_status=rec.status,
                )
            if 'assignee_id' in vals and (old_assignee or rec.assignee_id) and \
                    (old_assignee.id if old_assignee else False) != (rec.assignee_id.id or False):
                rec._notify_ticket_event(
                    'assignee', old_assignee=old_assignee, new_assignee=rec.assignee_id,
                )
        return res

    @api.model_create_multi
    def create(self, vals_list):
        records = super().create(vals_list)
        for rec in records:
            try:
                rec.message_post(
                    body=_("Ticket opened by %s") % rec.reporter_id.display_name,
                    subtype_xmlid='mail.mt_note',
                )
            except Exception:
                _logger.debug("ticket %s: initial chatter failed", rec.id, exc_info=True)
        return records

    @api.model
    def _auto_init(self):
        res = super()._auto_init()
        cr = self.env.cr
        for name, ddl in (
            (
                'encoach_ticket_entity_status_idx',
                "CREATE INDEX IF NOT EXISTS encoach_ticket_entity_status_idx "
                "ON encoach_ticket (entity_id, status) WHERE entity_id IS NOT NULL",
            ),
            (
                'encoach_ticket_reporter_status_idx',
                "CREATE INDEX IF NOT EXISTS encoach_ticket_reporter_status_idx "
                "ON encoach_ticket (reporter_id, status)",
            ),
            (
                'encoach_ticket_assignee_status_idx',
                "CREATE INDEX IF NOT EXISTS encoach_ticket_assignee_status_idx "
                "ON encoach_ticket (assignee_id, status) WHERE assignee_id IS NOT NULL",
            ),
        ):
            try:
                cr.execute(ddl)
            except Exception:
                _logger.warning("could not create index %s", name, exc_info=True)
        return res
