import { pgTable, integer, text, timestamp, uuid, primaryKey, pgEnum } from 'drizzle-orm/pg-core'
import { organization } from './org.js'

/**
 * Category. Fixed by #26, amended by #31.
 *
 * A curated global spine with retailer-local categories mapped onto it.
 * The split is load-bearing:
 *
 *   eligibility  ->  local_category            and stops there. No traversal.
 *   matching     ->  local_category -> spine_node -> supersession
 *
 * #31 decision 1: eligibility references the local category ONLY, so spine
 * churn can never reach an authorization decision.
 */

/** Two levels. Eligibility attaches only to leaves; nothing inherits (#26 decision 3). */
export const spineNode = pgTable('spine_node', {
  id: uuid('id').primaryKey().defaultRandom(),
  label: text('label').notNull(),
  /** Null for a group; set for a leaf. */
  groupId: uuid('group_id'),
  /** Interop annotation only, never identity (#26 decision 2). */
  unspscFamilies: text('unspsc_families').array().notNull().default([]),

  // Nodes are immutable. Labels may be corrected; a label is not an identity.
  validFrom: timestamp('valid_from', { withTimezone: true }).notNull().defaultNow(),
  /** Null = live. New RFPs and grants select only from live nodes. */
  validTo: timestamp('valid_to', { withTimezone: true }),
  supersededBy: uuid('superseded_by').array().notNull().default([]),
})

/**
 * The retailer's own vocabulary. Gets the spine's mutability model per #31
 * decision 2: immutable identity, editable label, retire via validTo,
 * structural change is an insert, delete is not offered.
 */
export const localCategory = pgTable('local_category', {
  id: uuid('id').primaryKey().defaultRandom(),
  retailerOrgId: uuid('retailer_org_id').notNull().references(() => organization.id),
  label: text('label').notNull(),
  /** Unmapped still works locally — it is simply invisible to matching. */
  spineLeafId: uuid('spine_leaf_id').references(() => spineNode.id),
  validFrom: timestamp('valid_from', { withTimezone: true }).notNull().defaultNow(),
  validTo: timestamp('valid_to', { withTimezone: true }),
  supersededBy: uuid('superseded_by').array().notNull().default([]),
})

export const grantProvenance = pgEnum('grant_provenance', ['manual', 'split_event', 'merge_event'])

/**
 * Durable state on the (retailer, vendor, local category) triple. Exact and
 * non-derived — policies never compute a closure over it (#26 decision 3).
 */
export const eligibility = pgTable('eligibility', {
  retailerOrgId: uuid('retailer_org_id').notNull().references(() => organization.id),
  vendorOrgId: uuid('vendor_org_id').notNull().references(() => organization.id),
  localCategoryId: uuid('local_category_id').notNull().references(() => localCategory.id),
  provenance: grantProvenance('provenance').notNull().default('manual'),
  /** Set when provenance is a split or merge — points at the event that wrote this row. */
  provenanceEventId: uuid('provenance_event_id'),
  grantedAt: timestamp('granted_at', { withTimezone: true }).notNull().defaultNow(),
  revokedAt: timestamp('revoked_at', { withTimezone: true }),
}, (t) => [primaryKey({ columns: [t.retailerOrgId, t.vendorOrgId, t.localCategoryId] })])

export const categoryChangeKind = pgEnum('category_change_kind', [
  'split', 'merge', 'retire', 'remap',
])

/**
 * Splits, merges, retirements and remaps. Grant propagation is an explicit,
 * event-stamped write — never read-time derivation (#31 decision 3).
 * A split propagates to every successor; a merge only to the intersection.
 */
export const categoryChangeEvent = pgTable('category_change_event', {
  id: uuid('id').primaryKey().defaultRandom(),
  kind: categoryChangeKind('kind').notNull(),
  retailerOrgId: uuid('retailer_org_id').references(() => organization.id),
  fromIds: uuid('from_ids').array().notNull(),
  toIds: uuid('to_ids').array().notNull(),
  rowsWritten: integer('rows_written').notNull().default(0),
  performedBy: uuid('performed_by').notNull(),
  performedAt: timestamp('performed_at', { withTimezone: true }).notNull().defaultNow(),
})
