"use server";

import { promises as fs } from "node:fs";
import path from "node:path";
import { z } from "zod";
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { db, schema } from "@/lib/db";
import { requireAdmin } from "@/lib/guards";
import {
  fieldSchema,
  documentRequestSchema,
  type FormField,
  type DocumentRequest,
} from "@/lib/forms/types";

const UPLOADS_DIR =
  process.env.UPLOADS_DIR || path.join(process.cwd(), "uploads");

function safeFilename(name: string): string {
  const base = path.basename(name);
  return base.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 200);
}

const saveTemplateInput = z.object({
  serviceSlug: z.string().min(1),
  title: z.string().min(1, "Titre requis"),
  description: z.string().optional().nullable(),
  fields: z.array(fieldSchema),
  documentRequests: z.array(documentRequestSchema),
  published: z.boolean(),
});

export type SaveTemplateInput = z.infer<typeof saveTemplateInput>;

export async function saveFormTemplate(input: SaveTemplateInput) {
  await requireAdmin();
  const data = saveTemplateInput.parse(input);

  // Vérifier l'unicité des field IDs.
  const fieldIds = new Set<string>();
  for (const f of data.fields as FormField[]) {
    if (fieldIds.has(f.id)) {
      throw new Error(`ID de champ dupliqué : ${f.id}`);
    }
    fieldIds.add(f.id);
  }

  const docIds = new Set<string>();
  for (const d of data.documentRequests as DocumentRequest[]) {
    if (docIds.has(d.id)) {
      throw new Error(`ID de document dupliqué : ${d.id}`);
    }
    docIds.add(d.id);
  }

  const existing = await db
    .select()
    .from(schema.formTemplate)
    .where(eq(schema.formTemplate.serviceSlug, data.serviceSlug))
    .limit(1);

  if (existing.length > 0) {
    await db
      .update(schema.formTemplate)
      .set({
        title: data.title,
        description: data.description ?? null,
        fields: data.fields,
        documentRequests: data.documentRequests,
        published: data.published,
        updatedAt: new Date(),
      })
      .where(eq(schema.formTemplate.serviceSlug, data.serviceSlug));
  } else {
    await db.insert(schema.formTemplate).values({
      serviceSlug: data.serviceSlug,
      title: data.title,
      description: data.description ?? null,
      fields: data.fields,
      documentRequests: data.documentRequests,
      published: data.published,
    });
  }

  revalidatePath("/socialexadmin/dossiers/formulaires");
  revalidatePath(`/socialexadmin/dossiers/formulaires/${data.serviceSlug}`);

  return { ok: true as const };
}

/**
 * Upload d'un document d'exemple attaché à un documentRequest.
 * Le fichier sert de modèle téléchargeable côté public.
 *
 * Stockage : /var/www/html/socialex/uploads/_samples/{serviceSlug}/{docId}/{uuid}-{name}
 * Le path retourné est relatif à UPLOADS_DIR.
 */
export async function uploadSampleDocument(
  formData: FormData
): Promise<
  | { ok: true; sample: { name: string; path: string; sizeBytes: number; mimeType?: string } }
  | { ok: false; error: string }
> {
  await requireAdmin();

  const serviceSlug = formData.get("serviceSlug");
  const docId = formData.get("docId");
  const file = formData.get("file");

  if (typeof serviceSlug !== "string" || !serviceSlug)
    return { ok: false, error: "serviceSlug manquant" };
  if (typeof docId !== "string" || !docId)
    return { ok: false, error: "docId manquant" };
  if (!(file instanceof File) || file.size === 0)
    return { ok: false, error: "Fichier manquant" };
  if (file.size > 25 * 1024 * 1024)
    return { ok: false, error: "Fichier trop volumineux (max 25 Mo)" };

  const dir = path.join(UPLOADS_DIR, "_samples", serviceSlug, docId);
  await fs.mkdir(dir, { recursive: true });

  const safeName = safeFilename(file.name);
  const finalName = `${crypto.randomUUID()}-${safeName}`;
  const absPath = path.join(dir, finalName);
  const bytes = Buffer.from(await file.arrayBuffer());
  await fs.writeFile(absPath, bytes);

  return {
    ok: true,
    sample: {
      name: file.name,
      path: path.posix.join("_samples", serviceSlug, docId, finalName),
      sizeBytes: file.size,
      mimeType: file.type || undefined,
    },
  };
}

/**
 * Supprime le fichier exemple sur disk (on garde le row jsonb tel quel,
 * c'est l'admin qui sauvera le template ensuite avec sampleFile retiré).
 */
export async function deleteSampleDocumentFile(relativePath: string) {
  await requireAdmin();
  if (!relativePath.startsWith("_samples/")) {
    throw new Error("Chemin invalide");
  }
  const abs = path.join(UPLOADS_DIR, relativePath);
  try {
    await fs.unlink(abs);
  } catch {
    // déjà supprimé : on ignore
  }
  return { ok: true as const };
}
