import {
  pgTable,
  pgEnum,
  uuid,
  text,
  integer,
  boolean,
  date,
  timestamp,
  index,
  uniqueIndex,
} from "drizzle-orm/pg-core";

export const mediaTypeEnum = pgEnum("media_type", ["tv_series", "movie"]);
export const seriesStatusEnum = pgEnum("series_status", ["upcoming", "continuing", "ended"]);
export const seasonStatusEnum = pgEnum("season_status", ["upcoming", "airing", "complete"]);
export const externalIdSourceEnum = pgEnum("external_id_source", ["tvdb", "tmdb", "imdb", "plex"]);

// Shared identity record for both TV series and movies.
export const mediaItems = pgTable("media_items", {
  id: uuid("id").primaryKey().defaultRandom(),
  mediaType: mediaTypeEnum("media_type").notNull(),
  title: text("title").notNull(),
  sortTitle: text("sort_title"),
  overview: text("overview"),
  year: integer("year"),
  posterPath: text("poster_path"),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

export const series = pgTable("series", {
  id: uuid("id").primaryKey().references(() => mediaItems.id, { onDelete: "cascade" }),
  status: seriesStatusEnum("status").notNull().default("upcoming"),
  network: text("network"),
  firstAirDate: date("first_air_date"),
  lastAirDate: date("last_air_date"),
  // TMDB's episode_run_time, the typical length of an episode. Kept because it
  // is the only cheap way to ask whether the series we think we have matches
  // the files on disk: a show whose episodes probe at 45 minutes is not the
  // 22-minute show TMDB describes, whatever the folder is called. TMDB leaves
  // it empty for plenty of modern shows, so absence means nothing.
  episodeRuntimeMinutes: integer("episode_runtime_minutes"),
  // Whether the show is still airing OR ended less than one year ago.
  // Recomputed on metadata refresh. See PLANNING.md "Currently Airing".
  isCurrentlyRelevant: boolean("is_currently_relevant"),
  metadataRefreshedAt: timestamp("metadata_refreshed_at"),
});

export const seasons = pgTable(
  "seasons",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    seriesId: uuid("series_id").notNull().references(() => series.id, { onDelete: "cascade" }),
    seasonNumber: integer("season_number").notNull(),
    title: text("title"),
    episodeCount: integer("episode_count"),
    airDate: date("air_date"),
    status: seasonStatusEnum("status").notNull().default("upcoming"),
  },
  // Required for the metadata refresh to upsert rather than duplicate.
  (t) => [uniqueIndex("seasons_series_id_season_number_idx").on(t.seriesId, t.seasonNumber)],
);

export const episodes = pgTable(
  "episodes",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    seasonId: uuid("season_id").notNull().references(() => seasons.id, { onDelete: "cascade" }),
    episodeNumber: integer("episode_number").notNull(),
    title: text("title"),
    overview: text("overview"),
    airDate: date("air_date"),
  },
  (t) => [uniqueIndex("episodes_season_id_episode_number_idx").on(t.seasonId, t.episodeNumber)],
);

export const movies = pgTable("movies", {
  id: uuid("id").primaryKey().references(() => mediaItems.id, { onDelete: "cascade" }),
  releaseDate: date("release_date"),
  runtimeMinutes: integer("runtime_minutes"),
  status: text("status"),
  metadataRefreshedAt: timestamp("metadata_refreshed_at"),
});

export const externalIds = pgTable(
  "external_ids",
  {
    id: uuid("id").primaryKey().defaultRandom(),
    mediaItemId: uuid("media_item_id").notNull().references(() => mediaItems.id, { onDelete: "cascade" }),
    source: externalIdSourceEnum("source").notNull(),
    externalId: text("external_id").notNull(),
    // Has somebody established that this id is really this title?
    //
    // Most of these were derived by matching a FOLDER NAME against TMDB search,
    // and that matcher has been wrong in ways nothing downstream could detect:
    // Taskmaster (AU) carried the UK show's 227 episodes, Dune the wrong Dune,
    // Kingdom the anime instead of the MMA drama. The link being plausible is
    // not the same as it being right, and until it is checked, everything built
    // on it -- what to fetch, what to rename a file to -- inherits the doubt.
    //
    // Set automatically when the id came from a person choosing a specific
    // TMDB result (the catalog board, a Plex watchlist), because there the id
    // IS the request rather than a guess about it. Set by hand for the rest.
    verifiedAt: timestamp("verified_at"),
    // Null for automatic verification: nobody claimed it, the provenance did.
    verifiedBy: uuid("verified_by"),
    // What the remote source calls this id, and when we last asked.
    //
    // Stored because it is the fact a reviewer actually needs and the only one
    // not already here: the scanner writes the FOLDER's title and the metadata
    // refresh deliberately never overwrites it, so the database knows what we
    // call a thing and not what the id we linked it to names. Asking "is this
    // the right film?" without it means asking somebody to hold two tabs open.
    remoteTitle: text("remote_title"),
    remoteCheckedAt: timestamp("remote_checked_at"),
    // Somebody looked and said this id is NOT this title.
    //
    // A third state, distinct from unreviewed, and the reason it exists is
    // that rejecting has to be as cheap as confirming. A reviewer who can only
    // say yes will say yes to a link they are unsure of rather than lose their
    // place, and the flag stops meaning anything. Saying no costs one click,
    // moves the title out of the queue, and leaves it somewhere it can be
    // found again.
    //
    // The wrong id stays attached on purpose. It is the evidence of what went
    // wrong, and whoever resolves this needs to see what was rejected as much
    // as what the title is.
    rejectedAt: timestamp("rejected_at"),
    rejectedBy: uuid("rejected_by"),
  },
  (t) => [
    uniqueIndex("external_ids_source_external_id_idx").on(t.source, t.externalId),
    // The unique index above is on (source, external_id), which answers "who
    // holds tmdb:1726?" but not "what is this title linked to?" -- and the
    // second question is the one the app actually asks, on every page that
    // shows a poster or a link.
    index("external_ids_media_item_idx").on(t.mediaItemId),
  ]
);
