import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { buildMjmlFromStrategy } from '@/modules/editor/lib/render-template';
import { renderMjml } from '@/modules/editor/lib/render-mjml';
import { runPreflight } from '@/modules/spam-check/lib/preflight';
import { getWorkspaceBySlug } from '@/lib/db/repositories';
import { readBrandKit } from '@/lib/db/defaults';
import { getSectionTemplate } from '@/lib/db/section-template-repo';
import { docToHtml, type TipTapDoc } from '@/modules/briefing/lib/sections-to-tiptap';
import {
  injectExtraSections,
  type ResolvedSection,
  type SectionBodyMode,
  type SectionPosition,
} from '@/modules/editor/lib/inject-sections';
import { sanitizeSectionHtml, extractMjml } from '@/modules/editor/lib/sanitize-section-html';
import { withAuth } from '@/core/auth';

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

const StrategySchema = z.object({
  subject_variants: z.array(z.string()).min(1).max(3),
  preheader: z.string(),
  hero_headline: z.string(),
  intro: z.string(),
  pain_point: z.string(),
  solution: z.string(),
  benefits: z.array(z.string()),
  cta_primary_text: z.string(),
  cta_secondary_text: z.string().optional(),
  ps_line: z.string(),
});

const RequestSchema = z.object({
  strategy: StrategySchema,
  workspaceSlug: z.string(),
  ctaPrimaryUrl: z.string(),
  ctaSecondaryUrl: z.string().optional(),
  heroUrl: z.string().optional(),
  heroEnabled: z.boolean().default(true),
  /** Optional: max. Hero-Bild-Höhe in px. Wenn gesetzt → MJML rendert mit object-fit:cover-Crop. */
  heroHeight: z.number().int().min(80).max(800).optional(),
  /** Optional: Crop-Position für object-position. Default 'center' wenn nicht gesetzt. */
  heroCropPosition: z.enum(['top', 'center', 'bottom']).optional(),
  preserveBodyHtml: z.string().optional(),
  /**
   * Modulare Zusatz-Sections — gleiches Schema wie in /api/generate. Re-Render-Pfad muss
   * sie mitziehen, sonst verschwinden Custom-Sections (z.B. App-Banner) sobald der User
   * den Body editiert (debounced Re-Render alle 600ms).
   */
  extraSections: z
    .array(
      z.object({
        instanceId: z.string().min(1),
        templateId: z.string().min(1),
        kind: z.string().min(1),
        position: z.enum(['before_cta', 'after_cta', 'before_signature', 'after_signature']),
        contentJson: z.object({ type: z.literal('doc'), content: z.array(z.record(z.string(), z.unknown())) }),
        bodyMode: z.enum(['tiptap', 'mjml_raw', 'html_raw']).optional(),
        customBody: z.string().optional(),
      }),
    )
    .optional(),
});

// Workspaces kommen jetzt aus der DB via getWorkspaceBySlug() — siehe Phase 1.5

export const POST = withAuth(async (req: NextRequest, _ctx) => {
  try {
    const start = Date.now();
    const body = await req.json();
    const parsed = RequestSchema.safeParse(body);
    if (!parsed.success) {
      const issues = parsed.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`);
      return NextResponse.json({ error: 'Invalid input', issues }, { status: 400 });
    }
    const { strategy, workspaceSlug, ctaPrimaryUrl, ctaSecondaryUrl, heroUrl, heroEnabled, heroHeight, heroCropPosition, preserveBodyHtml, extraSections } = parsed.data;
    const workspace = getWorkspaceBySlug(workspaceSlug);
    if (!workspace) {
      return NextResponse.json({ error: `Unknown workspace: ${workspaceSlug}` }, { status: 400 });
    }

    const kit = readBrandKit(workspace.brandKitJson);
    let mjml = buildMjmlFromStrategy({
      strategy,
      workspace: {
        name: workspace.name,
        primaryColor: workspace.primaryColor,
        footerText: workspace.footerText,
        logoUrl: workspace.logoUrl ?? undefined,
        headerPhone: kit.header?.phone,
        headerTagline: kit.header?.tagline,
        footerLegalText: kit.footer?.legalFooterText,
        footerLegalTextEn: kit.footer?.legalFooterTextEn,
        footerConfidentialityNotice: kit.footer?.confidentialityNotice,
        footerConfidentialityNoticeEn: kit.footer?.confidentialityNoticeEn,
        footerShowUnsubscribe: kit.footer?.showUnsubscribe,
        footerCompanyInfo: kit.footer?.companyInfo,
        footerLegalRegulations: kit.footer?.legalRegulations,
        footerComplaintBodies: kit.footer?.complaintBodies,
        footerMjml: kit.footer?.mode === 'mjml' ? kit.footer?.customMjml : undefined,
        footerDefaultSignature: kit.footer?.defaultSignature,
        footerDefaultSignatureEn: kit.footer?.defaultSignatureEn,
      },
      ctaPrimaryUrl,
      ctaSecondaryUrl,
      heroUrl,
      heroEnabled,
      heroHeight,
      heroCropPosition,
      preserveBodyHtml,
    });

    // Extra-Sections beim Re-Render mitziehen — gleiche Resolve-Logik wie in /api/generate.
    // Ohne diesen Block würden Custom-Sections (App-Banner, Kontakt, Testimonial, FAQ) bei
    // jedem TipTap-Edit verschwinden, weil das Frontend /api/render alle 600ms triggert.
    if (extraSections?.length) {
      const resolvedSections: ResolvedSection[] = [];
      for (const inst of extraSections) {
        const tpl = getSectionTemplate(inst.templateId);
        if (!tpl) {
          console.warn(`[/api/render] unknown section template: ${inst.templateId}, skipping`);
          continue;
        }
        if (tpl.workspaceId !== workspace.id) {
          console.warn(`[/api/render] section template belongs to other workspace: ${inst.templateId}, skipping`);
          continue;
        }
        const tplBodyMode = (tpl.bodyMode ?? 'tiptap') as SectionBodyMode;
        const effectiveBodyMode: SectionBodyMode = inst.bodyMode ?? tplBodyMode;

        let mjmlBlock: string;
        let contentHtml = '';
        let headStyles: string | undefined;

        if (effectiveBodyMode === 'mjml_raw') {
          const customBody = (inst.customBody ?? '').trim();
          if (!customBody) {
            mjmlBlock = tpl.mjmlBlock;
            contentHtml = docToHtml(inst.contentJson as unknown as TipTapDoc);
          } else {
            const extracted = extractMjml(customBody);
            mjmlBlock = extracted.body;
            headStyles = extracted.styles || undefined;
          }
        } else if (effectiveBodyMode === 'html_raw') {
          const customBody = (inst.customBody ?? '').trim();
          if (!customBody) {
            mjmlBlock = tpl.mjmlBlock;
            contentHtml = docToHtml(inst.contentJson as unknown as TipTapDoc);
          } else {
            const { html } = sanitizeSectionHtml(customBody);
            mjmlBlock = `<mj-raw>${html}</mj-raw>`;
          }
        } else {
          mjmlBlock = tpl.mjmlBlock;
          contentHtml = docToHtml(inst.contentJson as unknown as TipTapDoc);
        }

        resolvedSections.push({
          instanceId: inst.instanceId,
          kind: inst.kind,
          position: inst.position as SectionPosition,
          bodyMode: effectiveBodyMode,
          mjmlBlock,
          contentHtml,
          headStyles,
        });
      }

      if (resolvedSections.length > 0) {
        mjml = injectExtraSections(mjml, resolvedSections, {
          brandPrimaryColor: workspace.primaryColor,
          brandName: workspace.name,
        });
      }
    }

    // CTA-URL-Tokens nach dem Section-Inject auflösen — analog zu /api/generate's
    // pipeline.ts. Greift wenn User-Sections {{CTA_PRIMARY_URL}} referenzieren.
    const primaryHref = ctaPrimaryUrl?.trim() || '#';
    mjml = mjml.replace(/\{\{CTA_PRIMARY_URL\}\}/g, primaryHref);
    const secondaryHref = ctaSecondaryUrl?.trim() || '#';
    mjml = mjml.replace(/\{\{CTA_SECONDARY_URL\}\}/g, secondaryHref);

    const rendered = await renderMjml(mjml);
    const spam = runPreflight({
      html: rendered.html,
      text: rendered.text,
      subject: strategy.subject_variants[0],
      preheader: strategy.preheader,
      hasListUnsubscribeHeader: true,
      brandTerms: [workspace.name.toUpperCase()],
    });

    return NextResponse.json({
      ok: true,
      mjml,
      html: rendered.html,
      text: rendered.text,
      htmlBytes: rendered.bytes,
      mjmlErrors: rendered.errors,
      spam,
      meta: { durationMs: Date.now() - start, source: 'local-template' },
    });
  } catch (err) {
    console.error('[/api/render] error:', err);
    return NextResponse.json(
      { error: err instanceof Error ? err.message : 'Internal error' },
      { status: 500 },
    );
  }
});
