import crypto from 'crypto';
import type { Request, Response } from 'express';
import redis from '../redis.js';
import { getPool } from '../db.js';
import { createTrackingLink } from '../tracking-links.js';
import { sendMessage } from '../clients/whatsapp.js';
import type { RowDataPacket } from 'mysql2/promise';

const TWIML_ACK = '<?xml version="1.0" encoding="UTF-8"?><Response></Response>';

export async function initTwilioWebhookMapping(): Promise<void> {
  const twilioNumber = (process.env.TWILIO_WHATSAPP_NUMBER ?? '').replace(/^whatsapp:/, '');
  const pilotCustomerId = process.env.PILOT_CUSTOMER_ID ?? '';
  if (!twilioNumber || !pilotCustomerId) {
    console.warn('[twilio-webhook] TWILIO_WHATSAPP_NUMBER or PILOT_CUSTOMER_ID unset — customer mapping not seeded');
    return;
  }
  try {
    await redis.set(`twilio_wa_number:${twilioNumber}`, pilotCustomerId);
    console.log(`[twilio-webhook] mapped ${twilioNumber} → ${pilotCustomerId}`);
  } catch (err) {
    console.error('[twilio-webhook] Redis unavailable at startup — customer mapping not seeded; inbound messages will be dropped until next restart:', err);
  }
}

export function validateTwilioSignature(
  authToken: string,
  url: string,
  params: Record<string, string>,
  sig: string
): boolean {
  const sortedParams = Object.keys(params).sort().map(k => k + params[k]).join('');
  const expected = crypto.createHmac('sha1', authToken).update(url + sortedParams).digest('base64');
  const expectedBuf = Buffer.from(expected);
  const sigBuf = Buffer.from(sig);
  if (expectedBuf.length !== sigBuf.length) return false;
  return crypto.timingSafeEqual(expectedBuf, sigBuf);
}

interface PostDraft extends RowDataPacket {
  id: number;
  draft_text_wa: string;
  destination_url: string | null;
  created_at: Date;
}

interface CustomerRow extends RowDataPacket {
  pilot_owner_phone: string | null;
}

export async function handleApproval(
  customerId: string,
  from: string,
  messageId: string,
  body: string,
  numMedia: number
): Promise<void> {
  const dedupKey = `wa_msg_seen:${messageId}`;
  const isNew = await redis.set(dedupKey, '1', { NX: true, EX: 86400 });
  if (!isNew) {
    console.log(`[twilio-webhook] duplicate MessageSid=${messageId}, skipping`);
    return;
  }

  const pool = getPool();
  const [customers] = await pool.query<CustomerRow[]>(
    'SELECT pilot_owner_phone FROM customers WHERE id = ?',
    [customerId]
  );
  const pilotPhone = customers[0]?.pilot_owner_phone ?? '';
  if (!pilotPhone || from !== pilotPhone) {
    console.log(`[twilio-webhook] sender ${from} not in allowlist, customerId=${customerId}, MessageSid=${messageId}`);
    return;
  }

  if (numMedia > 0) {
    await sendMessage({ to: from, message: 'Please text 1, 2, or 3 to approve a draft. Voice notes and images are not supported here.' });
    return;
  }

  const [rows] = await pool.query<PostDraft[]>(
    `SELECT id, draft_text_wa, destination_url, created_at
     FROM post_drafts
     WHERE tenant_id = ? AND status = 'pending'
     ORDER BY created_at DESC`,
    [customerId]
  );
  const drafts = rows as PostDraft[];

  if (drafts.length === 0) {
    await sendMessage({ to: from, message: 'No pending drafts right now. New ones arrive on Monday.' });
    return;
  }

  const newestDate = new Date(drafts[0].created_at);
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
  if (newestDate < sevenDaysAgo) {
    await sendMessage({ to: from, message: 'Those drafts expired — new ones arrive Monday.' });
    return;
  }

  const newestDay = newestDate.toDateString();
  const batch = drafts.filter(d => new Date(d.created_at).toDateString() === newestDay).slice(0, 3);

  const trimmed = body.trim();
  if (!['1', '2', '3'].includes(trimmed)) {
    const list = batch.map((d, i) => `${i + 1}. ${[...d.draft_text_wa].slice(0, 80).join('')}`).join('\n');
    await sendMessage({ to: from, message: `Reply 1, 2, or 3 to approve a draft:\n\n${list}` });
    return;
  }

  const index = parseInt(trimmed, 10) - 1;
  const draft = batch[index];
  if (!draft) {
    await sendMessage({ to: from, message: `Only ${batch.length} draft${batch.length === 1 ? '' : 's'} available. Reply 1–${batch.length}.` });
    return;
  }

  let trackedUrl: string | null = null;
  if (draft.destination_url) {
    try {
      const token = await createTrackingLink({
        customerId,
        draftId: draft.id,
        destinationUrl: draft.destination_url,
        expiresAt: null,
      });
      const baseUrl = (process.env.PUBLIC_WEBHOOK_BASE_URL ?? '').replace(/\/$/, '');
      trackedUrl = `${baseUrl}/t/${token}`;
    } catch (err) {
      console.error(`[twilio-webhook] createTrackingLink failed, draft=${draft.id}, customerId=${customerId}:`, err);
    }
  }

  await pool.query(
    "UPDATE post_drafts SET status = 'approved', approved_at = NOW() WHERE id = ?",
    [draft.id]
  );

  const confirmMsg = trackedUrl
    ? `Draft ${index + 1} approved and live! Track clicks: ${trackedUrl}`
    : `Draft ${index + 1} approved and live!`;
  await sendMessage({ to: from, message: confirmMsg });

  console.log(`[twilio-webhook] approved draft=${draft.id}, customerId=${customerId}, MessageSid=${messageId}`);
}

export async function handleTwilioWhatsApp(req: Request, res: Response): Promise<void> {
  const authToken = process.env.TWILIO_AUTH_TOKEN ?? '';
  if (!authToken) {
    console.error('[twilio-webhook] TWILIO_AUTH_TOKEN not set — refusing to process');
    res.status(503).send('Webhook validation not configured');
    return;
  }

  const sig = req.headers['x-twilio-signature'] as string | undefined;
  if (!sig) {
    res.status(403).send('Missing signature');
    return;
  }

  const baseUrl = (process.env.PUBLIC_WEBHOOK_BASE_URL ?? '').replace(/\/$/, '');
  const webhookUrl = baseUrl
    ? `${baseUrl}/webhook/twilio/whatsapp`
    : (() => {
        console.warn('[twilio-webhook] PUBLIC_WEBHOOK_BASE_URL not set — using req.hostname fallback; signature validation WILL fail behind K8s ingress');
        return `https://${req.hostname}/webhook/twilio/whatsapp`;
      })();

  const params = req.body as Record<string, string>;

  if (!validateTwilioSignature(authToken, webhookUrl, params, sig)) {
    res.status(403).send('Invalid signature');
    return;
  }

  res.type('text/xml').status(200).send(TWIML_ACK);

  const from = (params.From ?? '').replace(/^whatsapp:/, '');
  const to = (params.To ?? '').replace(/^whatsapp:/, '');
  const messageId = params.MessageSid ?? '';
  const body = params.Body ?? '';
  const numMedia = parseInt(params.NumMedia ?? '0', 10);

  if (!from || !messageId) {
    console.error('[twilio-webhook] missing From or MessageSid in payload');
    return;
  }

  const customerId = await redis.get(`twilio_wa_number:${to}`).catch(() => null);
  if (!customerId) {
    console.warn(`[twilio-webhook] no customer mapping for Twilio number ${to}`);
    return;
  }

  handleApproval(customerId, from, messageId, body, numMedia).catch(err => {
    console.error(`[twilio-webhook] unhandled error, MessageSid=${messageId}, customerId=${customerId}:`, err);
  });
}
