"use server";

// Starting and undoing a Plex link.
//
// Linking is a round trip through plex.tv: we ask for a PIN, send the person to
// plex.tv to claim it, and Plex returns them to /api/plex/callback, which is
// where the link is actually finished. The PIN id is parked in an httpOnly
// cookie for the duration -- it is a one-use handle, worthless without the
// sign-in that claims it, and it means the flow survives the user taking a
// minute over it.
//
// The FINISHING half deliberately lives in a route handler rather than here or
// in the page: it has to delete that cookie and revalidate, and a page render
// may do neither. See src/app/api/plex/callback/route.ts.
//
// Linking is what grants library access AND what makes a watchlist and watch
// history readable. All of it follows from the same consent, which is why it
// happens together rather than being things an admin has to remember to do.

import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { revalidatePath } from "next/cache";
import { eq } from "drizzle-orm";

import { auth } from "@/auth";
import { db } from "@/db/client";
import { plexAccounts } from "@/db/schema";
import { appUrl } from "@/lib/app-url";
import { createPin, authUrl } from "@/lib/plex";
import { PIN_COOKIE } from "@/lib/plex-link";

/**
 * Both surfaces, always.
 *
 * The embed is a separate route rendering the same data, so refreshing only
 * /profile would leave charon's panel showing a Plex link that has already been
 * removed until something else happened to invalidate it.
 */
function revalidateProfile() {
  revalidatePath("/profile");
  revalidatePath("/embed/profile");
}

async function requireUser() {
  const session = await auth();
  if (!session?.user?.id) throw new Error("Sign in first");
  return session.user;
}

export async function startPlexLinkAction() {
  await requireUser();

  const pin = await createPin();
  const jar = await cookies();
  jar.set(PIN_COOKIE, String(pin.id), {
    httpOnly: true,
    sameSite: "lax",
    secure: true,
    path: "/",
    maxAge: 15 * 60,
  });

  // A bare path, no query string -- Plex carries this inside a URL fragment
  // that its own client-side code parses. redirect() throws, so it goes last.
  redirect(authUrl(pin, appUrl("/api/plex/callback")));
}

export async function unlinkPlexAction() {
  const user = await requireUser();
  // Only the link is removed. Library access is granted on Plex's side and is
  // not ours to quietly revoke from a button labelled "unlink"; whoever owns
  // the server can take it back there.
  await db.delete(plexAccounts).where(eq(plexAccounts.userId, user.id));
  revalidateProfile();
}
