"""
Management command: fetch_ticket_replies
Polls support@example.com via IMAP and creates TicketReply objects
for any customer replies that reference a ticket number in the subject line.

Run manually:   python manage.py fetch_ticket_replies
Cron (every 5m): */5 * * * * /path/to/venv/bin/python manage.py fetch_ticket_replies
"""
import imaplib
import email
import re
import html
from email.header import decode_header
from django.core.management.base import BaseCommand
from django.conf import settings
from apps.tickets.models import Ticket, TicketReply


def _decode_header_value(value):
    """Decode an email header value safely."""
    if not value:
        return ''
    parts = decode_header(value)
    decoded = []
    for part, charset in parts:
        if isinstance(part, bytes):
            decoded.append(part.decode(charset or 'utf-8', errors='replace'))
        else:
            decoded.append(part)
    return ''.join(decoded)


def _get_body(msg):
    """Extract plain text body from an email message."""
    body = ''
    if msg.is_multipart():
        for part in msg.walk():
            content_type = part.get_content_type()
            disposition = str(part.get('Content-Disposition', ''))
            if content_type == 'text/plain' and 'attachment' not in disposition:
                charset = part.get_content_charset() or 'utf-8'
                body = part.get_payload(decode=True).decode(charset, errors='replace')
                break
        # Fall back to HTML part if no plain text
        if not body:
            for part in msg.walk():
                if part.get_content_type() == 'text/html':
                    charset = part.get_content_charset() or 'utf-8'
                    raw_html = part.get_payload(decode=True).decode(charset, errors='replace')
                    body = re.sub(r'<[^>]+>', ' ', raw_html)
                    body = html.unescape(body)
                    break
    else:
        charset = msg.get_content_charset() or 'utf-8'
        body = msg.get_payload(decode=True).decode(charset, errors='replace')

    # Strip quoted reply (lines starting with > or typical reply headers)
    lines = body.splitlines()
    clean_lines = []
    for line in lines:
        stripped = line.strip()
        if stripped.startswith('>'):
            break
        # Stop at common quoted-reply markers
        if re.match(r'^On .+ wrote:$', stripped):
            break
        if stripped.startswith('From:') and len(clean_lines) > 3:
            break
        clean_lines.append(line)

    return '\n'.join(clean_lines).strip()


class Command(BaseCommand):
    help = 'Fetch customer email replies and attach them to tickets'

    def handle(self, *args, **options):
        from apps.ftlink.models import JobRun
        # Settings only. Credentials do not live in .env.
        from apps.pages.email_backend import support_credentials, support_imap
        support_email, support_password = support_credentials()
        imap_host, imap_port = support_imap()

        if not support_email or not support_password:
            self.stderr.write('No support mailbox set. Add it under Settings, Outgoing Email.')
            return
        if not imap_host:
            self.stderr.write('No mail host set. Add the SMTP host under Settings, Outgoing Email.')
            return

        try:
            mail = imaplib.IMAP4_SSL(imap_host, imap_port)
            mail.login(support_email, support_password)
        except Exception as exc:
            self.stderr.write(f'IMAP login failed: {exc}')
            return

        mail.select('INBOX')
        _, data = mail.search(None, 'UNSEEN')
        msg_ids = data[0].split()

        if not msg_ids:
            self.stdout.write('No new emails.')
            mail.logout()
            # An empty mailbox is a successful run, not a skipped one. Without
            # this the heartbeat only fired on days a customer happened to
            # reply, and System Info would report a perfectly healthy job as
            # never having run.
            JobRun.record('fetch_ticket_replies', note='no new emails')
            return

        processed = 0
        for msg_id in msg_ids:
            _, msg_data = mail.fetch(msg_id, '(RFC822)')
            raw = msg_data[0][1]
            msg = email.message_from_bytes(raw)

            subject = _decode_header_value(msg.get('Subject', ''))
            from_header = _decode_header_value(msg.get('From', ''))

            # Two shapes have to work here, and the old one is not
            # optional: every ticket ever raised went out as PREFIX-123456
            # and those numbers are sitting in customers' inboxes. The new
            # form has no hyphen and a T after the prefix.
            #
            #   FTT260800012   current
            #   FT-123456      everything issued before 22 August 2026
            #
            # Current form first, because a subject could carry both if a
            # customer quotes an old thread.
            ticket_number = ''
            for pattern in (r'\b([A-Za-z]{2,6}\d{8,12})\b',
                            r'\b([A-Za-z]{1,4}-\d{4,})\b'):
                m = re.search(pattern, subject)
                if m:
                    ticket_number = m.group(1).upper()
                    break
            if not ticket_number:
                # Mark as read so we don't reprocess
                mail.store(msg_id, '+FLAGS', '\\Seen')
                continue
            try:
                ticket = Ticket.objects.get(ticket_number=ticket_number)
            except Ticket.DoesNotExist:
                mail.store(msg_id, '+FLAGS', '\\Seen')
                self.stdout.write(f'Ticket {ticket_number} not found — skipping.')
                continue

            body = _get_body(msg)
            if not body:
                mail.store(msg_id, '+FLAGS', '\\Seen')
                continue

            # Extract sender name from From header
            sender_name = re.sub(r'<.*?>', '', from_header).strip().strip('"') or ticket.sender_name

            TicketReply.objects.create(
                ticket=ticket,
                author=None,
                author_name=sender_name,
                message=body,
                is_internal=False,
                sent_by_email=False,
            )

            # Move ticket back to open/waiting if resolved
            if ticket.status in (Ticket.STATUS_RESOLVED, Ticket.STATUS_CLOSED):
                ticket.status = Ticket.STATUS_OPEN
            else:
                ticket.status = Ticket.STATUS_OPEN
            ticket.save(update_fields=['status', 'updated_at'])

            mail.store(msg_id, '+FLAGS', '\\Seen')
            processed += 1
            self.stdout.write(f'Reply added to {ticket_number} from {sender_name}')

        mail.logout()
        self.stdout.write(f'Done — {processed} replies imported.')
        JobRun.record('fetch_ticket_replies', note=f'{processed} replies imported')
