import "server-only";
import type { HomepageSection, Prisma } from "@prisma/client";
import { db } from "./db";
import type { SectionData } from "./sections";

export type RenderSection = { id: string; type: HomepageSection["type"]; data: SectionData };

/** Live homepage: published content, published order and visibility, within its schedule window. */
export async function liveSections(now = new Date()): Promise<RenderSection[]> {
  const rows = await db.homepageSection.findMany({ orderBy: { sortOrder: "asc" } });
  return rows
    .filter((r) => r.publishedData && r.visible && (!r.publishAt || r.publishAt <= now) && (!r.unpublishAt || r.unpublishAt > now))
    .map((r) => ({ id: r.id, type: r.type, data: r.publishedData as SectionData }));
}

/** Preview: what the page will look like after Publish (drafts, draft order, draft visibility). Schedules still apply. */
export async function previewSections(now = new Date()): Promise<RenderSection[]> {
  const rows = await db.homepageSection.findMany();
  return rows
    .map((r) => ({ r, order: r.draftOrder ?? r.sortOrder, visible: r.draftVisible ?? r.visible }))
    .filter(({ r, visible }) => visible && (!r.publishAt || r.publishAt <= now) && (!r.unpublishAt || r.unpublishAt > now))
    .sort((a, b) => a.order - b.order)
    .map(({ r }) => ({ id: r.id, type: r.type, data: r.draftData as SectionData }));
}

export function hasUnpublished(r: HomepageSection) {
  return (
    JSON.stringify(r.draftData) !== JSON.stringify(r.publishedData) ||
    (r.draftOrder != null && r.draftOrder !== r.sortOrder) ||
    (r.draftVisible != null && r.draftVisible !== r.visible)
  );
}

export async function publishHomepage(actorId: string, note?: string) {
  return db.$transaction(async (tx) => {
    const rows = await tx.homepageSection.findMany();
    for (const r of rows) {
      await tx.homepageSection.update({
        where: { id: r.id },
        data: {
          publishedData: r.draftData as Prisma.InputJsonValue,
          sortOrder: r.draftOrder ?? r.sortOrder,
          visible: r.draftVisible ?? r.visible,
          draftOrder: null,
          draftVisible: null,
        },
      });
    }
    const after = await tx.homepageSection.findMany({ orderBy: { sortOrder: "asc" } });
    return tx.homepageRevision.create({
      data: {
        note: note || "Published",
        createdBy: actorId,
        snapshot: after.map((r) => ({ id: r.id, type: r.type, label: r.label, sortOrder: r.sortOrder, visible: r.visible, publishAt: r.publishAt, unpublishAt: r.unpublishAt, data: r.publishedData })) as Prisma.InputJsonValue,
      },
    });
  });
}

type Snap = { id: string; type: HomepageSection["type"]; label: string; sortOrder: number; visible: boolean; publishAt: string | null; unpublishAt: string | null; data: Prisma.InputJsonValue };

/** Restores a published revision as both live and draft content. */
export async function restoreRevision(revisionId: string, actorId: string) {
  const rev = await db.homepageRevision.findUniqueOrThrow({ where: { id: revisionId } });
  const snap = rev.snapshot as unknown as Snap[];
  await db.$transaction(async (tx) => {
    await tx.homepageSection.deleteMany({});
    for (const s of snap) {
      await tx.homepageSection.create({
        data: { id: s.id, type: s.type, label: s.label, sortOrder: s.sortOrder, visible: s.visible, publishAt: s.publishAt ? new Date(s.publishAt) : null, unpublishAt: s.unpublishAt ? new Date(s.unpublishAt) : null, draftData: s.data, publishedData: s.data },
      });
    }
    await tx.homepageRevision.create({ data: { note: `Restored revision from ${rev.createdAt.toISOString().slice(0, 16).replace("T", " ")}`, createdBy: actorId, snapshot: rev.snapshot as Prisma.InputJsonValue } });
  });
}
