import { NextResponse } from 'next/server';
import { eq } from 'drizzle-orm';
import { z } from 'zod';

import { withAuth, withPermission } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { db } from '@/lib/db';
import { ports } from '@/lib/db/schema/ports';
import { sendEmail } from '@/lib/email';
import { getBrandingShell } from '@/lib/email/branding-resolver';
import { findTestTemplate, TEST_TEMPLATES } from '@/lib/email/test-registry';
import { errorResponse, NotFoundError, ValidationError } from '@/lib/errors';

const bodySchema = z.object({
  templateId: z.string().min(1),
  recipient: z.string().email(),
});

/**
 * GET - return the test-template registry (id + label + description)
 * so the admin UI dropdown can render without duplicating the catalog
 * client-side.
 */
export const GET = withAuth(
  withPermission('admin', 'manage_settings', async () => {
    try {
      return NextResponse.json({
        data: TEST_TEMPLATES.map((t) => ({
          id: t.id,
          label: t.label,
          description: t.description,
        })),
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);

/**
 * POST - render the chosen template with realistic sample fixtures and
 * fire it through the configured SMTP transport. Used by admins to
 * preview each transactional template against a designated address
 * without triggering the real upstream flow.
 *
 * Permission: `admin.manage_settings` - same gate as the existing
 * SMTP test-send (the port's real From / SMTP credentials are used).
 */
export const POST = withAuth(
  withPermission('admin', 'manage_settings', async (req, ctx) => {
    try {
      const body = await parseBody(req, bodySchema);
      const template = findTestTemplate(body.templateId);
      if (!template) {
        throw new ValidationError(`Unknown templateId: ${body.templateId}`);
      }

      // Resolve port branding context so the rendered email actually
      // matches the admin's port (header logo, accent colour) instead of
      // falling through to defaults.
      const port = await db.query.ports.findFirst({ where: eq(ports.id, ctx.portId) });
      if (!port) throw new NotFoundError('Port');

      // No publicUrl column on `ports` yet - synthesise a plausible URL
      // from the slug so the sample renders with a "real-looking" base.
      const portUrl = `https://${port.slug}.example`;
      // Resolve the per-port branding shell (logo, blur background, accent,
      // header/footer HTML) so the preview matches the live production look
      // - without this the email falls back to the neutral default shell
      // and admins see a logo-less, background-less email.
      const branding = await getBrandingShell(ctx.portId);
      const rendered = await template.render({
        recipientName: 'Sample Recipient',
        recipientEmail: body.recipient,
        portName: port.name,
        portUrl,
        branding,
      });

      // Subject prefix makes it visually unambiguous in the recipient's
      // inbox that this is a test - important because some of the
      // templates (signing reminder, etc.) would otherwise look
      // identical to a real production send.
      const taggedSubject = `[TEST · ${template.label}] ${rendered.subject}`;

      const info = await sendEmail(
        body.recipient,
        taggedSubject,
        rendered.html,
        undefined,
        rendered.text,
        ctx.portId,
      );

      return NextResponse.json({
        data: {
          templateId: template.id,
          recipient: body.recipient,
          subject: taggedSubject,
          messageId: info.messageId ?? null,
        },
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
