"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { db } from "@/lib/db";
import { requireConfAccess } from "@/lib/rbac";
import { audit } from "@/lib/audit";
import { newCertId } from "@/lib/utils";
import { saveUploadedImage, fetchAndStoreImage } from "@/lib/storage";
import { fetchLinkedInProfile, normalizeLinkedInUrl } from "@/lib/linkedin";
import { hashPassword } from "@/lib/auth";
import {
  sendAnnouncementBlast,
  sendCertificateEmail,
  sendRegistrationConfirmedEmail,
  sendReviewerAssignedEmail,
  sendSubmissionDecisionEmail,
  sendVolunteerWelcomeEmail,
} from "@/lib/email";
import { randomBytes } from "crypto";

const wp = (id: string, page = "") => `/dashboard/c/${id}${page ? `/${page}` : ""}`;

// ── Registrations ─────────────────────────────────────────

export async function setRegistrationStatus(confId: string, regId: string, status: "CONFIRMED" | "CANCELLED") {
  const { user } = await requireConfAccess(confId, "registrations.manage");
  await db.registration.update({ where: { id: regId, conferenceId: confId }, data: { status } });
  if (status === "CONFIRMED") await sendRegistrationConfirmedEmail(regId);
  await audit({ userId: user.id, conferenceId: confId, action: `registration.${status.toLowerCase()}`, entity: "Registration", entityId: regId });
  revalidatePath(wp(confId, "registrations"));
}

// ── Check-in ──────────────────────────────────────────────

export async function checkInAction(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "checkin.perform");
  const code = String(formData.get("code") || "").toUpperCase().trim();
  const q = (msg: string, tone: string) => redirect(`${wp(confId, "checkin")}?msg=${encodeURIComponent(msg)}&tone=${tone}`);

  const reg = await db.registration.findUnique({ where: { regCode: code }, include: { ticketType: true, checkIns: { where: { type: "CONFERENCE" } } } });
  if (!reg || reg.conferenceId !== confId) q(`No registration found for “${code}”.`, "error");
  if (reg!.status === "CANCELLED") q(`${reg!.name} — registration is CANCELLED. Do not admit.`, "error");
  if (reg!.status === "PENDING") q(`${reg!.name} — payment PENDING. Collect payment or confirm first.`, "warn");
  if (reg!.checkIns.length > 0) q(`${reg!.name} was already checked in.`, "warn");

  await db.checkIn.create({ data: { registrationId: reg!.id, type: "CONFERENCE", checkedInBy: user.name } });
  await audit({ userId: user.id, conferenceId: confId, action: "checkin.conference", entity: "Registration", entityId: reg!.id });
  q(`✓ ${reg!.name} checked in — ${reg!.ticketType.name}${reg!.mode === "VIRTUAL" ? " (virtual attendee)" : ""}.`, "ok");
}

// ── Tickets & coupons ─────────────────────────────────────

export async function createTicketType(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "registrations.manage");
  const priceRupees = parseFloat(String(formData.get("price") || "0")) || 0;
  const quantity = String(formData.get("quantity") || "").trim();
  await db.ticketType.create({
    data: {
      conferenceId: confId,
      name: String(formData.get("name") || "Pass").trim(),
      description: String(formData.get("description") || "") || null,
      price: Math.round(priceRupees * 100),
      quantity: quantity ? parseInt(quantity, 10) : null,
      audience: String(formData.get("audience") || "ATTENDEE"),
      saleEndsAt: formData.get("saleEndsAt") ? new Date(String(formData.get("saleEndsAt"))) : null,
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "ticket.create" });
  revalidatePath(wp(confId, "tickets"));
}

export async function toggleTicketType(confId: string, ticketId: string) {
  await requireConfAccess(confId, "registrations.manage");
  const t = await db.ticketType.findFirst({ where: { id: ticketId, conferenceId: confId } });
  if (t) await db.ticketType.update({ where: { id: ticketId }, data: { active: !t.active } });
  revalidatePath(wp(confId, "tickets"));
}

export async function createCoupon(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "registrations.manage");
  const code = String(formData.get("code") || "").toUpperCase().trim();
  if (!code) return;
  const maxUses = String(formData.get("maxUses") || "").trim();
  try {
    await db.coupon.create({
      data: {
        conferenceId: confId,
        code,
        discountType: String(formData.get("discountType") || "PERCENT"),
        value: parseInt(String(formData.get("value") || "0"), 10) || 0,
        maxUses: maxUses ? parseInt(maxUses, 10) : null,
      },
    });
  } catch { /* duplicate code — ignore */ }
  await audit({ userId: user.id, conferenceId: confId, action: "coupon.create", meta: { code } });
  revalidatePath(wp(confId, "tickets"));
}

export async function toggleCoupon(confId: string, couponId: string) {
  await requireConfAccess(confId, "registrations.manage");
  const c = await db.coupon.findFirst({ where: { id: couponId, conferenceId: confId } });
  if (c) await db.coupon.update({ where: { id: couponId }, data: { active: !c.active } });
  revalidatePath(wp(confId, "tickets"));
}

// ── Agenda: tracks, rooms, sessions (conflict detection) ──

export async function createTrack(confId: string, formData: FormData) {
  await requireConfAccess(confId, "agenda.manage");
  const name = String(formData.get("name") || "").trim();
  if (name) await db.track.create({ data: { conferenceId: confId, name, color: String(formData.get("color") || "#4f46e5") } });
  revalidatePath(wp(confId, "agenda"));
}

export async function createRoom(confId: string, formData: FormData) {
  await requireConfAccess(confId, "agenda.manage");
  const name = String(formData.get("name") || "").trim();
  const cap = String(formData.get("capacity") || "").trim();
  if (name) await db.room.create({ data: { conferenceId: confId, name, capacity: cap ? parseInt(cap, 10) : null } });
  revalidatePath(wp(confId, "agenda"));
}

export async function createSession(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  const title = String(formData.get("title") || "").trim();
  const startAt = new Date(String(formData.get("startAt")));
  const endAt = new Date(String(formData.get("endAt")));
  const roomId = String(formData.get("roomId") || "") || null;
  const trackId = String(formData.get("trackId") || "") || null;
  const speakerIds = formData.getAll("speakerIds").map(String).filter(Boolean);
  const day = String(formData.get("day") || "");
  const err = (msg: string) => redirect(`${wp(confId, "agenda")}?day=${day}&error=${encodeURIComponent(msg)}`);

  if (!title) err("Session title is required.");
  if (isNaN(startAt.getTime()) || isNaN(endAt.getTime()) || endAt <= startAt) err("End time must be after start time.");

  // Conflict detection: same room, overlapping window
  if (roomId) {
    const clash = await db.session.findFirst({
      where: { conferenceId: confId, roomId, startAt: { lt: endAt }, endAt: { gt: startAt } },
      include: { room: true },
    });
    if (clash) err(`Room conflict: “${clash.title}” already occupies ${clash.room?.name} in that time window.`);
  }
  // Speaker double-booking
  if (speakerIds.length) {
    const clash = await db.session.findFirst({
      where: {
        conferenceId: confId,
        startAt: { lt: endAt },
        endAt: { gt: startAt },
        speakers: { some: { speakerId: { in: speakerIds } } },
      },
      include: { speakers: { include: { speaker: true } } },
    });
    if (clash) {
      const who = clash.speakers.find((s) => speakerIds.includes(s.speakerId))?.speaker.name;
      err(`Speaker conflict: ${who} is already scheduled in “${clash.title}” at that time.`);
    }
  }

  await db.session.create({
    data: {
      conferenceId: confId,
      title,
      abstract: String(formData.get("abstract") || "") || null,
      type: String(formData.get("type") || "TALK"),
      mode: String(formData.get("mode") || "PHYSICAL"),
      startAt,
      endAt,
      roomId,
      trackId,
      speakers: { create: speakerIds.map((speakerId) => ({ speakerId })) },
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "session.create", meta: { title } });
  revalidatePath(wp(confId, "agenda"));
  redirect(`${wp(confId, "agenda")}?day=${day}`);
}

export async function deleteSession(confId: string, sessionId: string) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  await db.session.delete({ where: { id: sessionId, conferenceId: confId } });
  await audit({ userId: user.id, conferenceId: confId, action: "session.delete", entityId: sessionId });
  revalidatePath(wp(confId, "agenda"));
}

// ── Speakers ──────────────────────────────────────────────

function socialLinksFrom(formData: FormData): string | null {
  const links: Record<string, string> = {};
  for (const [key, field] of [["linkedin", "linkedin"], ["x", "xUrl"], ["website", "otherUrl"]] as const) {
    const v = String(formData.get(field) || "").trim();
    if (v) links[key] = v.startsWith("http") ? v : `https://${v}`;
  }
  return Object.keys(links).length ? JSON.stringify(links) : null;
}

async function speakerFields(formData: FormData) {
  return {
    name: String(formData.get("name") || "").trim(),
    email: String(formData.get("email") || "") || null,
    organization: String(formData.get("organization") || "") || null,
    designation: String(formData.get("designation") || "") || null,
    bio: String(formData.get("bio") || "") || null,
    speakerType: String(formData.get("speakerType") || "SPEAKER"),
    featured: formData.get("featured") === "on",
    socialLinks: socialLinksFrom(formData),
    photoUrl: await saveUploadedImage(formData.get("photo")), // null when no file chosen
  };
}

export async function createSpeaker(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "speakers.manage");
  let fields;
  try {
    fields = await speakerFields(formData);
  } catch (e) {
    redirect(`${wp(confId, "speakers")}?error=${encodeURIComponent((e as Error).message)}`);
  }
  if (!fields!.name) return;
  await db.speaker.create({ data: { conferenceId: confId, ...fields! } });
  await audit({ userId: user.id, conferenceId: confId, action: "speaker.create", meta: { name: fields!.name } });
  revalidatePath(wp(confId, "speakers"));
  redirect(wp(confId, "speakers"));
}

export async function updateSpeaker(confId: string, speakerId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "speakers.manage");
  let fields;
  try {
    fields = await speakerFields(formData);
  } catch (e) {
    redirect(`${wp(confId, "speakers")}?edit=${speakerId}&error=${encodeURIComponent((e as Error).message)}`);
  }
  const { photoUrl, ...rest } = fields!;
  await db.speaker.update({
    where: { id: speakerId, conferenceId: confId },
    data: { ...rest, name: rest.name || undefined, ...(photoUrl ? { photoUrl } : {}) }, // keep existing photo unless replaced
  });
  await audit({ userId: user.id, conferenceId: confId, action: "speaker.update", entityId: speakerId });
  revalidatePath(wp(confId, "speakers"));
  redirect(wp(confId, "speakers"));
}

/** USP: paste a LinkedIn profile URL → best-effort import (og tags / JSON-LD, photo download, slug fallback). */
export async function importSpeakerFromLinkedIn(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "speakers.manage");
  const back = wp(confId, "speakers");
  const url = normalizeLinkedInUrl(String(formData.get("linkedinUrl") || ""));
  if (!url) redirect(`${back}?error=${encodeURIComponent("That doesn't look like a LinkedIn profile URL (expected linkedin.com/in/…).")}`);

  const profile = await fetchLinkedInProfile(url!);
  if (!profile.name) redirect(`${back}?error=${encodeURIComponent("Couldn't derive a name from that profile URL.")}`);

  const photoUrl = profile.photoUrl ? await fetchAndStoreImage(profile.photoUrl) : null;
  const speaker = await db.speaker.create({
    data: {
      conferenceId: confId,
      name: profile.name!,
      designation: profile.designation ?? null,
      organization: profile.organization ?? null,
      bio: profile.bio ?? null,
      photoUrl,
      socialLinks: JSON.stringify({ linkedin: url }),
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "speaker.import.linkedin", entityId: speaker.id, meta: { url, partial: profile.partial } });
  revalidatePath(wp(confId, "speakers"));
  redirect(`${back}?imported=${encodeURIComponent(profile.name!)}${profile.partial ? "&partial=1" : ""}&edit=${speaker.id}`);
}

export async function deleteSpeaker(confId: string, speakerId: string) {
  await requireConfAccess(confId, "speakers.manage");
  await db.speaker.delete({ where: { id: speakerId, conferenceId: confId } });
  revalidatePath(wp(confId, "speakers"));
}

// ── Announcements ─────────────────────────────────────────

export async function createAnnouncement(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "content.manage");
  const title = String(formData.get("title") || "").trim();
  if (!title) return;
  await db.announcement.create({
    data: {
      conferenceId: confId,
      title,
      body: String(formData.get("body") || ""),
      audience: String(formData.get("audience") || "ALL"),
      pinned: formData.get("pinned") === "on",
    },
  });
  // fan out in-app notifications to registered users with accounts
  const regs = await db.registration.findMany({
    where: { conferenceId: confId, status: "CONFIRMED", userId: { not: null } },
    select: { userId: true },
    distinct: ["userId"],
  });
  const conf = await db.conference.findUnique({ where: { id: confId }, select: { name: true, slug: true } });
  if (regs.length && conf) {
    await db.notification.createMany({
      data: regs.map((r) => ({ userId: r.userId!, title: `${conf.name}: ${title}`, body: String(formData.get("body") || "").slice(0, 200), link: `/c/${conf.slug}` })),
    });
  }
  await sendAnnouncementBlast(confId, title, String(formData.get("body") || ""));
  await audit({ userId: user.id, conferenceId: confId, action: "announcement.create", meta: { title } });
  revalidatePath(wp(confId, "announcements"));
}

// ── Certificates ──────────────────────────────────────────

export async function generateCertificates(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "certificates.manage");
  const type = String(formData.get("type") || "PARTICIPATION");
  const scope = String(formData.get("scope") || "CHECKED_IN"); // CHECKED_IN | CONFIRMED

  let created = 0;
  const issuedCertIds: string[] = [];
  if (type === "SPEAKER") {
    const speakers = await db.speaker.findMany({ where: { conferenceId: confId } });
    for (const s of speakers) {
      if (!s.email) continue;
      const exists = await db.certificate.findFirst({ where: { conferenceId: confId, recipientEmail: s.email, type } });
      if (exists) continue;
      const cert = await db.certificate.create({
        data: { certId: newCertId(), conferenceId: confId, recipientName: s.name, recipientEmail: s.email, type },
      });
      issuedCertIds.push(cert.certId);
      created++;
    }
  } else {
    const regs = await db.registration.findMany({
      where: {
        conferenceId: confId,
        status: "CONFIRMED",
        ...(scope === "CHECKED_IN" ? { checkIns: { some: { type: "CONFERENCE" } } } : {}),
      },
    });
    for (const r of regs) {
      const exists = await db.certificate.findFirst({ where: { conferenceId: confId, registrationId: r.id, type } });
      if (exists) continue;
      const cert = await db.certificate.create({
        data: { certId: newCertId(), conferenceId: confId, registrationId: r.id, recipientName: r.name, recipientEmail: r.email, type },
      });
      issuedCertIds.push(cert.certId);
      created++;
    }
  }
  await Promise.allSettled(issuedCertIds.map((cid) => sendCertificateEmail(cid)));
  await audit({ userId: user.id, conferenceId: confId, action: "certificate.generate", meta: { type, scope, created } });
  redirect(`${wp(confId, "certificates")}?generated=${created}`);
}

// ── Submissions & reviews ─────────────────────────────────

export async function decideSubmission(confId: string, submissionId: string, status: string) {
  const { user } = await requireConfAccess(confId, "submissions.manage");
  await db.submission.update({ where: { id: submissionId, conferenceId: confId }, data: { status } });
  if (["ACCEPTED", "REJECTED", "REVISION_REQUIRED"].includes(status)) await sendSubmissionDecisionEmail(submissionId);
  const sub = await db.submission.findUnique({ where: { id: submissionId }, include: { conference: true } });
  if (sub) {
    await db.notification.create({
      data: {
        userId: sub.userId,
        title: `Submission ${status.replace(/_/g, " ").toLowerCase()}: ${sub.title}`,
        body: `Your submission to ${sub.conference.name} is now ${status.replace(/_/g, " ").toLowerCase()}.`,
      },
    });
  }
  await audit({ userId: user.id, conferenceId: confId, action: `submission.${status.toLowerCase()}`, entityId: submissionId });
  revalidatePath(wp(confId, "submissions"));
}

export async function assignReviewer(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "submissions.manage");
  const submissionId = String(formData.get("submissionId"));
  const email = String(formData.get("email") || "").toLowerCase().trim();
  const reviewer = await db.user.findUnique({ where: { email } });
  if (!reviewer) redirect(`${wp(confId, "submissions")}?error=${encodeURIComponent(`No Confexe user with email ${email}. Ask them to sign up first.`)}`);
  try {
    await db.review.create({ data: { submissionId, reviewerId: reviewer!.id } });
    await db.confMember.upsert({
      where: { conferenceId_userId_role: { conferenceId: confId, userId: reviewer!.id, role: "REVIEWER" } },
      create: { conferenceId: confId, userId: reviewer!.id, role: "REVIEWER" },
      update: {},
    });
    await db.submission.updateMany({ where: { id: submissionId, status: "SUBMITTED" }, data: { status: "UNDER_REVIEW" } });
    await db.notification.create({ data: { userId: reviewer!.id, title: "New review assignment", body: "A paper has been assigned to you for review.", link: "/me" } });
    await sendReviewerAssignedEmail(email, confId);
  } catch { /* already assigned */ }
  await audit({ userId: user.id, conferenceId: confId, action: "review.assign", entityId: submissionId, meta: { reviewer: email } });
  revalidatePath(wp(confId, "submissions"));
}

// ── Sponsors ──────────────────────────────────────────────

export async function createSponsor(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "sponsors.manage");
  const name = String(formData.get("name") || "").trim();
  if (!name) return;
  const amountRupees = parseFloat(String(formData.get("amount") || "0")) || 0;
  let logoUrl: string | null = null;
  try {
    logoUrl = await saveUploadedImage(formData.get("logo"), "sponsors");
  } catch (e) {
    redirect(`${wp(confId, "sponsors")}?error=${encodeURIComponent((e as Error).message)}`);
  }
  await db.sponsor.create({
    data: {
      conferenceId: confId,
      name,
      logoUrl,
      tier: String(formData.get("tier") || "GOLD"),
      website: String(formData.get("website") || "") || null,
      description: String(formData.get("description") || "") || null,
      boothNumber: String(formData.get("boothNumber") || "") || null,
      amount: Math.round(amountRupees * 100),
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "sponsor.create", meta: { name } });
  revalidatePath(wp(confId, "sponsors"));
}

export async function deleteSponsor(confId: string, sponsorId: string) {
  await requireConfAccess(confId, "sponsors.manage");
  await db.sponsor.delete({ where: { id: sponsorId, conferenceId: confId } });
  revalidatePath(wp(confId, "sponsors"));
}

// ── Settings ──────────────────────────────────────────────

export async function updateConference(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "conference.manage");
  const num = (k: string) => { const v = String(formData.get(k) || "").trim(); return v ? parseInt(v, 10) : null; };
  const date = (k: string) => { const v = String(formData.get(k) || "").trim(); const d = new Date(v); return v && !isNaN(d.getTime()) ? d : null; };
  await db.conference.update({
    where: { id: confId },
    data: {
      name: String(formData.get("name") || "").trim() || undefined,
      tagline: String(formData.get("tagline") || "") || null,
      description: String(formData.get("description") || "") || null,
      theme: String(formData.get("theme") || "") || null,
      type: String(formData.get("type") || "HYBRID"),
      venueName: String(formData.get("venueName") || "") || null,
      venueAddress: String(formData.get("venueAddress") || "") || null,
      city: String(formData.get("city") || "") || null,
      country: String(formData.get("country") || "") || null,
      streamUrl: String(formData.get("streamUrl") || "") || null,
      capacity: num("capacity"),
      languages: String(formData.get("languages") || "") || null,
      contactEmail: String(formData.get("contactEmail") || "") || null,
      contactPhone: String(formData.get("contactPhone") || "") || null,
      primaryColor: String(formData.get("primaryColor") || "#4f46e5"),
      regOpensAt: date("regOpensAt"),
      regClosesAt: date("regClosesAt"),
      startAt: date("startAt") ?? undefined,
      endAt: date("endAt") ?? undefined,
      cfpOpen: formData.get("cfpOpen") === "on",
      cfpDeadline: date("cfpDeadline"),
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "conference.update" });
  revalidatePath(wp(confId, "settings"));
  redirect(`${wp(confId, "settings")}?saved=1`);
}

export async function setConferenceStatus(confId: string, status: string) {
  const { user } = await requireConfAccess(confId, "conference.manage");
  await db.conference.update({ where: { id: confId }, data: { status } });
  await audit({ userId: user.id, conferenceId: confId, action: `conference.status.${status.toLowerCase()}` });
  revalidatePath(wp(confId));
  revalidatePath(wp(confId, "settings"));
}

// ── Venues & halls ────────────────────────────────────────

export async function createVenue(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  const name = String(formData.get("name") || "").trim();
  if (!name) return;
  await db.venue.create({
    data: {
      conferenceId: confId,
      name,
      address: String(formData.get("address") || "") || null,
      city: String(formData.get("city") || "") || null,
      mapUrl: String(formData.get("mapUrl") || "") || null,
      description: String(formData.get("description") || "") || null,
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "venue.create", meta: { name } });
  revalidatePath(wp(confId, "venue"));
}

export async function updateVenue(confId: string, venueId: string, formData: FormData) {
  await requireConfAccess(confId, "agenda.manage");
  await db.venue.update({
    where: { id: venueId, conferenceId: confId },
    data: {
      name: String(formData.get("name") || "").trim() || undefined,
      address: String(formData.get("address") || "") || null,
      city: String(formData.get("city") || "") || null,
      mapUrl: String(formData.get("mapUrl") || "") || null,
    },
  });
  revalidatePath(wp(confId, "venue"));
}

export async function deleteVenue(confId: string, venueId: string) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  await db.venue.delete({ where: { id: venueId, conferenceId: confId } }); // halls remain, venueId set null
  await audit({ userId: user.id, conferenceId: confId, action: "venue.delete", entityId: venueId });
  revalidatePath(wp(confId, "venue"));
}

export async function createHall(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  const name = String(formData.get("name") || "").trim();
  if (!name) return;
  const cap = String(formData.get("capacity") || "").trim();
  await db.room.create({
    data: {
      conferenceId: confId,
      venueId: String(formData.get("venueId") || "") || null,
      name,
      capacity: cap ? parseInt(cap, 10) : null,
      floor: String(formData.get("floor") || "") || null,
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "hall.create", meta: { name } });
  revalidatePath(wp(confId, "venue"));
  revalidatePath(wp(confId, "agenda"));
}

export async function deleteHall(confId: string, roomId: string) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  await db.room.delete({ where: { id: roomId, conferenceId: confId } }); // sessions keep running, roomId set null
  await audit({ userId: user.id, conferenceId: confId, action: "hall.delete", entityId: roomId });
  revalidatePath(wp(confId, "venue"));
  revalidatePath(wp(confId, "agenda"));
}

// ── Track management ──────────────────────────────────────

export async function updateTrack(confId: string, trackId: string, formData: FormData) {
  await requireConfAccess(confId, "agenda.manage");
  await db.track.update({
    where: { id: trackId, conferenceId: confId },
    data: {
      name: String(formData.get("name") || "").trim() || undefined,
      color: String(formData.get("color") || "#4f46e5"),
    },
  });
  revalidatePath(wp(confId, "agenda"));
}

export async function deleteTrack(confId: string, trackId: string) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  await db.track.delete({ where: { id: trackId, conferenceId: confId } }); // sessions keep, trackId set null
  await audit({ userId: user.id, conferenceId: confId, action: "track.delete", entityId: trackId });
  revalidatePath(wp(confId, "agenda"));
}

// ── Session editing ───────────────────────────────────────

export async function updateSession(confId: string, sessionId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  const startAt = new Date(String(formData.get("startAt")));
  const endAt = new Date(String(formData.get("endAt")));
  const roomId = String(formData.get("roomId") || "") || null;
  const speakerIds = formData.getAll("speakerIds").map(String).filter(Boolean);
  const back = `${wp(confId, "agenda")}?day=${String(formData.get("day") || "")}`;
  const err = (msg: string) => redirect(`${back}&error=${encodeURIComponent(msg)}`);

  if (isNaN(startAt.getTime()) || isNaN(endAt.getTime()) || endAt <= startAt) err("End time must be after start time.");

  if (roomId) {
    const clash = await db.session.findFirst({
      where: { conferenceId: confId, roomId, id: { not: sessionId }, startAt: { lt: endAt }, endAt: { gt: startAt } },
      include: { room: true },
    });
    if (clash) err(`Room conflict: “${clash.title}” already occupies ${clash.room?.name} in that window.`);
  }
  if (speakerIds.length) {
    const clash = await db.session.findFirst({
      where: {
        conferenceId: confId,
        id: { not: sessionId },
        startAt: { lt: endAt },
        endAt: { gt: startAt },
        speakers: { some: { speakerId: { in: speakerIds } } },
      },
      include: { speakers: { include: { speaker: true } } },
    });
    if (clash) {
      const who = clash.speakers.find((s) => speakerIds.includes(s.speakerId))?.speaker.name;
      err(`Speaker conflict: ${who} is already in “${clash.title}” at that time.`);
    }
  }

  await db.session.update({
    where: { id: sessionId, conferenceId: confId },
    data: {
      title: String(formData.get("title") || "").trim() || undefined,
      abstract: String(formData.get("abstract") || "") || null,
      type: String(formData.get("type") || "TALK"),
      mode: String(formData.get("mode") || "PHYSICAL"),
      startAt,
      endAt,
      roomId,
      trackId: String(formData.get("trackId") || "") || null,
      speakers: { deleteMany: {}, create: speakerIds.map((speakerId) => ({ speakerId })) },
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "session.update", entityId: sessionId });
  revalidatePath(wp(confId, "agenda"));
  redirect(back);
}

/**
 * Drag-reorder within one day + one hall lane. Sessions keep their durations and
 * the gaps between consecutive slots; the whole sequence is re-laid out in the
 * new order starting from the lane's original first start time.
 */
export async function reorderSessions(confId: string, day: string, roomKey: string, orderedIds: string[]) {
  const { user } = await requireConfAccess(confId, "agenda.manage");
  const dayStart = new Date(`${day}T00:00:00+05:30`);
  const dayEnd = new Date(`${day}T23:59:59+05:30`);
  const sessions = await db.session.findMany({
    where: {
      conferenceId: confId,
      roomId: roomKey === "none" ? null : roomKey,
      startAt: { gte: dayStart, lte: dayEnd },
    },
    orderBy: { startAt: "asc" },
  });
  const byId = new Map(sessions.map((s) => [s.id, s]));
  if (sessions.length !== orderedIds.length || orderedIds.some((id) => !byId.has(id))) return; // stale board — ignore

  // original slot shape: durations travel with sessions, gaps stay positional
  const gaps = sessions.slice(1).map((s, i) => Math.max(0, s.startAt.getTime() - sessions[i].endAt.getTime()));
  let cursor = sessions[0].startAt.getTime();
  const updates: { id: string; startAt: Date; endAt: Date }[] = [];
  orderedIds.forEach((id, i) => {
    const s = byId.get(id)!;
    const duration = s.endAt.getTime() - s.startAt.getTime();
    updates.push({ id, startAt: new Date(cursor), endAt: new Date(cursor + duration) });
    cursor += duration + (gaps[i] ?? 0);
  });
  await db.$transaction(updates.map((u) => db.session.update({ where: { id: u.id }, data: { startAt: u.startAt, endAt: u.endAt } })));
  await audit({ userId: user.id, conferenceId: confId, action: "agenda.reorder", meta: { day, roomKey, count: updates.length } });
  revalidatePath(wp(confId, "agenda"));
}

// ── Volunteers ────────────────────────────────────────────

function volunteerFields(formData: FormData) {
  const areas = formData.getAll("areas").map(String);
  return {
    name: String(formData.get("name") || "").trim(),
    email: String(formData.get("email") || "").toLowerCase().trim(),
    phone: String(formData.get("phone") || "") || null,
    department: String(formData.get("department") || "") || null,
    linkedin: String(formData.get("linkedin") || "") || null,
    areas: JSON.stringify(areas),
  };
}

export async function createVolunteer(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "volunteers.manage");
  const back = wp(confId, "volunteers");
  const f = volunteerFields(formData);
  if (!f.name || !f.email) redirect(`${back}?error=${encodeURIComponent("Name and email are required.")}`);

  let photoUrl: string | null = null;
  try {
    photoUrl = await saveUploadedImage(formData.get("photo"), "volunteers");
  } catch (e) {
    redirect(`${back}?error=${encodeURIComponent((e as Error).message)}`);
  }

  // Ensure a login exists — create one with a temp password if the email is new
  let account = await db.user.findUnique({ where: { email: f.email } });
  let tempPassword: string | null = null;
  if (!account) {
    tempPassword = `cfx-${randomBytes(4).toString("hex")}`;
    account = await db.user.create({
      data: { email: f.email, name: f.name, passwordHash: await hashPassword(tempPassword), emailVerified: true },
    });
  }

  const existing = await db.volunteer.findFirst({ where: { conferenceId: confId, userId: account.id } });
  if (existing) redirect(`${back}?error=${encodeURIComponent(`${f.email} is already a volunteer on this conference.`)}`);

  await db.volunteer.create({ data: { conferenceId: confId, userId: account.id, ...f, photoUrl } });
  await db.confMember.upsert({
    where: { conferenceId_userId_role: { conferenceId: confId, userId: account.id, role: "VOLUNTEER" } },
    create: { conferenceId: confId, userId: account.id, role: "VOLUNTEER" },
    update: {},
  });
  await db.notification.create({
    data: { userId: account.id, title: "You've been added as a volunteer", body: `You now have volunteer access on a conference. Open the dashboard to see your assigned areas.`, link: "/dashboard" },
  });
  await sendVolunteerWelcomeEmail({ email: f.email, name: f.name, conferenceId: confId, tempPassword, areas: JSON.parse(f.areas) });
  await audit({ userId: user.id, conferenceId: confId, action: "volunteer.create", meta: { email: f.email, areas: f.areas } });
  revalidatePath(back);
  redirect(`${back}?added=${encodeURIComponent(f.name)}${tempPassword ? `&temp=${encodeURIComponent(tempPassword)}` : ""}`);
}

export async function updateVolunteer(confId: string, volunteerId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "volunteers.manage");
  const back = wp(confId, "volunteers");
  const f = volunteerFields(formData);
  let photoUrl: string | null = null;
  try {
    photoUrl = await saveUploadedImage(formData.get("photo"), "volunteers");
  } catch (e) {
    redirect(`${back}?edit=${volunteerId}&error=${encodeURIComponent((e as Error).message)}`);
  }
  await db.volunteer.update({
    where: { id: volunteerId, conferenceId: confId },
    data: {
      name: f.name || undefined,
      phone: f.phone,
      department: f.department,
      linkedin: f.linkedin,
      areas: f.areas,
      ...(photoUrl ? { photoUrl } : {}),
      // email is the login identity — changing it here would orphan the account, so it stays fixed
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "volunteer.update", entityId: volunteerId, meta: { areas: f.areas } });
  revalidatePath(back);
  redirect(back);
}

export async function removeVolunteer(confId: string, volunteerId: string) {
  const { user } = await requireConfAccess(confId, "volunteers.manage");
  const vol = await db.volunteer.findFirst({ where: { id: volunteerId, conferenceId: confId } });
  if (!vol) return;
  await db.volunteer.delete({ where: { id: volunteerId } });
  await db.confMember.deleteMany({ where: { conferenceId: confId, userId: vol.userId, role: "VOLUNTEER" } });
  await audit({ userId: user.id, conferenceId: confId, action: "volunteer.remove", meta: { email: vol.email } });
  revalidatePath(wp(confId, "volunteers"));
}

// ── Live conference control ───────────────────────────────

export async function createLiveUpdate(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "content.manage");
  const title = String(formData.get("title") || "").trim();
  if (!title) return;
  await db.liveUpdate.create({
    data: {
      conferenceId: confId,
      type: String(formData.get("type") || "KEY_POINT"),
      title,
      body: String(formData.get("body") || "") || null,
      quote: String(formData.get("quote") || "") || null,
      statValue: String(formData.get("statValue") || "") || null,
      statLabel: String(formData.get("statLabel") || "") || null,
      link: String(formData.get("link") || "") || null,
      tags: String(formData.get("tags") || "") || null,
      sessionId: String(formData.get("sessionId") || "") || null,
      speakerId: String(formData.get("speakerId") || "") || null,
      featured: formData.get("featured") === "on",
      createdById: user.id,
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "live.update.publish", meta: { title } });
  revalidatePath(wp(confId, "live"));
}

export async function deleteLiveUpdate(confId: string, updateId: string) {
  const { user } = await requireConfAccess(confId, "content.manage");
  await db.liveUpdate.delete({ where: { id: updateId, conferenceId: confId } });
  await audit({ userId: user.id, conferenceId: confId, action: "live.update.delete", entityId: updateId });
  revalidatePath(wp(confId, "live"));
}

export async function toggleLiveUpdateFeatured(confId: string, updateId: string) {
  await requireConfAccess(confId, "content.manage");
  const u = await db.liveUpdate.findFirst({ where: { id: updateId, conferenceId: confId } });
  if (u) await db.liveUpdate.update({ where: { id: updateId }, data: { featured: !u.featured } });
  revalidatePath(wp(confId, "live"));
}

export async function setLiveQuestionStatus(confId: string, questionId: string, status: "APPROVED" | "DISMISSED") {
  const { user } = await requireConfAccess(confId, "content.manage");
  await db.liveQuestion.update({ where: { id: questionId, conferenceId: confId }, data: { status } });
  await audit({ userId: user.id, conferenceId: confId, action: `live.question.${status.toLowerCase()}`, entityId: questionId });
  revalidatePath(wp(confId, "live"));
}

export async function updateLiveSettings(confId: string, formData: FormData) {
  const { user } = await requireConfAccess(confId, "content.manage");
  await db.conference.update({
    where: { id: confId },
    data: {
      streamUrl: String(formData.get("streamUrl") || "") || null,
      hashtags: String(formData.get("hashtags") || "") || null,
    },
  });
  await audit({ userId: user.id, conferenceId: confId, action: "live.settings.update" });
  revalidatePath(wp(confId, "live"));
}
