/**
 * The demand side: what customers want, and what we have sold them.
 *
 * The stage set follows how enterprise AI-infrastructure deals are publicly
 * described as progressing, with `legal` deliberately second — see the note on
 * DEMAND_STAGES in @pig/core. A `capacityRequest` is modelled separately from
 * the deal because the technical shape of what a customer needs is negotiated
 * on a different clock from the commercials, and because it is the thing that
 * gets matched against inventory.
 */
import {
  boolean,
  index,
  integer,
  jsonb,
  numeric,
  pgTable,
  text,
  timestamp,
  uuid,
} from 'drizzle-orm/pg-core';
import {
  demandStageEnum,
  interconnectTypeEnum,
  productLineEnum,
  securityTierEnum,
} from './enums';
import { accounts, contacts } from './crm';
import { users } from './identity';

export const demandDeals = pgTable(
  'demand_deals',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    accountId: uuid('account_id')
      .notNull()
      .references(() => accounts.id, { onDelete: 'cascade' }),

    name: text('name').notNull(),
    description: text('description'),

    /**
     * One account routinely carries several independent opportunities across
     * product lines. Collapsing everything into "GPU hours" would make
     * land-and-expand — the actual motion in this market — invisible.
     */
    productLine: productLineEnum('product_line').notNull().default('compute_reserved'),

    stage: demandStageEnum('stage').notNull().default('qualification'),
    stageChangedAt: timestamp('stage_changed_at', { withTimezone: true }).notNull().defaultNow(),

    ownerUserId: uuid('owner_user_id').references(() => users.id, { onDelete: 'set null' }),
    primaryContactId: uuid('primary_contact_id').references(() => contacts.id, {
      onDelete: 'set null',
    }),

    /** Annual contract value in cents. */
    acvCents: integer('acv_cents'),
    /** Total contract value across the whole term, in cents. */
    tcvCents: integer('tcv_cents'),
    currency: text('currency').notNull().default('USD'),
    termMonths: integer('term_months'),

    /** Forecast confidence, 0–1. Distinct from stage: late deals still die. */
    probability: numeric('probability', { precision: 4, scale: 3 }),
    expectedCloseDate: timestamp('expected_close_date', { withTimezone: true }),
    closedAt: timestamp('closed_at', { withTimezone: true }),
    closedReason: text('closed_reason'),

    /**
     * Whether legal is cleared. Tracked as a flag as well as a stage, because a
     * deal can advance past `legal` into scoping while an amendment is still
     * outstanding, and shipping capacity without executed paper is the mistake
     * this field exists to prevent.
     */
    msaExecuted: boolean('msa_executed').notNull().default(false),
    dpaExecuted: boolean('dpa_executed').notNull().default(false),

    /** Set when this deal is a renewal or expansion of an earlier one. */
    parentDealId: uuid('parent_deal_id'),

    lastActivityAt: timestamp('last_activity_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index('demand_deals_account_idx').on(t.accountId),
    index('demand_deals_stage_idx').on(t.stage),
    index('demand_deals_owner_idx').on(t.ownerUserId),
    index('demand_deals_close_date_idx').on(t.expectedCloseDate),
  ],
);

/**
 * The technical shape of what a customer needs.
 *
 * Separated from the deal because this is the object that gets matched against
 * supply. A seller asking "what have we got that fits this?" is asking about
 * hardware, dates and constraints — not about ACV.
 */
export const capacityRequests = pgTable(
  'capacity_requests',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    demandDealId: uuid('demand_deal_id')
      .notNull()
      .references(() => demandDeals.id, { onDelete: 'cascade' }),

    gpuType: text('gpu_type'),
    /** Acceptable substitutes, in preference order. Widens the match. */
    gpuTypeAlternatives: jsonb('gpu_type_alternatives').$type<string[]>().notNull().default([]),
    gpuCount: integer('gpu_count').notNull(),

    /**
     * A hard requirement for distributed training and irrelevant for inference.
     * Getting this wrong in either direction is expensive: sell Ethernet to a
     * training customer and it fails; insist on InfiniBand for an inference
     * customer and you lose on price.
     */
    requiresHighSpeedInterconnect: boolean('requires_high_speed_interconnect')
      .notNull()
      .default(false),
    minInterconnectType: interconnectTypeEnum('min_interconnect_type'),

    minSecurityTier: securityTierEnum('min_security_tier').notNull().default('secure_cloud'),

    /** Regions the workload may run in, and those it may not. */
    allowedRegions: jsonb('allowed_regions').$type<string[]>().notNull().default([]),
    excludedJurisdictions: jsonb('excluded_jurisdictions').$type<string[]>().notNull().default([]),

    startsAt: timestamp('starts_at', { withTimezone: true }),
    endsAt: timestamp('ends_at', { withTimezone: true }),
    /** Total hours wanted; drives the allocation arithmetic. */
    totalGpuHours: numeric('total_gpu_hours', { precision: 16, scale: 2 }),

    /** Ceiling the customer will pay, in cents. The other half of the spread. */
    maxPricePerGpuHourCents: integer('max_price_per_gpu_hour_cents'),

    /** Compliance requirements surfaced during procurement. */
    requiredCertifications: jsonb('required_certifications').$type<string[]>().notNull().default([]),

    notes: text('notes'),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index('capacity_requests_deal_idx').on(t.demandDealId),
    index('capacity_requests_gpu_type_idx').on(t.gpuType),
    index('capacity_requests_window_idx').on(t.startsAt, t.endsAt),
  ],
);

/** Many-to-many between deals and the people involved in them. */
export const dealContacts = pgTable(
  'deal_contacts',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    demandDealId: uuid('demand_deal_id')
      .notNull()
      .references(() => demandDeals.id, { onDelete: 'cascade' }),
    contactId: uuid('contact_id')
      .notNull()
      .references(() => contacts.id, { onDelete: 'cascade' }),
    /** economic buyer | technical evaluator | champion | procurement | legal | blocker */
    role: text('role'),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index('deal_contacts_deal_idx').on(t.demandDealId),
    index('deal_contacts_contact_idx').on(t.contactId),
  ],
);

export type DemandDeal = typeof demandDeals.$inferSelect;
export type NewDemandDeal = typeof demandDeals.$inferInsert;
export type CapacityRequest = typeof capacityRequests.$inferSelect;
export type NewCapacityRequest = typeof capacityRequests.$inferInsert;
