/**
 * The supply side: where capacity comes from, what it costs, and what we have
 * committed to buy.
 *
 * This is the half of the business no generic CRM models at all. A supplier is
 * not merely an account with a different label — it has physical sites, priced
 * inventory that changes hourly, and commitments that keep costing money
 * whether or not anyone bought the hours.
 */
import {
  boolean,
  index,
  integer,
  jsonb,
  numeric,
  pgTable,
  text,
  timestamp,
  uniqueIndex,
  uuid,
} from 'drizzle-orm/pg-core';
import {
  gpuSocketEnum,
  interconnectTypeEnum,
  recordSourceEnum,
  securityTierEnum,
  stockStatusEnum,
  supplyStageEnum,
} from './enums';
import { accounts, contacts } from './crm';
import { users } from './identity';

/**
 * A physical facility. Distinct from the account that sells it, because one
 * provider operates many sites with materially different characteristics, and
 * because jurisdiction attaches to the building rather than the company.
 */
export const sites = pgTable(
  'sites',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    accountId: uuid('account_id')
      .notNull()
      .references(() => accounts.id, { onDelete: 'cascade' }),

    name: text('name').notNull(),
    /** Provider's own datacenter identifier, for reconciliation. */
    externalDataCenterId: text('external_data_center_id'),

    country: text('country'),
    countryCode: text('country_code'),
    region: text('region'),
    city: text('city'),

    /**
     * Export-control and data-residency jurisdiction. Some capacity legally
     * cannot serve some customers, which makes this a matching constraint and
     * not a note.
     */
    jurisdiction: text('jurisdiction'),

    /** Contracted power envelope. The real ceiling on how much can ever land here. */
    powerMw: numeric('power_mw', { precision: 10, scale: 3 }),
    /** Power usage effectiveness — a cost driver the supplier rarely volunteers. */
    pue: numeric('pue', { precision: 4, scale: 2 }),

    /** Compliance posture. Enterprise buyers gate on these during procurement. */
    certifications: jsonb('certifications').$type<string[]>().notNull().default([]),

    /** Observed reliability, maintained by hand or by the agent. */
    uptimeHistoryPct: numeric('uptime_history_pct', { precision: 6, scale: 3 }),
    notes: text('notes'),

    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index('sites_account_idx').on(t.accountId),
    index('sites_country_idx').on(t.countryCode),
  ],
);

/**
 * A priced offer of capacity.
 *
 * The field names and value shapes deliberately mirror the Prime Intellect
 * availability API so that synced rows map across without translation, and so
 * that a hand-entered listing from a provider who has no API is directly
 * comparable with a synced one.
 *
 * Listings are volatile — prices and stock move hourly. They are a snapshot of
 * what is purchasable, not a record of what we own; that is
 * `capacityCommitments` below.
 */
export const inventoryListings = pgTable(
  'inventory_listings',
  {
    id: uuid('id').primaryKey().defaultRandom(),

    accountId: uuid('account_id').references(() => accounts.id, { onDelete: 'set null' }),
    siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),

    /** Upstream identifiers, used to reconcile a sync without duplicating rows. */
    externalCloudId: text('external_cloud_id'),
    providerSlug: text('provider_slug'),

    /** Kept as free text: new accelerators ship faster than enum migrations. */
    gpuType: text('gpu_type').notNull(),
    socket: gpuSocketEnum('socket'),
    gpuCount: integer('gpu_count').notNull(),
    gpuMemoryGb: integer('gpu_memory_gb'),

    vcpu: integer('vcpu'),
    memoryGb: integer('memory_gb'),
    diskGb: integer('disk_gb'),
    internetMbps: integer('internet_mbps'),

    /**
     * The field that decides whether this capacity can train or only serve.
     * Ethernet-only hardware sold at a training price is the most common way to
     * be overcharged in this market, so it is indexed and surfaced everywhere.
     */
    interconnectGbps: integer('interconnect_gbps'),
    interconnectType: interconnectTypeEnum('interconnect_type').notNull().default('Unknown'),

    region: text('region'),
    country: text('country'),

    securityTier: securityTierEnum('security_tier').notNull().default('secure_cloud'),
    stockStatus: stockStatusEnum('stock_status').notNull().default('Available'),
    isSpot: boolean('is_spot').notNull().default(false),

    /** Minutes from order to usable. Two orders of magnitude across the market. */
    provisioningMinutes: integer('provisioning_minutes'),
    /** Hours of prepaid time bundled with the listing, where offered. */
    prepaidHours: numeric('prepaid_hours', { precision: 12, scale: 2 }),

    /** Money in cents. Never floats — this feeds margin reporting. */
    onDemandPriceCents: integer('on_demand_price_cents'),
    communityPriceCents: integer('community_price_cents'),
    priceIsVariable: boolean('price_is_variable').notNull().default(false),
    currency: text('currency').notNull().default('USD'),

    images: jsonb('images').$type<string[]>().notNull().default([]),
    raw: jsonb('raw').$type<Record<string, unknown>>(),

    source: recordSourceEnum('source').notNull().default('manual'),
    /** When the upstream last confirmed this. Stale listings mislead sellers. */
    observedAt: timestamp('observed_at', { withTimezone: true }).notNull().defaultNow(),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    /**
     * Natural key for an upstream sync. A provider's inventory is identified by
     * the cloud, the SKU, the socket, the count and the tier; upserting on this
     * keeps repeated syncs idempotent instead of accumulating near-duplicates.
     */
    uniqueIndex('inventory_listings_external_key').on(
      t.externalCloudId,
      t.gpuType,
      t.socket,
      t.gpuCount,
      t.securityTier,
    ),
    index('inventory_listings_gpu_type_idx').on(t.gpuType),
    index('inventory_listings_stock_idx').on(t.stockStatus),
    index('inventory_listings_interconnect_idx').on(t.interconnectType),
    index('inventory_listings_account_idx').on(t.accountId),
    index('inventory_listings_observed_idx').on(t.observedAt),
  ],
);

/**
 * A supply engagement — the pipeline for bringing a provider on.
 *
 * Qualification is split into technical and financial diligence because
 * accepting capacity is a two-key decision: engineering judges whether the
 * cluster can actually do the work, finance judges whether the economics clear.
 * Recording who accepted what, and why, is the difference between a decision
 * and a vibe.
 */
export const supplyDeals = pgTable(
  'supply_deals',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    accountId: uuid('account_id')
      .notNull()
      .references(() => accounts.id, { onDelete: 'cascade' }),
    siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),

    name: text('name').notNull(),
    stage: supplyStageEnum('stage').notNull().default('sourced'),
    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',
    }),

    /** What is on offer. */
    gpuType: text('gpu_type'),
    gpuCount: integer('gpu_count'),
    interconnectType: interconnectTypeEnum('interconnect_type'),
    targetCostPerGpuHourCents: integer('target_cost_per_gpu_hour_cents'),
    termMonths: integer('term_months'),
    availableFrom: timestamp('available_from', { withTimezone: true }),

    /**
     * The two diligence gates, recorded separately and attributably.
     * A null verdict means the gate has not been reached, which is different
     * from having been considered and passed.
     */
    technicalVerdict: text('technical_verdict'),
    technicalVerdictBy: uuid('technical_verdict_by').references(() => users.id, {
      onDelete: 'set null',
    }),
    technicalVerdictAt: timestamp('technical_verdict_at', { withTimezone: true }),
    technicalNotes: text('technical_notes'),

    financialVerdict: text('financial_verdict'),
    financialVerdictBy: uuid('financial_verdict_by').references(() => users.id, {
      onDelete: 'set null',
    }),
    financialVerdictAt: timestamp('financial_verdict_at', { withTimezone: true }),
    financialNotes: text('financial_notes'),

    /** Recorded when the stage becomes `rejected`. Rejections teach. */
    rejectionReason: text('rejection_reason'),

    closedAt: timestamp('closed_at', { withTimezone: true }),
    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('supply_deals_account_idx').on(t.accountId),
    index('supply_deals_stage_idx').on(t.stage),
    index('supply_deals_owner_idx').on(t.ownerUserId),
  ],
);

/**
 * Capacity we have actually committed to buy.
 *
 * This is the cost side of the margin ledger. Unlike a listing, a commitment
 * is a liability: the hours are paid for whether or not anyone uses them. That
 * is precisely why idle capacity is worth alerting on, and why margin is
 * computed against the full commitment rather than only the hours that sold.
 */
export const capacityCommitments = pgTable(
  'capacity_commitments',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    accountId: uuid('account_id')
      .notNull()
      .references(() => accounts.id, { onDelete: 'restrict' }),
    siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),
    supplyDealId: uuid('supply_deal_id').references(() => supplyDeals.id, {
      onDelete: 'set null',
    }),

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

    gpuType: text('gpu_type').notNull(),
    socket: gpuSocketEnum('socket'),
    gpuCount: integer('gpu_count').notNull(),
    interconnectType: interconnectTypeEnum('interconnect_type').notNull().default('Unknown'),
    securityTier: securityTierEnum('security_tier').notNull().default('secure_cloud'),

    startsAt: timestamp('starts_at', { withTimezone: true }).notNull(),
    endsAt: timestamp('ends_at', { withTimezone: true }).notNull(),

    /**
     * Total contracted GPU-hours. Stored explicitly rather than derived from
     * count × duration, because real contracts include ramp periods,
     * maintenance windows and holdbacks that no formula predicts.
     */
    totalGpuHours: numeric('total_gpu_hours', { precision: 16, scale: 2 }).notNull(),
    costPerGpuHourCents: integer('cost_per_gpu_hour_cents').notNull(),
    currency: text('currency').notNull().default('USD'),

    /**
     * The capacity SHAPE — how much is actually held at each point in time.
     *
     * A commitment is not a rectangle. Real contracts ramp over tranches, step
     * down at renegotiation checkpoints, and hold different node counts in
     * different months. A single start/end/total flattens all of that and then
     * reports availability that does not exist in the month someone wants it.
     *
     * Modelled as parallel arrays of interval boundaries and the quantity held
     * during each interval — the primitive used by compute exchanges that
     * actually trade this. Availability at any instant is then simply:
     *
     *     available(t) = shapeQuantityAt(t) − Σ overlapping allocations(t)
     *
     * `startsAt`, `endsAt` and `gpuCount` above remain as the coarse envelope,
     * for indexing and for the common flat case. Where `shape` is present it
     * is authoritative.
     */
    shape: jsonb('shape').$type<{
      /** ISO-8601 boundaries, ascending. n+1 entries for n intervals. */
      intervals: string[];
      /** GPUs held during each interval. Length = intervals.length − 1. */
      quantities: number[];
    }>(),

    /**
     * Placement constraints. Contiguity and adjacency are commercial terms
     * here, not deployment details: a customer needing one contiguous 512-GPU
     * block cannot use two 256-GPU blocks in different halls, and a deal dies
     * on exactly that distinction.
     */
    colocateWith: jsonb('colocate_with').$type<string[]>().notNull().default([]),
    isContiguous: boolean('is_contiguous').notNull().default(true),

    /** Contractual floor: the minimum we owe regardless of usage. */
    minimumSpendCents: integer('minimum_spend_cents'),
    isAutoRenew: boolean('is_auto_renew').notNull().default(false),
    /** Days of notice required to exit. Drives renewal alerting. */
    noticeDays: integer('notice_days'),

    /**
     * Take-or-pay floor as a percentage of contracted volume — the share we
     * owe whether or not we draw it. This is the field that turns a
     * commitment from an option into a liability, and it is why idle capacity
     * is worth alerting on rather than merely noting.
     */
    takeOrPayFloorPct: numeric('take_or_pay_floor_pct', { precision: 6, scale: 2 }),
    /** Share of total value paid upfront. Prepayment is common and material. */
    prepaidPct: numeric('prepaid_pct', { precision: 6, scale: 2 }),
    prepaidAmountCents: integer('prepaid_amount_cents'),

    /**
     * Depreciation assumptions for any hardware we own against this block.
     *
     * Useful life is the single largest swing variable in compute unit
     * economics — operators in this market publish anything from four to six
     * years for identical hardware, and the same contract can show roughly
     * 10% or 25% contribution margin depending purely on that choice. Storing
     * the assumption per block, rather than as a company-wide constant, is
     * what makes margin recomputable under different scenarios instead of
     * being an unexaminable number.
     */
    usefulLifeYears: numeric('useful_life_years', { precision: 4, scale: 2 }),
    salvageValuePct: numeric('salvage_value_pct', { precision: 6, scale: 2 }),
    depreciationStartAt: timestamp('depreciation_start_at', { withTimezone: true }),

    /**
     * Cost of capital for THIS block, in basis points.
     *
     * An attribute of the block rather than of the company, because blocks are
     * funded by different instruments at very different rates — blended debt
     * costs across operators in this market span roughly fourfold. Applying a
     * single corporate rate makes per-deal margin wrong in both directions.
     */
    costOfCapitalBps: integer('cost_of_capital_bps'),
    financingInstrument: text('financing_instrument'),

    /**
     * Deliberate oversubscription. Some capacity is sold beyond 100% on the
     * assumption not every buyer draws their full reservation. Recording the
     * intent stops the margin view from flagging a policy as a bug.
     */
    oversubscriptionPct: numeric('oversubscription_pct', { precision: 6, scale: 2 })
      .notNull()
      .default('0'),

    notes: text('notes'),
    terminatedAt: timestamp('terminated_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
    updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
  },
  (t) => [
    index('capacity_commitments_account_idx').on(t.accountId),
    index('capacity_commitments_gpu_type_idx').on(t.gpuType),
    index('capacity_commitments_window_idx').on(t.startsAt, t.endsAt),
  ],
);

export type Site = typeof sites.$inferSelect;
export type InventoryListing = typeof inventoryListings.$inferSelect;
export type NewInventoryListing = typeof inventoryListings.$inferInsert;
export type SupplyDeal = typeof supplyDeals.$inferSelect;
export type CapacityCommitment = typeof capacityCommitments.$inferSelect;
export type NewCapacityCommitment = typeof capacityCommitments.$inferInsert;
