import { sql } from "drizzle-orm";
import {
  pgTable,
  pgEnum,
  uuid,
  text,
  integer,
  timestamp,
  index,
  uniqueIndex,
} from "drizzle-orm/pg-core";
import { users } from "./users";
import { mediaItems } from "./media";

export const watchlistSourceEnum = pgEnum("watchlist_source", ["plex", "manual"]);
export const watchingNowScopeEnum = pgEnum("watching_now_scope", ["show", "season"]);
export const watchHistorySourceEnum = pgEnum("watch_history_source", ["plex", "manual"]);

// Items synced from Plex watchlists or added manually.
// Default behavior: archive-tier 720p.
export const watchlistItems = pgTable("watchlist_items", {
  id: uuid("id").primaryKey().defaultRandom(),
  userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
  mediaItemId: uuid("media_item_id").notNull().references(() => mediaItems.id, { onDelete: "cascade" }),
  source: watchlistSourceEnum("source").notNull().default("plex"),
  addedAt: timestamp("added_at").notNull().defaultNow(),
  removedAt: timestamp("removed_at"),
});

// Active watching intent. Drives promotion to live storage.
// Each user has a slot quota (5 TV, 10 movies by default).
export const watchingNowItems = pgTable("watching_now_items", {
  id: uuid("id").primaryKey().defaultRandom(),
  userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
  mediaItemId: uuid("media_item_id").notNull().references(() => mediaItems.id, { onDelete: "cascade" }),
  scope: watchingNowScopeEnum("scope").notNull().default("show"),
  // Set when scope is "season".
  seasonNumber: integer("season_number"),
  slotNumber: integer("slot_number"),
  addedAt: timestamp("added_at").notNull().defaultNow(),
  removedAt: timestamp("removed_at"),
});

// What somebody has actually watched.
//
// COPIED OUT OF PLEX RATHER THAN READ FROM IT. Plex prunes its own history, and
// a rebuilt server starts empty -- both of which have happened here -- so a
// profile that queried Plex live would quietly lose years of viewing and a page
// load would depend on the media server being up. This is the durable record.
//
// It is also the signal the placement classifier has never had. "Watched, and
// the show has ended" is the strongest possible argument for demotion, and it
// cannot be made from watchlists and Watching Now alone: those say what someone
// intends, and only this says what they finished.
//
// PRIVACY: the owner's Plex token can read EVERY user's history, which is not
// permission to show it. Rows are written only for accounts that linked
// themselves at /profile, and are only ever read back scoped to the user who
// owns them -- there is no admin view of who watched what. See the guard in
// getWatchHistory().
export const watchHistory = pgTable(
  "watch_history",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
    mediaItemId: uuid("media_item_id").notNull().references(() => mediaItems.id, { onDelete: "cascade" }),
    // Null for a movie. Set for an episode, and stored as numbers rather than a
    // reference to episodes.id because Plex reports plays for episodes this
    // database has no row for -- a season TMDB has not published yet, or a file
    // imported before the metadata refresh caught up. A foreign key there would
    // mean dropping the play.
    seasonNumber: integer("season_number"),
    episodeNumber: integer("episode_number"),
    watchedAt: timestamp("watched_at").notNull(),
    source: watchHistorySourceEnum("source").notNull().default("plex"),
    // Plex's own id for the play (its `historyKey`). This is what makes the
    // sync idempotent: it re-reads an overlapping window every run, and without
    // a stable id per play every run would duplicate the overlap.
    plexHistoryKey: text("plex_history_key"),
    plexRatingKey: text("plex_rating_key"),
    createdAt: timestamp("created_at").notNull().defaultNow(),
  },
  (t) => [
    // Partial, because a manually added row has no Plex key and Postgres would
    // otherwise treat every null as distinct anyway -- stating it makes the
    // intent readable instead of relying on that.
    uniqueIndex("watch_history_user_plex_key_idx")
      .on(t.userId, t.plexHistoryKey)
      .where(sql`plex_history_key is not null`),
    // The one query the profile makes: this person's plays, newest first.
    index("watch_history_user_watched_idx").on(t.userId, t.watchedAt.desc()),
  ],
);

// Admin-managed list of movies that remain live permanently regardless of age.
export const classics = pgTable("classics", {
  id: uuid("id").primaryKey().defaultRandom(),
  mediaItemId: uuid("media_item_id").notNull().references(() => mediaItems.id, { onDelete: "cascade" }).unique(),
  note: text("note"),
  addedBy: uuid("added_by").references(() => users.id),
  addedAt: timestamp("added_at").notNull().defaultNow(),
});
