/**
 * User-defined fields.
 *
 * Every CRM grows fields its designers did not anticipate, and a schema that
 * refuses them is a schema that gets worked around in a spreadsheet. The
 * variant here carries one idea the usual designs miss: `agentBrief` — a prose
 * instruction telling the agent *how* to fill this particular field. A custom
 * field is otherwise opaque to an agent, which
 * knows the column exists but nothing about what would constitute a good value.
 */
import {
  boolean,
  index,
  integer,
  numeric,
  pgTable,
  text,
  timestamp,
  uniqueIndex,
  uuid,
} from 'drizzle-orm/pg-core';
import { accounts, contacts } from './crm';
import { demandDeals } from './demand';
import { supplyDeals } from './supply';
import { users } from './identity';

export const fieldDefinitions = pgTable(
  'field_definitions',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    /** 'account' | 'contact' | 'demand_deal' | 'supply_deal' */
    entity: text('entity').notNull(),
    key: text('key').notNull(),
    label: text('label').notNull(),
    /**
     * 'text' | 'long_text' | 'number' | 'date' | 'checkbox' | 'select' |
     * 'url' | 'email' | 'user'
     */
    type: text('type').notNull().default('text'),

    /** Whether the agent may populate this field at all. */
    agentFilled: boolean('agent_filled').notNull().default(true),
    /**
     * How to fill it, in prose. e.g. "The provider's InfiniBand
     * oversubscription ratio, as 1:1, 2:1 or 4:1. Only record it if stated
     * explicitly — never infer it from marketing copy."
     */
    agentBrief: text('agent_brief'),

    isRequired: boolean('is_required').notNull().default(false),
    showOnTable: boolean('show_on_table').notNull().default(false),
    position: integer('position').notNull().default(0),
    archivedAt: timestamp('archived_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [uniqueIndex('field_definitions_entity_key').on(t.entity, t.key)],
);

export const fieldOptions = pgTable(
  'field_options',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    fieldId: uuid('field_id')
      .notNull()
      .references(() => fieldDefinitions.id, { onDelete: 'cascade' }),
    label: text('label').notNull(),
    position: integer('position').notNull().default(0),
    archivedAt: timestamp('archived_at', { withTimezone: true }),
  },
  (t) => [index('field_options_field_idx').on(t.fieldId)],
);

/**
 * Values, typed by column rather than serialised into JSON, so that Postgres
 * can sort and filter them properly. The uniqueness constraints keep one value
 * per field per record.
 */
export const fieldValues = pgTable(
  'field_values',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    fieldId: uuid('field_id')
      .notNull()
      .references(() => fieldDefinitions.id, { onDelete: 'cascade' }),

    accountId: uuid('account_id').references(() => accounts.id, { onDelete: 'cascade' }),
    contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }),
    demandDealId: uuid('demand_deal_id').references(() => demandDeals.id, {
      onDelete: 'cascade',
    }),
    supplyDealId: uuid('supply_deal_id').references(() => supplyDeals.id, {
      onDelete: 'cascade',
    }),

    textValue: text('text_value'),
    numberValue: numeric('number_value', { precision: 24, scale: 4 }),
    dateValue: timestamp('date_value', { withTimezone: true }),
    boolValue: boolean('bool_value'),
    optionId: uuid('option_id').references(() => fieldOptions.id, { onDelete: 'set null' }),
    userValue: uuid('user_value').references(() => users.id, { onDelete: 'set null' }),

    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    uniqueIndex('field_values_account_key').on(t.fieldId, t.accountId),
    uniqueIndex('field_values_contact_key').on(t.fieldId, t.contactId),
    uniqueIndex('field_values_demand_deal_key').on(t.fieldId, t.demandDealId),
    uniqueIndex('field_values_supply_deal_key').on(t.fieldId, t.supplyDealId),
  ],
);

export type FieldDefinition = typeof fieldDefinitions.$inferSelect;
export type FieldValue = typeof fieldValues.$inferSelect;
