"use server";

import { revalidatePath } from "next/cache";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { db } from "@/db";
import { projects, clients, payments, project_offers } from "@/db/schema";
import { eq } from "drizzle-orm";
import { z } from "zod";
import { nanoid } from "nanoid";

async function requireAdmin() {
  const session = await getServerSession(authOptions);
  if (!session) throw new Error("Non autorizzato");
}

export async function createProject(fd: FormData): Promise<{ projectId: string }> {
  await requireAdmin();
  const name = String(fd.get("name") ?? "").trim();
  const clientId = String(fd.get("client_id") ?? "").trim();

  if (!name) throw new Error("Nome progetto obbligatorio");
  if (!clientId) throw new Error("Cliente obbligatorio");

  const clientRows = await db
    .select({ id: clients.id })
    .from(clients)
    .where(eq(clients.id, clientId))
    .limit(1);
  if (clientRows.length === 0) throw new Error("Cliente non trovato");

  const id = nanoid();
  await db.insert(projects).values({ id, client_id: clientId, name });

  revalidatePath("/admin/projects");
  revalidatePath(`/admin/clients/${clientId}`);
  return { projectId: id };
}

export async function archiveProject(projectId: string): Promise<void> {
  await requireAdmin();
  await db.update(projects).set({ archived: true }).where(eq(projects.id, projectId));
  revalidatePath("/admin/projects");
}

export async function unarchiveProject(projectId: string): Promise<void> {
  await requireAdmin();
  await db.update(projects).set({ archived: false }).where(eq(projects.id, projectId));
  revalidatePath("/admin/projects");
}

export async function updateProjectAcceptedTotal(projectId: string, acceptedTotal: string): Promise<void> {
  await requireAdmin();
  await db.update(projects).set({ accepted_total: acceptedTotal }).where(eq(projects.id, projectId));
  revalidatePath(`/admin/projects/${projectId}`);
}

// Creates Acconto 50% + Saldo 50% stubs for a project that has no payments yet.
export async function initProjectPayments(projectId: string): Promise<void> {
  await requireAdmin();
  const rows = await db
    .select({ accepted_total: projects.accepted_total })
    .from(projects)
    .where(eq(projects.id, projectId))
    .limit(1);
  if (!rows[0]) throw new Error("Progetto non trovato");
  const total = parseFloat(rows[0].accepted_total ?? "0");
  const half = (total / 2).toFixed(2);
  await db.insert(payments).values([
    { project_id: projectId, label: "Acconto 50%", amount: half, status: "da_saldare" },
    { project_id: projectId, label: "Saldo 50%", amount: half, status: "da_saldare" },
  ]);
  revalidatePath(`/admin/projects/${projectId}`);
}

// Splits one payment into two with custom amounts. The original row keeps
// its label (+ " – Rata 1"), a new row is created with the remainder.
export async function splitPayment(
  paymentId: string,
  projectId: string,
  firstAmountStr: string
): Promise<void> {
  await requireAdmin();
  const first = parseFloat(firstAmountStr);
  if (isNaN(first) || first <= 0) throw new Error("Importo non valido");

  const rows = await db.select().from(payments).where(eq(payments.id, paymentId)).limit(1);
  if (!rows[0]) throw new Error("Pagamento non trovato");

  const original = rows[0];
  const total = parseFloat(original.amount);
  const second = parseFloat((total - first).toFixed(2));
  if (second <= 0) throw new Error("Il primo importo deve essere inferiore al totale");

  // Strip any existing " – Rata N" suffix so re-splits stay clean.
  const baseLabel = original.label.replace(/ – Rata \d+$/, "");

  await db
    .update(payments)
    .set({ amount: first.toFixed(2), label: `${baseLabel} – Rata 1` })
    .where(eq(payments.id, paymentId));

  await db.insert(payments).values({
    project_id: projectId,
    label: `${baseLabel} – Rata 2`,
    amount: second.toFixed(2),
    status: original.status,
  });

  revalidatePath(`/admin/projects/${projectId}`);
}

// ── Offer assignment actions ──────────────────────────────────────────────────

const assignOfferSchema = z.object({
  project_id: z.string().min(1),
  micro_id: z.string().min(1, "Seleziona una micro-offerta"),
  accepted_total: z.coerce.number().min(0).optional(),
});

export async function assignOfferToProject(formData: FormData) {
  await requireAdmin();
  const parsed = assignOfferSchema.safeParse({
    project_id: formData.get("project_id"),
    micro_id: formData.get("micro_id"),
    accepted_total: formData.get("accepted_total") || undefined,
  });
  if (!parsed.success) throw new Error(parsed.error.issues[0].message);
  await db.insert(project_offers).values({
    project_id: parsed.data.project_id,
    micro_id: parsed.data.micro_id,
    accepted_total:
      parsed.data.accepted_total !== undefined
        ? parsed.data.accepted_total.toFixed(2)
        : null,
  });
  revalidatePath(`/admin/projects/${parsed.data.project_id}`);
}

export async function removeProjectOffer(projectOfferId: string, projectId: string) {
  await requireAdmin();
  await db.delete(project_offers).where(eq(project_offers.id, projectOfferId));
  revalidatePath(`/admin/projects/${projectId}`);
}

export async function updateProjectOfferTotal(
  projectOfferId: string,
  projectId: string,
  accepted_total: string
) {
  await requireAdmin();
  const amount = parseFloat(accepted_total);
  if (isNaN(amount) || amount < 0) throw new Error("Importo non valido");
  await db
    .update(project_offers)
    .set({ accepted_total: amount.toFixed(2) })
    .where(eq(project_offers.id, projectOfferId));
  revalidatePath(`/admin/projects/${projectId}`);
}