/**
 * Phase-1-Smoke-Test für den Trigger-Mailer.
 * Run: MAILFORGE_API_KEY_PEPPER=test-pepper-1234567890 npx tsx scripts/smoke-trigger.ts
 *
 * Deckt ab:
 *  1. createTriggerTemplate → API-Key wird einmalig zurückgegeben
 *  2. verifyApiKey → akzeptiert echten Key, lehnt falschen ab
 *  3. enqueueTriggerSend → recipient + email_queue werden in Container-Campaign angelegt
 *  4. Idempotenz: gleicher idempotencyKey → keine zweite Queue-Insertion
 *  5. extractVariablesFromMjml → erkennt User-Variablen, filtert System-Tags
 *  6. finalizeCampaignIfDone → Container-Campaign bleibt status='sending' nach Send
 *  7. Subject-Personalisierung läuft über substituteTagsText (smoke-getestet via Tag-Helper)
 */

import { db, schema } from '../src/lib/db/client';
import { eq } from 'drizzle-orm';
import {
  createTriggerTemplate,
  verifyApiKey,
  enqueueTriggerSend,
  extractVariablesFromMjml,
  validateTriggerPayload,
  findRecentTriggerByIdempotency,
  logTriggerEvent,
  getTriggerTemplateByKey,
} from '../src/lib/db/trigger-template-repo';
import { finalizeCampaignIfDone } from '../src/lib/db/campaign-repo';
import { substituteTagsText } from '../src/modules/sending/lib/tag-substitution';

function assertEq<T>(actual: T, expected: T, label: string): void {
  if (actual === expected) {
    console.log(`  ✓ ${label}`);
  } else {
    console.error(`  ✗ ${label}`);
    console.error(`      expected: ${JSON.stringify(expected)}`);
    console.error(`      actual:   ${JSON.stringify(actual)}`);
    process.exitCode = 1;
  }
}

function assertTrue(cond: boolean, label: string): void {
  if (cond) console.log(`  ✓ ${label}`);
  else { console.error(`  ✗ ${label}`); process.exitCode = 1; }
}

async function main() {
  if (!process.env.MAILFORGE_API_KEY_PEPPER) {
    console.error('❌ MAILFORGE_API_KEY_PEPPER muss gesetzt sein (>=16 Zeichen)');
    process.exit(2);
  }

  console.log('— Variablen-Extraktion —');
  const sampleMjml = '<mjml><mj-body><mj-section><mj-text>Hallo {{firstName}}, deine Order {{orderNumber}} über {{ total }} EUR.</mj-text></mj-section></mj-body></mjml>';
  const sampleSubject = 'Bestellung {{orderNumber}} bestätigt — {{FIRST_NAME}}';
  const vars = extractVariablesFromMjml(sampleMjml, sampleSubject, '');
  const varKeys = vars.map(v => v.key).sort();
  assertEq(varKeys.join(','), 'firstName,orderNumber,total', 'erkannte User-Variablen ohne System-Tags');

  console.log('\n— Tag-Substitution: Subject mit System + Custom —');
  const subjectIn = 'Bestellung {{ orderNumber }} bestätigt — {{FIRST_NAME}}';
  const sub1 = substituteTagsText(subjectIn, { FIRST_NAME: 'Max', RECIPIENT_ID: 'r1', CRM_ID: '', EMAIL: 'a@b.de' });
  const sub2 = substituteTagsText(sub1, { orderNumber: '42' }, { reservedKeys: new Set(['FIRST_NAME', 'RECIPIENT_ID', 'CRM_ID', 'EMAIL']) });
  assertEq(sub2, 'Bestellung 42 bestätigt — Max', 'Subject vollständig personalisiert');

  console.log('\n— Test-Workspace suchen —');
  const workspace = db.select().from(schema.workspaces).limit(1).get();
  if (!workspace) {
    console.error('❌ Keine Workspace in der DB. Bitte erst `npm run db:migrate` und `npm run db:seed-dev` (oder über die App anlegen).');
    process.exit(2);
  }
  console.log(`  ℹ verwende Workspace: ${workspace.slug}`);

  console.log('\n— Template freischalten —');
  const key = `smoke_${Date.now()}`;
  const created = createTriggerTemplate({
    key,
    name: 'Smoke-Trigger',
    workspaceId: workspace.id,
    subject: sampleSubject,
    preheader: 'Vorschau-Text {{orderNumber}}',
    mjml: sampleMjml,
    html: '<html><body>Hallo {{firstName}}, deine Order {{orderNumber}}</body></html>',
    htmlText: 'Hallo {{firstName}}, deine Order {{orderNumber}}',
  });
  assertTrue(created.apiKey.startsWith('mf_live_'), 'API-Key hat mf_live_-Prefix');
  assertTrue(created.apiKey.length >= 40, 'API-Key ist hochentropisch (>=40 Zeichen)');
  assertEq(created.template.key, key, 'Template-Key persistiert');

  console.log('\n— API-Key-Verify —');
  const fresh = getTriggerTemplateByKey(key)!;
  assertTrue(verifyApiKey(fresh, created.apiKey), 'Korrekter Key wird akzeptiert');
  assertTrue(!verifyApiKey(fresh, created.apiKey + 'x'), 'Manipulierter Key wird abgelehnt');
  assertTrue(!verifyApiKey(fresh, ''), 'Leerer Key wird abgelehnt');

  console.log('\n— Validation —');
  const okResult = validateTriggerPayload(fresh, { to: { email: 'a@b.de' }, data: { firstName: 'Max', orderNumber: '42', total: '129' } });
  assertTrue(okResult.ok, 'Vollständige Payload validiert');
  const failResult = validateTriggerPayload(fresh, { to: { email: 'a@b.de' }, data: { firstName: 'Max' } });
  assertTrue(!failResult.ok, 'Fehlende Pflicht-Variable wird erkannt');
  assertTrue(failResult.errors.some(e => e.includes('orderNumber')), 'Fehlerliste nennt fehlende Variable');

  console.log('\n— enqueueTriggerSend —');
  const beforeRecipients = db.select().from(schema.recipients).where(eq(schema.recipients.campaignId, fresh.containerCampaignId)).all().length;
  const beforeQueue = db.select().from(schema.emailQueue).where(eq(schema.emailQueue.campaignId, fresh.containerCampaignId)).all().length;
  const enqueued = enqueueTriggerSend({
    template: fresh,
    to: { email: 'smoke@example.com', firstName: 'Max' },
    data: { firstName: 'Max', orderNumber: '42', total: '129.90' },
  });
  const afterRecipients = db.select().from(schema.recipients).where(eq(schema.recipients.campaignId, fresh.containerCampaignId)).all().length;
  const afterQueue = db.select().from(schema.emailQueue).where(eq(schema.emailQueue.campaignId, fresh.containerCampaignId)).all().length;
  assertEq(afterRecipients - beforeRecipients, 1, 'genau 1 neuer recipient');
  assertEq(afterQueue - beforeQueue, 1, 'genau 1 neuer email_queue-Job');
  const job = db.select().from(schema.emailQueue).where(eq(schema.emailQueue.id, enqueued.queueJobId)).get();
  assertEq(job?.status, 'pending', 'Job-Status ist pending');
  const recipient = db.select().from(schema.recipients).where(eq(schema.recipients.id, enqueued.recipientId)).get();
  const customData = recipient?.customDataJson as Record<string, unknown>;
  assertEq((customData?.orderNumber as string) ?? '', '42', 'customDataJson enthält orderNumber');

  console.log('\n— Idempotenz —');
  const idemKey = `idem-${Date.now()}`;
  logTriggerEvent({ triggerTemplateId: fresh.id, recipientEmail: 'idem@x.de', status: 'queued', idempotencyKey: idemKey, emailQueueJobId: enqueued.queueJobId });
  const dup = findRecentTriggerByIdempotency(fresh.id, idemKey);
  assertTrue(!!dup, 'Doppelter idempotencyKey wird im 24h-Fenster gefunden');
  const noDup = findRecentTriggerByIdempotency(fresh.id, 'never-used');
  assertTrue(!noDup, 'Unbenutzter idempotencyKey gibt undefined');

  console.log('\n— finalizeCampaignIfDone respektiert Container —');
  // Job auf 'sent' setzen, damit Queue „leer" ist; finalize darf NICHT auf 'sent' gehen
  db.update(schema.emailQueue).set({ status: 'sent' }).where(eq(schema.emailQueue.id, enqueued.queueJobId)).run();
  finalizeCampaignIfDone(fresh.containerCampaignId);
  const containerAfter = db.select().from(schema.campaigns).where(eq(schema.campaigns.id, fresh.containerCampaignId)).get();
  assertEq(containerAfter?.status, 'sending', 'Container-Campaign bleibt nach Send-Done auf sending');
  assertEq(containerAfter?.kind, 'trigger_container', 'Container hat kind=trigger_container');

  console.log('\n— Cleanup —');
  // Test-Daten aufräumen — Cascade über trigger_template → trigger_logs nicht jedoch container_campaign
  db.delete(schema.triggerLogs).where(eq(schema.triggerLogs.triggerTemplateId, fresh.id)).run();
  db.delete(schema.triggerTemplates).where(eq(schema.triggerTemplates.id, fresh.id)).run();
  db.delete(schema.emailQueue).where(eq(schema.emailQueue.campaignId, fresh.containerCampaignId)).run();
  db.delete(schema.recipients).where(eq(schema.recipients.campaignId, fresh.containerCampaignId)).run();
  db.delete(schema.campaignVariants).where(eq(schema.campaignVariants.campaignId, fresh.containerCampaignId)).run();
  db.delete(schema.campaigns).where(eq(schema.campaigns.id, fresh.containerCampaignId)).run();
  console.log('  ✓ Test-Daten entfernt');

  if (process.exitCode === 1) {
    console.error('\n❌ Smoke-Test FEHLGESCHLAGEN');
  } else {
    console.log('\n✅ Trigger-Smoke-Test erfolgreich');
  }
}

main().catch(e => { console.error(e); process.exit(1); });
