import { pgTable, serial, integer, text, boolean, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import { users } from '../auth/schema';

// The Jellyfin servers behind /jellyfin, for the officer-jellyfin sidecar.
//
// A REGISTRY rather than a single row, and here that is not speculative: this machine runs four Jellyfin
// instances on separate ports (video, albums, DJ sets, and a second person's), and the owner switches
// between them. Same shape as invoiceshelf_accounts and headscale_servers.
//
// `accessToken` is encrypted at rest via ../crypto.ts. A Jellyfin access token can read every item, every
// watch history and every stream on the instance, so a DB dump must not hand it over. Encryption is confined
// to queries/jellyfin.ts; nothing outside that file sees ciphertext, and no route ever returns the token.
//
// The PASSWORD is never stored. It is used once, by the sidecar, to trade for an access token through
// `POST /Users/AuthenticateByName` — the same shape as InvoiceShelf's mintToken.
export const jellyfinServers = pgTable(
  'jellyfin_servers',
  {
    id: serial('id').primaryKey(),
    userId: integer('user_id')
      .notNull()
      .references(() => users.id, { onDelete: 'cascade' }),
    /** What the owner calls this server. The switcher shows nothing else, so it has to be theirs to set. */
    label: text('label').notNull(),
    // Normalized without a trailing slash before write, so `${url}/Items/...` never doubles the separator.
    url: text('url').notNull(),
    accessToken: text('access_token').notNull(), // encrypted
    /**
     * The Jellyfin user the token belongs to, pinned on the row.
     *
     * Jellyfin's per-user routes (`/UserItems/Resume`, played state, favourites) need this id explicitly, and
     * it is NOT derivable from the token without a round trip. Authenticating returns it, so it is stored
     * once rather than looked up on every request.
     */
    jellyfinUserId: text('jellyfin_user_id').notNull(),
    /** The account name that was authenticated — shown in the UI so two logins on one server are told apart. */
    jellyfinUsername: text('jellyfin_username'),
    /**
     * The DeviceId sent on every call, generated once when the server is added.
     *
     * Jellyfin keys sessions and "remembered devices" by this. A fresh one per request would litter the
     * dashboard with hundreds of devices and break playback reporting, which correlates by session.
     */
    deviceId: text('device_id').notNull(),
    /** The instance's own name (`ServerName` from /System/Info/Public), for the switcher. */
    serverName: text('server_name'),
    /** Jellyfin version seen at the last successful probe — shown in the UI, never used for behaviour. */
    version: text('version'),
    isActive: boolean('is_active').notNull().default(false),
    lastSeenAt: timestamp('last_seen_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    uniqueIndex('uq_jellyfin_servers_user_label').on(t.userId, t.label),
    // At most one active server per owner, enforced by the DB rather than by convention: a partial unique
    // index over the active rows only. setActiveJellyfinServer still clears the others in a transaction, but
    // a bug there fails loudly here instead of silently leaving two active and the UI picking one.
    uniqueIndex('uq_jellyfin_servers_one_active')
      .on(t.userId)
      .where(sql`${t.isActive}`),
  ],
);
