import { ImapFlow } from 'imapflow';
import { simpleParser } from 'mailparser';
import { query, queryOne } from '../db';
import { logger } from '../logger';
import { classifyReplyIntent, enrichPlainTextReply } from './enrichmentService';
import { createOutboundTransporter } from '../mailer';

let pollTimer: ReturnType<typeof setInterval> | null = null;

function getEnv(name: string): string | undefined {
  const v = process.env[name];
  return v && v.trim() ? v.trim() : undefined;
}

async function resolveCampaignContactFromHeaders(mail: Awaited<ReturnType<typeof simpleParser>>): Promise<{
  campaignId: string | null;
  contactId: string | null;
}> {
  const getHeader = (name: string) => {
    const h = mail.headers.get(name);
    return typeof h === 'string' ? h : h?.toString?.() || '';
  };

  let campaignId = getHeader('x-email-campaigner-campaign-id') || null;
  let contactId = getHeader('x-email-campaigner-contact-id') || null;

  if (campaignId && contactId) return { campaignId, contactId };

  const inReplyTo = getHeader('in-reply-to') || '';
  const refs = getHeader('references') || '';
  const tokens = `${inReplyTo} ${refs}`
    .split(/\s+/)
    .map(s => s.replace(/[<>]/g, '').trim())
    .filter(Boolean);

  for (const mid of tokens) {
    const row = await queryOne<{ campaign_id: string; contact_id: string }>(
      `SELECT campaign_id, contact_id FROM campaign_enriched_emails
       WHERE outbound_message_id = $1 LIMIT 1`,
      [mid]
    );
    if (row) {
      return { campaignId: row.campaign_id, contactId: row.contact_id };
    }
  }

  return { campaignId, contactId };
}

async function findContactByFrom(mail: Awaited<ReturnType<typeof simpleParser>>): Promise<string | null> {
  const addr = mail.from?.value?.[0]?.address?.toLowerCase().trim();
  if (!addr) return null;
  const row = await queryOne<{ id: string }>('SELECT id FROM contacts WHERE lower(email) = $1', [addr]);
  return row?.id ?? null;
}

async function loadForwardList(campaignId: string | null): Promise<string[]> {
  if (!campaignId) return [];
  const row = await queryOne<{ auto_forward_emails: string[] }>(
    'SELECT auto_forward_emails FROM campaign_settings WHERE campaign_id = $1',
    [campaignId]
  );
  return (row?.auto_forward_emails || []).filter(Boolean);
}

async function processParsed(
  parsed: Awaited<ReturnType<typeof simpleParser>>
): Promise<void> {
  const subject = parsed.subject || '';
  const text = parsed.text || '';
  const { campaignId, contactId: headerContactId } = await resolveCampaignContactFromHeaders(parsed);
  const contactId = headerContactId || (await findContactByFrom(parsed));

  const intent = await classifyReplyIntent(subject, text);

  const forwardedTo: string[] = [];
  const forwardList = await loadForwardList(campaignId);

  if (intent === 'unsubscribe' && contactId) {
    await query(`UPDATE contacts SET subscribed = false, updated_at = now() WHERE id = $1`, [contactId]);
    await query(`INSERT INTO email_events (campaign_id, contact_id, type, metadata) VALUES ($1, $2, 'unsubscribed', $3)`, [
      campaignId,
      contactId,
      JSON.stringify({ source: 'reply' }),
    ]);

    if (campaignId) {
      const settings = await queryOne<{
        unsubscribe_response_template: string | null;
        enrich_unsubscribe_response: boolean;
      }>(
        `SELECT unsubscribe_response_template, enrich_unsubscribe_response FROM campaign_settings WHERE campaign_id = $1`,
        [campaignId]
      );

      let body =
        settings?.unsubscribe_response_template ||
        'You have been unsubscribed. You will not receive further emails from us.';
      if (settings?.enrich_unsubscribe_response) {
        try {
          body = await enrichPlainTextReply(body, {
            subject,
            snippet: text.slice(0, 500),
          });
        } catch (e) {
          logger.warn(e, '[mailbox] enrich unsubscribe reply failed');
        }
      }

      const transporter = await createOutboundTransporter();
      const fromAddr = getEnv('MS365_SMTP_USER') || process.env.SMTP_FROM || 'noreply@example.com';
      const replyTo = parsed.from?.value?.[0]?.address;
      if (replyTo) {
        await transporter.sendMail({
          from: fromAddr,
          to: replyTo,
          subject: 'Re: ' + subject.replace(/^Re:\s*/i, ''),
          text: body,
        });
      }
    }
  } else if ((intent === 'reply' || intent === 'other') && forwardList.length > 0) {
    const transporter = await createOutboundTransporter();
    const fromAddr = getEnv('MS365_SMTP_USER') || process.env.SMTP_FROM || 'noreply@example.com';
    const summary = `From: ${parsed.from?.text || ''}\nSubject: ${subject}\n\n${text.slice(0, 8000)}`;

    for (const dest of forwardList) {
      try {
        await transporter.sendMail({
          from: fromAddr,
          to: dest,
          subject: `[Fwd] ${subject}`,
          text: summary,
        });
        forwardedTo.push(dest);
      } catch (e) {
        logger.error(e, `[mailbox] forward to ${dest} failed`);
      }
    }
  }

  await query(
    `INSERT INTO inbox_messages
     (campaign_id, contact_id, message_id, in_reply_to, subject, body_text, classified_as, forwarded_to, processed_at)
     VALUES ($1, $2, $3, $4, $5, $6, $7::inbox_message_class, $8, now())`,
    [
      campaignId,
      contactId,
      parsed.messageId || null,
      typeof parsed.inReplyTo === 'string' ? parsed.inReplyTo : String(parsed.inReplyTo ?? '') || null,
      subject,
      text.slice(0, 50000),
      intent,
      forwardedTo,
    ]
  );
}

async function pollOnce(): Promise<void> {
  const host = getEnv('MS365_IMAP_HOST');
  const user = getEnv('MS365_IMAP_USER');
  const pass = getEnv('MS365_IMAP_PASSWORD');
  if (!host || !user || !pass) return;

  const client = new ImapFlow({
    host,
    port: parseInt(process.env.MS365_IMAP_PORT || '993', 10),
    secure: true,
    auth: { user, pass },
    logger: false,
  });

  try {
    await client.connect();
    const lock = await client.getMailboxLock('INBOX');
    try {
      const searched = await client.search({ seen: false }, { uid: true });
      const uids = searched === false ? [] : searched;
      for (const uid of uids) {
        try {
          const downloaded = await client.download(String(uid), undefined, { uid: true });
          if (!downloaded?.content) continue;
          const parsed = await simpleParser(downloaded.content);
          await processParsed(parsed);
          await client.messageFlagsAdd(String(uid), ['\\Seen'], { uid: true });
        } catch (e) {
          logger.error(e, '[mailbox] process uid');
        }
      }
    } finally {
      lock.release();
    }
    await client.logout();
  } catch (e) {
    logger.error(e, '[mailbox] poll error');
    try {
      await client.logout();
    } catch {
      /* ignore */
    }
  }
}

export function startMailboxPoller(): void {
  if (pollTimer) return;
  const interval = parseInt(process.env.MAILBOX_POLL_SEC || '60', 10) * 1000;
  pollTimer = setInterval(() => {
    pollOnce().catch(e => logger.error(e, '[mailbox] interval'));
  }, interval);
  pollOnce().catch(e => logger.error(e, '[mailbox] initial'));
}
