"use server";

// The buttons on a title's detail panel.
//
// The two admin verbs. Both are statements about the LIBRARY, which is what
// separates them from Watch Now -- that one is a statement about the person
// making it, lives in watch-now-actions, and is the button anybody sees.
//
//   timeless   "This one is kept, at whatever it costs" -- and it is the only
//              flag that admits a remux.
//   purge      "Stop wanting this." Deliberately loses to Watching Now, so
//              someone can still pull a purged title back by watching it, and
//              it returns to purged afterwards.
//
// Nothing here deletes a file. Purge withdraws intent, which is what makes it
// safe to put behind a button: the worst it can do is stop a download.

import { and, eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";

import { auth } from "@/auth";
import { db } from "@/db/client";
import { adminOverrides, classics } from "@/db/schema";
import { ensureMediaItem, findMediaItemByTmdbId, type CatalogRef, type MediaType } from "@/lib/media-item";

export type TitleFlags = {
  timeless: boolean;
  purged: boolean;
};

export type TitleActionResult =
  | { ok: true; flags: Partial<TitleFlags>; message?: string }
  | { ok: false; message: string };

function mediaTypeFromKind(kind: string | null): MediaType | null {
  if (kind === "movies") return "movie";
  if (kind === "television") return "tv_series";
  return null;
}

function text(value: FormDataEntryValue | null) {
  return typeof value === "string" && value.trim() ? value.trim() : null;
}

function isOn(value: FormDataEntryValue | null) {
  return value === "1" || value === "true";
}

/** The catalog entry a form is talking about, or null if it is malformed. */
function catalogRef(formData: FormData): CatalogRef | null {
  const mediaType = mediaTypeFromKind(text(formData.get("kind")));
  const tmdbId = text(formData.get("tmdbId"));
  const title = text(formData.get("title"));
  if (!mediaType || !tmdbId || !title) return null;

  const parsedYear = Number.parseInt(text(formData.get("year")) ?? "", 10);
  const releaseDate = text(formData.get("releaseDate"));

  return {
    mediaType,
    tmdbId,
    title,
    year: Number.isFinite(parsedYear) ? parsedYear : null,
    overview: text(formData.get("overview")),
    posterPath: text(formData.get("posterPath")),
    releaseDate: releaseDate && /^\d{4}-\d{2}-\d{2}$/.test(releaseDate) ? releaseDate : null,
  };
}

async function requireUser() {
  const session = await auth();
  if (!session?.user?.id) return null;
  return session.user;
}

/** Mark a title as one to keep forever, or stop. Admins only. */
export async function setTimeless(formData: FormData): Promise<TitleActionResult> {
  const user = await requireUser();
  if (!user) return { ok: false, message: "Sign in first." };
  if (!user.isAdmin) return { ok: false, message: "Only admins can change this." };

  const ref = catalogRef(formData);
  if (!ref) return { ok: false, message: "This title could not be identified." };
  const on = isOn(formData.get("on"));

  if (!on) {
    const mediaItemId = await findMediaItemByTmdbId(ref.mediaType, ref.tmdbId);
    if (mediaItemId) {
      await db.delete(classics).where(eq(classics.mediaItemId, mediaItemId));
    }
    revalidatePath("/");
    return { ok: true, flags: { timeless: false } };
  }

  const mediaItemId = await ensureMediaItem(ref);
  await db
    .insert(classics)
    .values({ mediaItemId, addedBy: user.id })
    .onConflictDoNothing({ target: classics.mediaItemId });

  revalidatePath("/");
  return {
    ok: true,
    flags: { timeless: true },
    message: ref.title + " is now timeless: kept live, and the only tier allowed a remux.",
  };
}

/** Stop wanting a title, or let it be wanted again. Admins only. */
export async function setPurged(formData: FormData): Promise<TitleActionResult> {
  const user = await requireUser();
  if (!user) return { ok: false, message: "Sign in first." };
  if (!user.isAdmin) return { ok: false, message: "Only admins can change this." };

  const ref = catalogRef(formData);
  if (!ref) return { ok: false, message: "This title could not be identified." };
  const on = isOn(formData.get("on"));

  // Purge withdraws intent, so there has to be intent to withdraw. Creating a
  // media item in order to mark it unwanted would leave a row that exists for
  // no reason but to say nobody wants it.
  const mediaItemId = await findMediaItemByTmdbId(ref.mediaType, ref.tmdbId);
  if (!mediaItemId) {
    return { ok: false, message: ref.title + " is not in the collection, so there is nothing to purge." };
  }

  // Delete first either way: the table allows several overrides per item, and a
  // second purge row would be a duplicate nobody could tell apart from the
  // first when it came time to lift it.
  await db
    .delete(adminOverrides)
    .where(and(eq(adminOverrides.mediaItemId, mediaItemId), eq(adminOverrides.overrideType, "purge")));

  if (on) {
    await db.insert(adminOverrides).values({
      mediaItemId,
      overrideType: "purge",
      reason: "purged from the title panel",
      createdBy: user.id,
    });
  }

  revalidatePath("/");
  return {
    ok: true,
    flags: { purged: on },
    message: on
      ? ref.title + " is purged: no longer wanted, though watching it still brings it back."
      : ref.title + " is no longer purged.",
  };
}
