"use server";

import { execFile } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { z } from "zod";
import { getViewerContext } from "@/lib/auth";
import { ROLES, ROUTES } from "@/lib/constants";
import { env } from "@/lib/env";
import {
  getLocalDb,
  saveLocalDb,
  saveLocalThumbnail,
  upsertLocalDocumentMetadata,
  upsertLocalEventMetadataOverride,
} from "@/lib/local-db";
import { encodePdfMetaAsset } from "@/lib/storage";
import { createClient } from "@/lib/supabase/server";
import { slugify } from "@/lib/utils";

const execFileAsync = promisify(execFile);

const metadataSchema = z.object({
  sentEmailName: z.string().trim().max(512).optional(),
  approvedDocumentName: z.string().trim().max(512).optional(),
  approvedDocumentNames: z.string().trim().max(20000).optional(),
  friendlyTitle: z.string().trim().max(256).optional(),
  brand: z.string().trim().max(128).optional(),
  brandCustom: z.string().trim().max(128).optional(),
  notes: z.string().trim().max(4000).optional(),
  campaignGroup: z.string().trim().max(128).optional(),
  groupKey: z.string().trim().max(256).optional(),
  returnTo: z.string().trim().max(1024).optional(),
  thumbnailIntent: z.enum(["remove"]).optional(),
});

const MAX_METADATA_UPLOAD_BYTES = 15 * 1024 * 1024;
const ALLOWED_METADATA_MIME = new Set([
  "image/png",
  "image/jpeg",
  "image/webp",
  "application/pdf",
]);
const ALLOWED_METADATA_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "pdf"]);

function isSupportedMetadataAsset(file: File) {
  const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
  return ALLOWED_METADATA_MIME.has(file.type) || ALLOWED_METADATA_EXTENSIONS.has(ext);
}

function extensionOf(fileName: string) {
  const ext = fileName.split(".").pop()?.toLowerCase();
  return ext && ext.length <= 8 ? ext : "png";
}

function isPdfFile(file: File) {
  const name = file.name.toLowerCase();
  return file.type === "application/pdf" || name.endsWith(".pdf");
}

function normalizeUploadPage(raw: FormDataEntryValue | null) {
  const value = Number(String(raw ?? "1"));
  if (!Number.isFinite(value)) return 1;
  return Math.max(1, Math.min(500, Math.floor(value)));
}

async function renderPdfPreviewPng(pdfBytes: Uint8Array, page: number) {
  const tempDir = await mkdtemp(path.join(tmpdir(), "email-dashboards-pdf-"));
  const inputPath = path.join(tempDir, "input.pdf");
  const outputPrefix = path.join(tempDir, "preview");
  const outputPath = `${outputPrefix}.png`;

  try {
    await writeFile(inputPath, pdfBytes);
    await execFileAsync("pdftoppm", [
      "-f",
      String(page),
      "-l",
      String(page),
      "-singlefile",
      "-png",
      inputPath,
      outputPrefix,
    ]);

    const png = await readFile(outputPath);
    return new Uint8Array(png);
  } catch {
    // Graceful fallback if poppler is missing or PDF rendering fails.
    return null;
  } finally {
    await rm(tempDir, { recursive: true, force: true });
  }
}

function parseApprovedDocumentNames(
  singleValue: string | undefined,
  encodedList: string | undefined,
) {
  const names = new Set<string>();
  if (singleValue?.trim()) {
    names.add(singleValue.trim());
  }

  if (encodedList?.trim()) {
    try {
      const parsed = JSON.parse(encodedList) as unknown;
      if (Array.isArray(parsed)) {
        for (const item of parsed) {
          const value = String(item ?? "").trim();
          if (value) names.add(value);
        }
      }
    } catch {
      // ignore malformed list and fallback to single value
    }
  }

  return [...names];
}

export async function saveMetadataAction(formData: FormData) {
  const viewer = await getViewerContext();
  const isAdmin = viewer.role === ROLES.admin;

  const parsed = metadataSchema.safeParse({
    sentEmailName: formData.get("sent_email_name"),
    approvedDocumentName: formData.get("approved_document_name"),
    approvedDocumentNames: formData.get("approved_document_names"),
    friendlyTitle: formData.get("friendly_title"),
    brand: formData.get("brand"),
    brandCustom: formData.get("brand_custom"),
    notes: formData.get("notes"),
    campaignGroup: formData.get("campaign_group"),
    groupKey: formData.get("group_key"),
    returnTo: formData.get("return_to"),
    thumbnailIntent: formData.get("thumbnail_intent") ?? undefined,
  });

  if (!parsed.success) {
    throw new Error(parsed.error.issues[0]?.message ?? "Invalid metadata payload.");
  }

  const {
    sentEmailName,
    approvedDocumentName,
    friendlyTitle,
    brand,
    brandCustom,
    notes,
    campaignGroup,
    groupKey,
    returnTo,
    thumbnailIntent,
  } = parsed.data;
  const approvedDocumentNames = parseApprovedDocumentNames(
    approvedDocumentName,
    parsed.data.approvedDocumentNames,
  );
  const targetSentEmailName = (sentEmailName ?? "").trim();
  const targetApprovedDocumentName = approvedDocumentNames[0] ?? "";
  const effectiveBrand = ((brandCustom ?? "").trim() || (brand ?? "").trim()) || null;
  const targetMode = isAdmin
    ? approvedDocumentNames.length > 0
      ? "document"
      : targetSentEmailName.length > 0
        ? "event"
        : null
    : targetSentEmailName.length > 0
      ? "event"
      : null;

  if (!targetMode) {
    throw new Error(
      isAdmin
        ? "Either approved_document_name or sent_email_name is required."
        : "Regional metadata requires a sent_email_name event.",
    );
  }

  const upload = formData.get("thumbnail");
  const thumbnailPage = normalizeUploadPage(formData.get("thumbnail_page"));
  const shouldClearThumbnail = isAdmin && thumbnailIntent === "remove";
  let thumbnailPath: string | null | undefined;

  if (!isAdmin && upload instanceof File && upload.size > 0) {
    throw new Error("Thumbnail uploads are admin-only.");
  }
  if (!isAdmin && thumbnailIntent === "remove") {
    throw new Error("Thumbnail updates are admin-only.");
  }

  if (!env.hasSupabasePublicEnv) {
    const db = await getLocalDb();

    if (!isAdmin) {
      const scopedEvent = db.raw_email_events.find(
        (event) =>
          event.sent_email_name === targetSentEmailName &&
          viewer.allowedCountries.includes(event.primary_country_code.toUpperCase()),
      );
      if (!scopedEvent) {
        throw new Error("You cannot edit metadata for this event.");
      }
    }

    if (shouldClearThumbnail) {
      thumbnailPath = null;
    } else if (isAdmin && upload instanceof File && upload.size > 0) {
      if (!isSupportedMetadataAsset(upload)) {
        throw new Error("Unsupported asset type. Upload PNG, JPG, WEBP or PDF.");
      }
      if (upload.size > MAX_METADATA_UPLOAD_BYTES) {
        throw new Error("Asset too large. Max size is 15MB.");
      }
      const base = targetMode === "document" ? targetApprovedDocumentName : targetSentEmailName;

      if (isPdfFile(upload)) {
        const fileBytes = new Uint8Array(await upload.arrayBuffer());
        const pdfPath = await saveLocalThumbnail(base, upload.name, fileBytes);
        const previewBytes = await renderPdfPreviewPng(fileBytes, thumbnailPage);
        const previewPath = previewBytes
          ? await saveLocalThumbnail(base, `${slugify(base)}-p${thumbnailPage}.png`, previewBytes)
          : null;

        thumbnailPath = encodePdfMetaAsset({
          pdfPath,
          previewPath,
          page: thumbnailPage,
        });
      } else {
        thumbnailPath = await saveLocalThumbnail(
          base,
          upload.name,
          new Uint8Array(await upload.arrayBuffer()),
        );
      }
    }

    if (targetMode === "document") {
      for (const documentName of approvedDocumentNames) {
        upsertLocalDocumentMetadata(db, {
          approved_document_name: documentName,
          friendly_title: friendlyTitle || null,
          brand: effectiveBrand,
          notes: notes || null,
          campaign_group: campaignGroup || null,
          ...(thumbnailPath !== undefined ? { thumbnail_path: thumbnailPath } : {}),
        });
      }
    } else {
      upsertLocalEventMetadataOverride(db, {
        sent_email_name: targetSentEmailName,
        friendly_title: friendlyTitle || null,
        brand: effectiveBrand,
        notes: notes || null,
        campaign_group: campaignGroup || null,
        ...(thumbnailPath !== undefined ? { thumbnail_path: thumbnailPath } : {}),
      });
    }
    await saveLocalDb(db);
  } else {
    const supabase = await createClient();

    if (!isAdmin) {
      const { data: scopedEvent, error: scopedEventError } = await supabase
        .from("raw_email_events")
        .select("sent_email_name")
        .eq("sent_email_name", targetSentEmailName)
        .maybeSingle();
      if (scopedEventError) throw new Error(scopedEventError.message);
      if (!scopedEvent) {
        throw new Error("You cannot edit metadata for this event.");
      }
    }

    if (shouldClearThumbnail) {
      thumbnailPath = null;
    } else if (isAdmin && upload instanceof File && upload.size > 0) {
      if (!isSupportedMetadataAsset(upload)) {
        throw new Error("Unsupported asset type. Upload PNG, JPG, WEBP or PDF.");
      }
      if (upload.size > MAX_METADATA_UPLOAD_BYTES) {
        throw new Error("Asset too large. Max size is 15MB.");
      }
      const base = targetApprovedDocumentName || targetSentEmailName;

      if (isPdfFile(upload)) {
        const fileBytes = new Uint8Array(await upload.arrayBuffer());

        const pdfPath = `${slugify(base)}-${Date.now()}.pdf`;
        const { error: pdfUploadError } = await supabase.storage.from("thumbnails").upload(pdfPath, fileBytes, {
          upsert: true,
          contentType: "application/pdf",
        });
        if (pdfUploadError) throw new Error(pdfUploadError.message);

        let previewPath: string | null = null;
        const previewBytes = await renderPdfPreviewPng(fileBytes, thumbnailPage);
        if (previewBytes) {
          previewPath = `${slugify(base)}-${Date.now()}-p${thumbnailPage}.png`;
          const { error: previewUploadError } = await supabase.storage
            .from("thumbnails")
            .upload(previewPath, previewBytes, {
              upsert: true,
              contentType: "image/png",
            });
          if (previewUploadError) throw new Error(previewUploadError.message);
        }

        thumbnailPath = encodePdfMetaAsset({
          pdfPath,
          previewPath,
          page: thumbnailPage,
        });
      } else {
        const ext = extensionOf(upload.name);
        const imagePath = `${slugify(base)}-${Date.now()}.${ext}`;
        const { error: uploadError } = await supabase.storage.from("thumbnails").upload(imagePath, upload, {
          upsert: true,
          contentType: upload.type || "image/png",
        });
        if (uploadError) throw new Error(uploadError.message);
        thumbnailPath = imagePath;
      }
    }

    const { error } =
      targetMode === "document"
        ? await supabase.from("email_metadata").upsert(
            approvedDocumentNames.map((documentName) => ({
              approved_document_name: documentName,
              friendly_title: friendlyTitle || null,
              brand: effectiveBrand,
              notes: notes || null,
              campaign_group: campaignGroup || null,
              ...(thumbnailPath !== undefined ? { thumbnail_path: thumbnailPath } : {}),
            })),
            { onConflict: "approved_document_name" },
          )
        : await supabase.from("email_event_metadata_overrides").upsert(
            {
              sent_email_name: targetSentEmailName,
              friendly_title: friendlyTitle || null,
              brand: effectiveBrand,
              notes: notes || null,
              campaign_group: campaignGroup || null,
              ...(thumbnailPath !== undefined ? { thumbnail_path: thumbnailPath } : {}),
            },
            { onConflict: "sent_email_name" },
          );

    if (error) throw new Error(error.message);
  }

  revalidatePath(ROUTES.metadata);
  revalidatePath(ROUTES.dashboard);

  let destination: string = ROUTES.metadata;
  if (returnTo && returnTo.startsWith(ROUTES.metadata)) {
    destination = returnTo;
  }

  const redirectUrl = new URL(destination, "http://localhost");
  if (groupKey) {
    redirectUrl.searchParams.set("group", groupKey);
  }
  redirectUrl.searchParams.set("saved", "1");

  redirect(`${redirectUrl.pathname}${redirectUrl.search}`);
}
