/**
 * Public Trigger-API: externe Systeme (Online-Shop, CRM) lösen ein freigeschaltetes
 * Email-Template über diesen Endpoint aus.
 *
 *   POST /api/trigger/{key}
 *   Authorization: Bearer mf_live_…
 *   Content-Type: application/json
 *   {
 *     "to":   { "email": "x@y.de", "firstName": "Max", "lastName": "Müller", "crmId": "12345" },
 *     "data": { "orderNumber": "42", "total": "129.90", ... },
 *     "idempotencyKey": "order-42"   // optional, dedupliziert über 24h
 *   }
 *
 * Pipeline:
 *   1. Bearer-Auth gegen `triggerTemplates.apiKeyHash` (timing-safe).
 *   2. Validierung gegen `variableSchemaJson` des Templates.
 *   3. Idempotenz-Check (gleicher Key in letzten 24h → 200 mit duplicate-Flag).
 *   4. Unsubscribe-Check.
 *   5. Rate-Limit (optional pro Template).
 *   6. enqueueTriggerSend → recipient + email_queue Insert in Container-Campaign.
 *   7. Worker pollt anschließend wie üblich.
 */

import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import {
  enqueueTriggerSend,
  exceedsRateLimit,
  findRecentTriggerByIdempotency,
  getTriggerTemplateByKey,
  incrementTriggerStats,
  isUnsubscribed,
  logTriggerEvent,
  validateTriggerPayload,
  verifyApiKey,
} from '@/lib/db/trigger-template-repo';

export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

const ToSchema = z.object({
  email: z.string().email('to.email muss eine gültige Email sein'),
  firstName: z.string().optional(),
  lastName: z.string().optional(),
  crmId: z.union([z.string(), z.number()]).optional(),
});

const BodySchema = z.object({
  to: ToSchema,
  data: z.record(z.string(), z.unknown()).default({}),
  idempotencyKey: z.string().min(1).max(128).optional(),
});

function jsonError(status: number, error: string, extra?: Record<string, unknown>) {
  return NextResponse.json({ error, ...extra }, { status });
}

export async function POST(req: NextRequest, ctx: { params: Promise<{ key: string }> }) {
  const { key } = await ctx.params;

  // 1. Bearer-Auth
  const authHeader = req.headers.get('authorization');
  const providedKey = authHeader?.startsWith('Bearer ')
    ? authHeader.slice('Bearer '.length).trim()
    : null;
  if (!providedKey) {
    return jsonError(401, 'Missing Bearer token in Authorization header');
  }

  // 2. Template laden — auch inaktive Templates antworten 404 (kein Info-Leak ob Key existiert).
  const template = getTriggerTemplateByKey(key);
  if (!template || !template.isActive) {
    return jsonError(404, 'Template not found or inactive');
  }

  // 3. Verify-Auth — gegen Hash + timing-safe-compare. Bei Misserfolg loggen + 401.
  if (!verifyApiKey(template, providedKey)) {
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: '<unknown>',
      status: 'rejected_auth',
      errorMessage: 'Invalid API key',
    });
    return jsonError(401, 'Invalid API key');
  }

  // 4. Body parsen + Schema-validieren (statisch via zod + dynamisch gegen variableSchema).
  let body: z.infer<typeof BodySchema>;
  try {
    const raw = await req.json();
    const parsed = BodySchema.safeParse(raw);
    if (!parsed.success) {
      logTriggerEvent({
        triggerTemplateId: template.id,
        recipientEmail: typeof raw?.to?.email === 'string' ? raw.to.email : '<invalid>',
        status: 'rejected_validation',
        errorMessage: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; '),
        requestPayloadJson: raw,
      });
      return jsonError(400, 'Validation failed', {
        issues: parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`),
      });
    }
    body = parsed.data;
  } catch (e) {
    return jsonError(400, `Invalid JSON body: ${e instanceof Error ? e.message : String(e)}`);
  }

  // crmId zu String normalisieren (Repo erwartet string | undefined)
  const to = {
    email: body.to.email,
    firstName: body.to.firstName,
    lastName: body.to.lastName,
    crmId: body.to.crmId !== undefined ? String(body.to.crmId) : undefined,
  };

  // Für die dynamische Validierung das to-Objekt mit normalisierter crmId weiterreichen,
  // damit die Repo-Funktion ein homogenes Shape (alle string|undefined) sieht.
  const dynamicValidation = validateTriggerPayload(template, { to, data: body.data });
  if (!dynamicValidation.ok) {
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: to.email,
      status: 'rejected_validation',
      errorMessage: dynamicValidation.errors.join('; '),
      requestPayloadJson: { to: body.to, data: body.data, idempotencyKey: body.idempotencyKey },
    });
    return jsonError(400, 'Validation failed', { issues: dynamicValidation.errors });
  }

  // 5. Idempotenz-Check (vor Unsubscribe/Rate-Limit, damit Caller bei Retry konsistente Antwort bekommt).
  if (body.idempotencyKey) {
    const dup = findRecentTriggerByIdempotency(template.id, body.idempotencyKey);
    if (dup) {
      logTriggerEvent({
        triggerTemplateId: template.id,
        recipientEmail: to.email,
        status: 'duplicate',
        idempotencyKey: body.idempotencyKey,
        emailQueueJobId: dup.emailQueueJobId ?? undefined,
      });
      return NextResponse.json({
        ok: true,
        duplicate: true,
        queueJobId: dup.emailQueueJobId,
        message: 'Trigger mit identischem idempotencyKey bereits in den letzten 24h verarbeitet',
      });
    }
  }

  // 6. Unsubscribe-Check
  if (isUnsubscribed(to.email)) {
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: to.email,
      status: 'rejected_unsubscribed',
      idempotencyKey: body.idempotencyKey,
      requestPayloadJson: { to: body.to, data: body.data, idempotencyKey: body.idempotencyKey },
    });
    return jsonError(400, 'Recipient has unsubscribed and cannot receive emails');
  }

  // 7. Rate-Limit (optional)
  if (template.rateLimitPerHour && exceedsRateLimit(template.id, template.rateLimitPerHour)) {
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: to.email,
      status: 'rejected_rate_limit',
      idempotencyKey: body.idempotencyKey,
      errorMessage: `Rate limit ${template.rateLimitPerHour}/h überschritten`,
    });
    return jsonError(429, `Rate limit exceeded: max ${template.rateLimitPerHour} triggers/hour for this template`);
  }

  // 8. Enqueue + Stats
  try {
    const { queueJobId } = enqueueTriggerSend({ template, to, data: body.data });
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: to.email,
      status: 'queued',
      idempotencyKey: body.idempotencyKey,
      emailQueueJobId: queueJobId,
      requestPayloadJson: { to: body.to, data: body.data, idempotencyKey: body.idempotencyKey },
    });
    incrementTriggerStats(template.id);

    return NextResponse.json({
      ok: true,
      queueJobId,
      status: 'queued',
      estimatedDeliverySeconds: 8,
      message: 'Email enqueued. Worker delivers within ~5-8s if running.',
    });
  } catch (err) {
    const errMsg = err instanceof Error ? err.message : String(err);
    console.error(`[/api/trigger/${key}] enqueue error:`, err);
    logTriggerEvent({
      triggerTemplateId: template.id,
      recipientEmail: to.email,
      status: 'rejected_validation',
      idempotencyKey: body.idempotencyKey,
      errorMessage: `Enqueue failed: ${errMsg}`,
    });
    return jsonError(500, 'Internal error during enqueue', { detail: errMsg });
  }
}
