/**
 * Gift card delivery email + branded PDF voucher.
 *
 * - enqueueGiftCardIssuedEmail: sends the recipient a styled email with
 *   the cleartext code (only available at issuance time) and the rendered
 *   PDF voucher attached.
 * - enqueueGiftCardRedeemedEmail: optional receipt sent after redemption
 *   showing applied amount + remaining balance.
 * - renderVoucherPdf: returns a Buffer with an A6 printable voucher.
 */
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { enqueueEmail } from './email/outbox.service';
import PDFDocument from 'pdfkit';
import { getSettings, maskCode } from './gift-cards.service';

interface RestaurantBrand {
  name: string;
  logo_url: string | null;
  brand_primary_color: string;
  currency: string;
}

async function loadRestaurantBrand(restaurantId: string): Promise<RestaurantBrand> {
  const { rows } = await db.execute(sql`
    SELECT name, logo_url, currency FROM restaurants WHERE id = ${restaurantId}
  `);
  const r = (rows[0] as Record<string, unknown> | undefined) || {};
  const settings = await getSettings(restaurantId);
  return {
    name: (r.name as string) || 'Restaurant',
    logo_url: (r.logo_url as string | null) ?? null,
    brand_primary_color: settings.brand_primary_color,
    currency: (r.currency as string | null) || settings.currency,
  };
}

function fmtMoney(amount: number, currency: string): string {
  try {
    return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount);
  } catch {
    return `${currency} ${amount.toFixed(2)}`;
  }
}

function formatDate(iso: string): string {
  try {
    return new Date(iso).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
  } catch { return iso; }
}

function escapeHtml(s: string): string {
  return s.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]!));
}

// ---------- PDF ----------

export interface VoucherInput {
  restaurantName: string;
  brandColor: string;
  amount: number;
  currency: string;
  recipientName: string;
  senderName: string | null;
  personalMessage: string | null;
  code: string;            // cleartext OR masked depending on context
  expiresAt: string | null;
  showCode: boolean;       // when true, show full code; otherwise show masked
  maskedCode: string;
}

export async function renderVoucherPdf(v: VoucherInput): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    try {
      // A6 landscape: 420 x 297 pt
      const doc = new PDFDocument({ size: [420, 297], margin: 0, info: { Title: `${v.restaurantName} gift card` } });
      const chunks: Buffer[] = [];
      doc.on('data', (c) => chunks.push(c as Buffer));
      doc.on('end', () => resolve(Buffer.concat(chunks)));
      doc.on('error', reject);

      const W = 420, H = 297;
      const brand = v.brandColor || '#f97316';

      // Outer card
      doc.rect(0, 0, W, H).fill('#ffffff');
      doc.rect(0, 0, W, 84).fill(brand);

      // Header text
      doc.fillColor('#ffffff').font('Helvetica-Bold').fontSize(11)
         .text('GIFT CARD', 20, 22, { characterSpacing: 3 });
      doc.font('Helvetica-Bold').fontSize(20)
         .text(v.restaurantName, 20, 40, { width: W - 40, ellipsis: true });

      // Amount
      doc.fillColor('#111827').font('Helvetica-Bold').fontSize(40)
         .text(fmtMoney(v.amount, v.currency), 0, 110, { align: 'center', width: W });

      // Recipient line
      doc.fillColor('#374151').font('Helvetica').fontSize(11)
         .text(`For ${v.recipientName}`, 0, 156, { align: 'center', width: W });
      if (v.senderName) {
        doc.fillColor('#6b7280').font('Helvetica-Oblique').fontSize(10)
           .text(`From ${v.senderName}`, 0, 172, { align: 'center', width: W });
      }

      // Code box
      const codeY = 200;
      const boxW = 280;
      const boxX = (W - boxW) / 2;
      doc.roundedRect(boxX, codeY, boxW, 42, 8).lineWidth(1).dash(4, { space: 3 }).stroke(brand).undash();
      doc.fillColor('#6b7280').font('Helvetica-Bold').fontSize(7)
         .text('CODE', boxX, codeY + 6, { align: 'center', width: boxW, characterSpacing: 2 });
      doc.fillColor('#111827').font('Courier-Bold').fontSize(18)
         .text(v.showCode ? v.code : v.maskedCode, boxX, codeY + 17, { align: 'center', width: boxW, characterSpacing: 2 });

      // Expiry
      const expiryText = v.expiresAt ? `Valid until ${formatDate(v.expiresAt)}` : 'No expiration';
      doc.fillColor('#6b7280').font('Helvetica').fontSize(9)
         .text(expiryText, 0, codeY + 52, { align: 'center', width: W });

      // Personal message footer
      if (v.personalMessage) {
        doc.fillColor('#374151').font('Helvetica-Oblique').fontSize(9)
           .text(`"${v.personalMessage}"`, 30, H - 30, { align: 'center', width: W - 60, ellipsis: true });
      }

      doc.end();
    } catch (err) {
      reject(err as Error);
    }
  });
}

// ---------- Email enqueue ----------

interface IssueEmailOptions {
  restaurantId: string;
  branchId: string | null;
  cardId: string;
  /** Cleartext code — only known right after issuance. */
  plaintextCode: string;
}

export async function enqueueGiftCardIssuedEmail(opts: IssueEmailOptions): Promise<void> {
  const brand = await loadRestaurantBrand(opts.restaurantId);
  const settings = await getSettings(opts.restaurantId);
  const { rows } = await db.execute(sql`SELECT * FROM gift_cards WHERE id = ${opts.cardId}::uuid`);
  const card = rows[0] as Record<string, unknown> | undefined;
  if (!card || !card.recipient_email) return;

  const amount = Number(card.original_amount);
  const recipientName = (card.recipient_name as string) || 'Friend';
  const senderName = (card.sender_name as string | null) || null;
  const personalMessage = (card.personal_message as string | null) || null;
  const expiresAt = (card.expires_at as string | null) || null;
  const showCode = settings.show_full_code_on_voucher;
  const masked = maskCode(settings.code_prefix, opts.plaintextCode.slice(-4), opts.plaintextCode.length);

  const pdf = await renderVoucherPdf({
    restaurantName: brand.name,
    brandColor: brand.brand_primary_color,
    amount,
    currency: brand.currency,
    recipientName,
    senderName,
    personalMessage,
    code: opts.plaintextCode,
    maskedCode: masked,
    expiresAt,
    showCode,
  });

  const messageBlock = personalMessage
    ? `<div style="margin:14px 0;padding:14px;background:#f9fafb;border-left:3px solid ${brand.brand_primary_color};border-radius:6px;font-style:italic;color:#374151;">${escapeHtml(personalMessage)}</div>`
    : '';
  const senderLine = senderName ? escapeHtml(senderName) : 'Someone';
  const expiryLine = expiresAt ? `Valid until ${formatDate(expiresAt)}` : 'No expiration';

  await enqueueEmail({
    to: card.recipient_email as string,
    templateKey: 'gift_card_issued',
    restaurantId: opts.restaurantId,
    branchId: opts.branchId,
    kind: 'gift_card',
    vars: {
      restaurantName: brand.name,
      recipientName,
      senderLine,
      amount: fmtMoney(amount, brand.currency),
      code: opts.plaintextCode,
      brandColor: brand.brand_primary_color,
      expiryLine,
      messageBlockHtml: messageBlock,
      messageBlock,
      // attachments are picked up by transport via outbox extension if supported;
      // base64 placed on payload so worker can attach if/when it understands it.
      _attachments: [{
        filename: `gift-card-${opts.plaintextCode}.pdf`,
        content_b64: pdf.toString('base64'),
        content_type: 'application/pdf',
      }],
    },
  });
}

interface RedeemEmailOptions {
  restaurantId: string;
  branchId?: string | null;
  cardId: string;
  appliedAmount: number;
  remainingBalance: number;
  orderRef: string;
}

export async function enqueueGiftCardRedeemedEmail(opts: RedeemEmailOptions): Promise<void> {
  const brand = await loadRestaurantBrand(opts.restaurantId);
  const settings = await getSettings(opts.restaurantId);
  const { rows } = await db.execute(sql`SELECT * FROM gift_cards WHERE id = ${opts.cardId}::uuid`);
  const card = rows[0] as Record<string, unknown> | undefined;
  if (!card || !card.recipient_email) return;
  await enqueueEmail({
    to: card.recipient_email as string,
    templateKey: 'gift_card_balance_after_redeem',
    restaurantId: opts.restaurantId,
    branchId: opts.branchId ?? null,
    kind: 'gift_card',
    vars: {
      restaurantName: brand.name,
      recipientName: card.recipient_name as string,
      maskedCode: maskCode(settings.code_prefix, card.code_last4 as string, settings.code_length),
      appliedAmount: fmtMoney(opts.appliedAmount, brand.currency),
      remainingBalance: fmtMoney(opts.remainingBalance, brand.currency),
      orderRef: opts.orderRef,
    },
  });
}

// ---------- Reminder / resend (no cleartext available) ----------

/**
 * Re-send the voucher email to the recipient. Because the cleartext code is
 * never stored, the masked code is shown on both the email and the attached
 * PDF voucher. The recipient still has the original cleartext from the
 * issuance email; this acts as a balance reminder + PDF resend.
 */
export async function enqueueGiftCardReminderEmail(opts: { restaurantId: string; branchId?: string | null; cardId: string }): Promise<void> {
  const brand = await loadRestaurantBrand(opts.restaurantId);
  const settings = await getSettings(opts.restaurantId);
  const { rows } = await db.execute(sql`SELECT * FROM gift_cards WHERE id = ${opts.cardId}::uuid AND restaurant_id = ${opts.restaurantId}`);
  const card = rows[0] as Record<string, unknown> | undefined;
  if (!card || !card.recipient_email) return;

  const amount = Number(card.original_amount);
  const balance = Number(card.balance);
  const recipientName = (card.recipient_name as string) || 'Friend';
  const senderName = (card.sender_name as string | null) || null;
  const personalMessage = (card.personal_message as string | null) || null;
  const expiresAt = (card.expires_at as string | null) || null;
  const masked = maskCode(settings.code_prefix, card.code_last4 as string, settings.code_length);

  const pdf = await renderVoucherPdf({
    restaurantName: brand.name,
    brandColor: brand.brand_primary_color,
    amount,
    currency: brand.currency,
    recipientName,
    senderName,
    personalMessage,
    code: masked,
    maskedCode: masked,
    expiresAt,
    showCode: false,
  });

  const messageBlock = personalMessage
    ? `<div style="margin:14px 0;padding:14px;background:#f9fafb;border-left:3px solid ${brand.brand_primary_color};border-radius:6px;font-style:italic;color:#374151;">${escapeHtml(personalMessage)}</div>`
    : '';
  const senderLine = senderName ? escapeHtml(senderName) : 'Someone';
  const expiryLine = expiresAt ? `Valid until ${formatDate(expiresAt)}` : 'No expiration';
  const balanceNote = `<p style="margin:10px 0 0 0;font-size:13px;color:#6b7280;">Remaining balance: <strong>${fmtMoney(balance, brand.currency)}</strong>. The full code was sent in your original email — only the last 4 digits are shown here for security.</p>`;

  await enqueueEmail({
    to: card.recipient_email as string,
    templateKey: 'gift_card_issued',
    restaurantId: opts.restaurantId,
    branchId: opts.branchId ?? null,
    kind: 'gift_card',
    vars: {
      restaurantName: brand.name,
      recipientName,
      senderLine,
      amount: fmtMoney(amount, brand.currency),
      code: masked,
      brandColor: brand.brand_primary_color,
      expiryLine,
      messageBlockHtml: messageBlock + balanceNote,
      messageBlock: messageBlock + balanceNote,
      _attachments: [{
        filename: `gift-card-${card.code_prefix as string}-${card.code_last4 as string}.pdf`,
        content_b64: pdf.toString('base64'),
        content_type: 'application/pdf',
      }],
    },
  });
}

// ---------- Voucher download (admin) ----------

export async function buildVoucherForCard(restaurantId: string, cardId: string, plaintext?: string): Promise<{ pdf: Buffer; filename: string }> {
  const brand = await loadRestaurantBrand(restaurantId);
  const settings = await getSettings(restaurantId);
  const { rows } = await db.execute(sql`
    SELECT * FROM gift_cards WHERE id = ${cardId}::uuid AND restaurant_id = ${restaurantId}
  `);
  const card = rows[0] as Record<string, unknown> | undefined;
  if (!card) throw new Error('Gift card not found');
  const masked = maskCode(settings.code_prefix, card.code_last4 as string, settings.code_length);
  const code = plaintext || masked;
  const showCode = !!plaintext && settings.show_full_code_on_voucher;
  const pdf = await renderVoucherPdf({
    restaurantName: brand.name,
    brandColor: brand.brand_primary_color,
    amount: Number(card.original_amount),
    currency: brand.currency,
    recipientName: (card.recipient_name as string) || 'Friend',
    senderName: (card.sender_name as string | null) || null,
    personalMessage: (card.personal_message as string | null) || null,
    code,
    maskedCode: masked,
    expiresAt: (card.expires_at as string | null) || null,
    showCode,
  });
  return { pdf, filename: `gift-card-${(card.code_prefix as string)}-${card.code_last4 as string}.pdf` };
}
