import {
  pgTable,
  text,
  uuid,
  timestamp,
  varchar,
  unique,
  check,
  primaryKey,
  index,
} from "drizzle-orm/pg-core";

import { sql } from "drizzle-orm";

import {
  SUPPORTED_POS,
  SUPPORTED_LANGUAGE_CODES,
  CEFR_LEVELS,
  SUPPORTED_DECK_TYPES,
} from "@glossa/shared";

export const terms = pgTable(
  "terms",
  {
    id: uuid().primaryKey().defaultRandom(),
    source: varchar({ length: 50 }), // 'omw', 'wiktionary', null for manual
    source_id: text(), // synset_id value for omw, wiktionary QID, etc.
    pos: varchar({ length: 20 }).notNull(),
    created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
  },
  (table) => [
    check(
      "pos_check",
      sql`${table.pos} IN (${sql.raw(SUPPORTED_POS.map((p) => `'${p}'`).join(", "))})`,
    ),
    unique("unique_source_id").on(table.source, table.source_id),
    index("idx_terms_source_pos").on(table.source, table.pos),
  ],
);

export const term_glosses = pgTable(
  "term_glosses",
  {
    id: uuid().primaryKey().defaultRandom(),
    term_id: uuid()
      .notNull()
      .references(() => terms.id, { onDelete: "cascade" }),
    language_code: varchar({ length: 10 }).notNull(),
    text: text().notNull(),
    created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
  },
  (table) => [
    unique("unique_term_gloss").on(
      table.term_id,
      table.language_code,
      table.text,
    ),
    check(
      "language_code_check",
      sql`${table.language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))})`,
    ),
  ],
);

export const translations = pgTable(
  "translations",
  {
    id: uuid().primaryKey().defaultRandom(),
    term_id: uuid()
      .notNull()
      .references(() => terms.id, { onDelete: "cascade" }),
    language_code: varchar({ length: 10 }).notNull(),
    text: text().notNull(),
    cefr_level: varchar({ length: 2 }),
    created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
  },
  (table) => [
    unique("unique_translations").on(
      table.term_id,
      table.language_code,
      table.text,
    ),
    check(
      "language_code_check",
      sql`${table.language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))})`,
    ),
    check(
      "cefr_check",
      sql`${table.cefr_level} IN (${sql.raw(CEFR_LEVELS.map((l) => `'${l}'`).join(", "))})`,
    ),
    index("idx_translations_lang").on(
      table.language_code,
      table.cefr_level,
      table.term_id,
    ),
  ],
);

export const users = pgTable("users", {
  id: uuid().primaryKey().defaultRandom(),
  openauth_sub: text().unique().notNull(),
  email: varchar({ length: 255 }).unique(),
  display_name: varchar({ length: 100 }).unique(),
  created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
  last_login_at: timestamp({ withTimezone: true }),
});
// KNOWN LIMITATION: email is nullable (GitHub users may have no public email)
// and unique, but two OAuth providers can return the same email for different
// accounts. For MVP this is acceptable since users are identified by
// openauth_sub, not email. If multi-provider login per user is added later,
// consider a separate user_emails table.

export const decks = pgTable(
  "decks",
  {
    id: uuid().primaryKey().defaultRandom(),
    name: text().notNull(),
    description: text(),
    source_language: varchar({ length: 10 }).notNull(),
    validated_languages: varchar({ length: 10 }).array().notNull().default([]),
    type: varchar({ length: 20 }).notNull(),
    created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
  },
  (table) => [
    check(
      "source_language_check",
      sql`${table.source_language} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))})`,
    ),
    check(
      "validated_languages_check",
      sql`validated_languages <@ ARRAY[${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))}]::varchar[]`,
    ),
    check(
      "validated_languages_excludes_source",
      sql`NOT (${table.source_language} = ANY(${table.validated_languages}))`,
    ),
    check(
      "deck_type_check",
      sql`${table.type} IN (${sql.raw(SUPPORTED_DECK_TYPES.map((t) => `'${t}'`).join(", "))})`,
    ),
    unique("unique_deck_name").on(table.name, table.source_language),
    index("idx_decks_type").on(table.type, table.source_language),
  ],
);

export const deck_terms = pgTable(
  "deck_terms",
  {
    deck_id: uuid()
      .notNull()
      .references(() => decks.id, { onDelete: "cascade" }),
    term_id: uuid()
      .notNull()
      .references(() => terms.id, { onDelete: "cascade" }),
  },
  (table) => [primaryKey({ columns: [table.deck_id, table.term_id] })],
);

export const topics = pgTable("topics", {
  id: uuid().primaryKey().defaultRandom(),
  slug: varchar({ length: 50 }).notNull().unique(),
  label: text().notNull(),
  description: text(),
  created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
});

export const term_topics = pgTable(
  "term_topics",
  {
    term_id: uuid()
      .notNull()
      .references(() => terms.id, { onDelete: "cascade" }),
    topic_id: uuid()
      .notNull()
      .references(() => topics.id, { onDelete: "cascade" }),
  },
  (table) => [primaryKey({ columns: [table.term_id, table.topic_id] })],
);

/*
 * INTENTIONAL DESIGN DECISIONS — see decisions.md for full reasoning
 *
 * source + source_id (terms): idempotency key per import pipeline
 * display_name UNIQUE (users): multiplayer requires distinguishable names
 * UNIQUE(term_id, language_code, text): allows synonyms, prevents exact duplicates
 * updated_at omitted: misleading without a trigger to maintain it
 * FK indexes: all FK columns covered, no sequential scans on joins
 */
