/**
 * The supply side of the demo book: who we buy capacity from, where it sits,
 * what we committed to, what the market is quoting today, and the pipeline
 * behind all of it.
 *
 * Suppliers are REAL companies — see the integrity note in `./index.ts`. Only
 * the commitments, sites and prices are invented, and every one of them is
 * labelled. Two counterparties here are invented outright, and deliberately:
 * a broker whose margin we complain about and a network we rejected are
 * negative commercial judgements, and those must never attach to a real named
 * company. Both carry the prefix and a `.example` domain, which cannot resolve.
 */
import type {
  GpuSocket,
  InterconnectType,
  SecurityTier,
  StockStatus,
  SupplierType,
  SupplyStage,
} from '@pig/core';
import { and, eq } from 'drizzle-orm';
import {
  accounts,
  capacityCommitments,
  inventoryListings,
  sites,
  supplyDeals,
  users,
  type NewInventoryListing,
} from '../../schema/index';
import { seedSupplyPaper } from './contracts';
import type { DemoContext } from './index';

/**
 * Archetype corrections against the base seed, which types every supplier
 * `neocloud`.
 *
 * The type drives qualification defaults, so a flat taxonomy makes the supply
 * pipeline lie about how these counterparties actually behave: a facility owner
 * negotiates power and term, a community network cannot be negotiated with at
 * all. Each of these is a publicly checkable fact about the business, not a
 * commercial term — Crusoe builds and operates its own energy-sited facilities,
 * RunPod's Community Cloud is long-tail individual owners, Datacrunch is a
 * Finnish operator whose whole proposition is EU jurisdiction.
 *
 * CoreWeave and Lambda are deliberately absent: `./agent.ts` seeds an applied
 * fact asserting `supplierType=neocloud` for the first and a proposed one for
 * the second, and a seed that contradicts its own agent provenance is worse
 * than a flat taxonomy.
 *
 * The durable home for this is the SUPPLIERS list in `../index.ts`; correcting
 * it here means the demo is right today without editing a file this task does
 * not own. See the handover note.
 */
const SUPPLIER_TYPE_CORRECTIONS: ReadonlyArray<{ domain: string; type: SupplierType }> = [
  { domain: 'crusoe.ai', type: 'datacenter' },
  { domain: 'runpod.io', type: 'community' },
  { domain: 'datacrunch.io', type: 'regional_operator' },
];

/** A tranche of a commitment's ramp: how long, and how many GPUs are held. */
interface Tranche {
  readonly days: number;
  readonly gpus: number;
}

interface SupplyBlock {
  readonly domain: string;
  readonly site: {
    readonly name: string;
    readonly externalDataCenterId: string;
    readonly country: string;
    readonly countryCode: string;
    readonly region: string;
    readonly city: string;
    readonly jurisdiction: string;
    readonly powerMw: string;
    readonly pue: string;
    readonly uptimeHistoryPct: string;
    readonly certifications: string[];
  };
  readonly deal: {
    readonly stage: SupplyStage;
    readonly technicalVerdict: string;
    readonly technicalNotes: string;
    readonly financialVerdict: string;
    readonly financialNotes: string;
  };
  readonly commitment: {
    readonly name: string;
    readonly gpuType: string;
    readonly socket: GpuSocket;
    readonly gpuCount: number;
    readonly interconnectType: InterconnectType;
    readonly securityTier?: SecurityTier;
    /** Days from now to the first hour of the block. Negative is the past. */
    readonly startDay: number;
    readonly days: number;
    /** Wall-clock hours actually contracted, after maintenance and holdbacks. */
    readonly efficiency?: number;
    readonly costPerGpuHourCents: number;
    readonly takeOrPayFloorPct: string;
    readonly prepaidPct: string;
    readonly noticeDays: number;
    readonly isAutoRenew: boolean;
    readonly oversubscriptionPct?: string;
    readonly costOfCapitalBps: number;
    readonly financingInstrument: string;
    /** Tranches before the final quantity. Empty means a flat rectangle. */
    readonly ramp: readonly Tranche[];
    /** GPUs held during the last interval — lower than `gpuCount` is a step-down. */
    readonly finalGpus: number;
    readonly notes: string;
  };
  /**
   * Whether to write an MSA, SLA and obligations for this block. Off for the
   * lapsed one: `seedSupplyPaper` dates its paper relative to now on the
   * assumption the block is live, and an MSA effective two months ago against
   * capacity that started thirteen months ago reads worse than no paper at all.
   */
  readonly paper: boolean;
  /** Whether the demand book may draw on this block. */
  readonly sellable: boolean;
}

/**
 * The blocks, built from the prefix rather than declared with it baked in.
 *
 * A plain top-level constant would have to reach back into `./index.ts` for
 * `PREFIX` while that module is still evaluating. See the note on
 * `DemoContext`: taking the prefix as an argument is what keeps this file
 * free of that hazard.
 *
 * Start dates are staggered across thirteen months. They used to share one
 * day, which made a business that has bought five blocks look like a business
 * founded last quarter, and left the capacity screen with nothing to say about
 * a block's position in its term.
 */
function supplyBlocks(prefix: string): SupplyBlock[] {
  return [
    {
      domain: 'coreweave.com',
      site: {
        name: 'US-East (NJ)',
        externalDataCenterId: 'us-east-nj-01',
        country: 'United States',
        countryCode: 'US',
        region: 'us-east',
        city: 'Weehawken',
        jurisdiction: 'United States',
        powerMw: '38.500',
        pue: '1.15',
        uptimeHistoryPct: '99.940',
        certifications: ['SOC 2 Type II', 'ISO 27001', 'ISO 27017', 'HIPAA'],
      },
      deal: {
        stage: 'live',
        technicalVerdict: 'pass',
        technicalNotes: 'NCCL all-reduce within 8% of theoretical across the full fabric.',
        financialVerdict: 'pass',
        financialNotes:
          'Break-even at $2.52/GPU-hr if only three quarters of the block sells, against a ' +
          'market quoting $2.59–3.35 on demand. The spread is the business.',
      },
      commitment: {
        name: `${prefix}512× H200 reserved, 12 months`,
        gpuType: 'H200',
        socket: 'SXM5',
        gpuCount: 512,
        interconnectType: 'Infiniband',
        startDay: -210,
        days: 365,
        costPerGpuHourCents: 189,
        takeOrPayFloorPct: '100',
        prepaidPct: '25',
        noticeDays: 90,
        isAutoRenew: true,
        costOfCapitalBps: 950,
        financingInstrument: 'Revolving credit facility',
        // Two tranches: a quarter of the fleet while the halls were fitted,
        // then a partial hand-over, then everything. The anchor block is the
        // one whose ramp a customer actually negotiated around.
        ramp: [
          { days: 12, gpus: 128 },
          { days: 14, gpus: 320 },
        ],
        finalGpus: 512,
        notes: 'Illustrative demo data. Not a real contract.',
      },
      paper: true,
      sellable: true,
    },
    {
      domain: 'nebius.com',
      site: {
        name: 'EU-North (FI)',
        externalDataCenterId: 'eu-north-fi-01',
        country: 'Finland',
        countryCode: 'FI',
        region: 'eu-north',
        city: 'Mäntsälä',
        jurisdiction: 'European Union',
        powerMw: '24.000',
        pue: '1.12',
        uptimeHistoryPct: '99.900',
        // The certifications a German health customer gates procurement on.
        // Dropping either of these takes the EU deal off this block entirely.
        certifications: ['ISO 27001', 'SOC 2 Type II', 'ISO 27017', 'EU data residency'],
      },
      deal: {
        stage: 'live',
        technicalVerdict: 'pass',
        technicalNotes: 'Fabric verified at 400G per GPU. Storage tier is shared, and says so.',
        financialVerdict: 'conditional',
        financialNotes:
          'A 46% markup needs ~69% of the block sold to clear cost. Signed anyway for the EU ' +
          'residency it unlocks; the step-down at month five is the hedge.',
      },
      commitment: {
        name: `${prefix}128× H100 reserved, 6 months`,
        gpuType: 'H100_80GB',
        socket: 'SXM5',
        gpuCount: 128,
        interconnectType: 'Infiniband',
        startDay: -95,
        days: 180,
        costPerGpuHourCents: 171,
        takeOrPayFloorPct: '80',
        prepaidPct: '0',
        noticeDays: 60,
        isAutoRenew: true,
        costOfCapitalBps: 1_150,
        financingInstrument: 'Term loan',
        // A STEP-DOWN, not a ramp: a renegotiation checkpoint at month five
        // hands a quarter of the fleet back. This is the shape the schema
        // comment exists for, and no flat envelope can express it.
        ramp: [{ days: 150, gpus: 128 }],
        finalGpus: 96,
        notes:
          'Illustrative demo data. Not a real contract. Steps down to 96 GPUs for the final ' +
          'thirty days under the month-five checkpoint.',
      },
      paper: true,
      sellable: true,
    },
    {
      domain: 'crusoe.ai',
      site: {
        name: 'US-Central (TX)',
        externalDataCenterId: 'us-central-tx-02',
        country: 'United States',
        countryCode: 'US',
        region: 'us-central',
        city: 'Midland',
        jurisdiction: 'United States',
        powerMw: '12.500',
        pue: '1.08',
        uptimeHistoryPct: '99.820',
        // Energy management sits on this site's certification list and nowhere
        // else in the book, because the facility is sited on stranded power.
        certifications: ['SOC 2 Type II', 'ISO 27001', 'ISO 50001'],
      },
      deal: {
        stage: 'live',
        technicalVerdict: 'pass',
        technicalNotes: 'Blackwell fabric verified. Two nodes returned on burn-in and replaced.',
        financialVerdict: 'pass',
        financialNotes:
          'Break-even at $4.07/GPU-hr at 75% sold, against $4.59–5.49 on demand for the same ' +
          'part. Blackwell scarcity is doing the work, and will not last the term.',
      },
      commitment: {
        name: `${prefix}64× B200 reserved, 9 months`,
        gpuType: 'B200',
        socket: 'SXM6',
        gpuCount: 64,
        interconnectType: 'Infiniband',
        startDay: -60,
        days: 270,
        costPerGpuHourCents: 305,
        takeOrPayFloorPct: '100',
        prepaidPct: '15',
        noticeDays: 90,
        // Not auto-renewing: Blackwell pricing is falling and we want the
        // block back on the market at the end of the term rather than rolled.
        isAutoRenew: false,
        costOfCapitalBps: 1_400,
        financingInstrument: 'Equipment lease',
        // Half the fleet for the first three weeks — Blackwell deliveries slip,
        // and the contract prices the slip rather than pretending it away.
        ramp: [{ days: 18, gpus: 32 }],
        finalGpus: 64,
        notes: 'Illustrative demo data. Not a real contract.',
      },
      paper: true,
      sellable: true,
    },
    {
      domain: 'runpod.io',
      site: {
        name: 'Community pool',
        externalDataCenterId: 'community-us-west',
        country: 'United States',
        countryCode: 'US',
        region: 'us-west',
        city: 'Distributed',
        jurisdiction: 'United States',
        powerMw: '0.000',
        pue: '1.45',
        uptimeHistoryPct: '98.100',
        // Empty, and that is the point: community capacity carries no
        // certifications, which is what puts it out of reach of half the book.
        certifications: [],
      },
      deal: {
        stage: 'live',
        technicalVerdict: 'conditional',
        technicalNotes:
          'Ethernet only, mixed host generations. Fine for inference and single-node work, ' +
          'excluded from anything that has to train across nodes.',
        financialVerdict: 'pass',
        financialNotes:
          'No floor and no prepayment, so the downside is bounded at whatever we draw. ' +
          'Break-even at $1.28/GPU-hr at 75% sold.',
      },
      commitment: {
        name: `${prefix}32× A100 burst pool, 3 months`,
        gpuType: 'A100_80GB',
        socket: 'SXM4',
        gpuCount: 32,
        interconnectType: 'Ethernet',
        securityTier: 'community_cloud',
        startDay: -30,
        days: 90,
        costPerGpuHourCents: 96,
        takeOrPayFloorPct: '0',
        prepaidPct: '0',
        noticeDays: 30,
        isAutoRenew: false,
        // Sold beyond 100% on purpose. A burst pool is bought precisely because
        // not every holder draws at once, and recording the intent stops the
        // margin view reporting a deliberate policy as a fault.
        oversubscriptionPct: '25',
        costOfCapitalBps: 1_800,
        financingInstrument: 'Operating cash',
        // No ramp. The pool is drawn from a shared fleet rather than delivered
        // in tranches, so there is no shape to describe — and the ramp that
        // used to be here claimed fewer GPU-hours than the block contracted.
        ramp: [],
        finalGpus: 32,
        notes: 'Illustrative demo data. Not a real contract.',
      },
      paper: true,
      sellable: true,
    },
    {
      /*
       * The block that ended.
       *
       * Every other record in this book is hopeful and current. This one ran
       * its full twelve months and stopped five weeks ago, which is the only
       * reason the business does not appear to have been founded a quarter
       * ago — and the only place a reader can see what a block looks like when
       * it is over.
       *
       * It is also why the supply relationship churned, and the numbers say so
       * without a word of commentary: 214¢/GPU-hr for H100 against the 171¢
       * the same part costs on the Finnish block today, 85% of wall-clock
       * actually contracted, and a site carrying one certification.
       */
      domain: 'lambda.ai',
      site: {
        name: 'US-West (CA)',
        externalDataCenterId: 'us-west-ca-01',
        country: 'United States',
        countryCode: 'US',
        region: 'us-west',
        city: 'San Jose',
        jurisdiction: 'United States',
        powerMw: '8.000',
        pue: '1.24',
        uptimeHistoryPct: '99.410',
        certifications: ['SOC 2 Type II'],
      },
      deal: {
        stage: 'churned',
        technicalVerdict: 'pass',
        technicalNotes:
          'Fabric was never the problem. Availability was: 99.41% observed against a 99.5% ' +
          'negotiated floor, and four separate abatement claims across the term.',
        financialVerdict: 'fail',
        financialNotes:
          'Renewal quoted at 209¢/GPU-hr for the same H100 fleet, a month after equivalent ' +
          'Infiniband capacity cleared at 171¢ in Finland. Let it lapse.',
      },
      commitment: {
        name: `${prefix}256× H100 reserved, 12 months (lapsed)`,
        gpuType: 'H100_80GB',
        socket: 'SXM5',
        gpuCount: 256,
        interconnectType: 'Infiniband',
        startDay: -400,
        days: 365,
        // 85% of wall-clock, not the 94% the live blocks carry. The gap is the
        // outage history, and it is the reason this relationship ended.
        efficiency: 0.85,
        costPerGpuHourCents: 214,
        takeOrPayFloorPct: '100',
        prepaidPct: '30',
        noticeDays: 90,
        isAutoRenew: false,
        costOfCapitalBps: 1_050,
        financingInstrument: 'Term loan',
        // Handed 64 GPUs back at the eight-month mark when the deal it was
        // bought for shrank. The step-down is the first half of the story the
        // churned deal finishes.
        ramp: [{ days: 245, gpus: 256 }],
        finalGpus: 192,
        notes:
          'Illustrative demo data. Not a real contract. Ran to term and was not renewed — see ' +
          'the churned supply deal on this account.',
      },
      paper: false,
      sellable: false,
    },
  ];
}

/**
 * Counterparties invented for this book, and why they are not real companies.
 *
 * A broker is in it for the spread it stacks on someone else's capacity, and a
 * rejection is a recorded judgement that a supplier is not good enough. Both
 * are commercially damaging claims. Attaching either to a real named business
 * would be fabricating a commercial record about it, which the note at the top
 * of `./index.ts` rules out — so these two are invented, prefixed, and given
 * `.example` domains that cannot resolve.
 */
interface InventedSupplier {
  readonly name: string;
  readonly domain: string;
  readonly type: SupplierType;
  readonly country: string;
  readonly description: string;
  readonly lastActivityDaysAgo: number;
  readonly deal: {
    readonly name: string;
    readonly stage: SupplyStage;
    readonly gpuType: string;
    readonly gpuCount: number;
    readonly interconnectType: InterconnectType;
    readonly targetCostPerGpuHourCents: number;
    readonly termMonths: number;
    readonly availableInDays: number;
    readonly technicalVerdict: string;
    readonly technicalNotes: string;
    readonly financialVerdict: string;
    readonly financialNotes: string;
    readonly rejectionReason: string | null;
  };
}

function inventedSuppliers(prefix: string): InventedSupplier[] {
  return [
    {
      name: `${prefix}Meridian Compute Partners`,
      domain: 'meridian-compute.example',
      type: 'broker',
      country: 'United States',
      description: 'Fictional capacity broker, for demonstration only.',
      lastActivityDaysAgo: 4,
      deal: {
        name: `${prefix}128× H100 resale — meridian-compute.example`,
        stage: 'pricing',
        gpuType: 'H100_80GB',
        gpuCount: 128,
        interconnectType: 'Infiniband',
        targetCostPerGpuHourCents: 198,
        termMonths: 6,
        availableInDays: 21,
        technicalVerdict: 'pass',
        technicalNotes:
          'Capacity is real and the fabric checks out. The operator underneath is disclosed ' +
          'only after signature, which is itself a term to negotiate.',
        financialVerdict: 'conditional',
        financialNotes:
          "The broker's margin stacks on the operator's: 198¢ against 171¢ for equivalent " +
          'Infiniband capacity contracted directly. Worth paying only for capacity we cannot ' +
          'source ourselves, and only inside a quarter.',
        rejectionReason: null,
      },
    },
    {
      name: `${prefix}Kestrel Grid Network`,
      domain: 'kestrel-grid.example',
      type: 'depin',
      country: 'Switzerland',
      description: 'Fictional decentralised compute network, for demonstration only.',
      lastActivityDaysAgo: 26,
      deal: {
        name: `${prefix}512× RTX 4090 aggregated — kestrel-grid.example`,
        stage: 'rejected',
        gpuType: 'RTX_4090',
        gpuCount: 512,
        interconnectType: 'Ethernet',
        targetCostPerGpuHourCents: 42,
        termMonths: 1,
        availableInDays: 3,
        // The two-key decision, split the interesting way round: the money was
        // the best in the book and the engineering still said no.
        technicalVerdict: 'fail',
        technicalNotes:
          'No fast fabric, no node provenance, and no way to attest where a given GPU is. ' +
          'Usable for batch inference; unusable for anything that trains across nodes.',
        financialVerdict: 'pass',
        financialNotes:
          'At 42¢/GPU-hr the economics are the best on the book. They are not the issue.',
        rejectionReason:
          'Cannot satisfy the interconnect or the jurisdiction evidence every enterprise deal ' +
          'in this pipeline requires. Revisit if the network ships attested placement.',
      },
    },
  ];
}

// ---------------------------------------------------------------------------
// Inventory — the buy side
// ---------------------------------------------------------------------------

/**
 * Node specifications per accelerator, per GPU.
 *
 * The upstream availability API reports memory, vCPU and RAM for the whole
 * node while PIG stores them per GPU, so these are multiplied out into `raw`
 * and stored divided in the columns — exactly the normalisation `mapListing`
 * performs. A listing that skips it makes an eight-GPU node look eight times
 * dearer than a single card.
 */
const GPU_SPECS: Record<
  string,
  { memoryGb: number; vcpu: number; ramGb: number; diskGb: number }
> = {
  H200: { memoryGb: 141, vcpu: 26, ramGb: 256, diskGb: 3_750 },
  H100_80GB: { memoryGb: 80, vcpu: 26, ramGb: 256, diskGb: 3_750 },
  B200: { memoryGb: 180, vcpu: 28, ramGb: 320, diskGb: 3_750 },
  A100_80GB: { memoryGb: 80, vcpu: 15, ramGb: 125, diskGb: 1_250 },
  L40S: { memoryGb: 48, vcpu: 16, ramGb: 128, diskGb: 1_000 },
  RTX_4090: { memoryGb: 24, vcpu: 8, ramGb: 32, diskGb: 250 },
};

interface ListingSpec {
  /** The supplier account this offer belongs to, or null for an unmatched one. */
  readonly domain: string;
  readonly cloudId: string;
  readonly slug: string;
  readonly gpuType: keyof typeof GPU_SPECS;
  readonly socket: GpuSocket;
  readonly gpuCount: number;
  readonly interconnectType: InterconnectType;
  readonly interconnectGbps: number;
  readonly internetMbps: number;
  readonly region: string;
  readonly country: string;
  readonly tier: SecurityTier;
  readonly stock: StockStatus;
  readonly onDemandCents: number;
  readonly communityCents?: number;
  readonly variable?: boolean;
  readonly spot?: boolean;
  readonly provisioningMinutes: number;
  readonly prepaidHours?: number;
  readonly images?: string[];
  /** How long ago the provider last confirmed this offer. Drives staleness. */
  readonly observedMinutesAgo: number;
}

/**
 * The marketplace as it stands this morning.
 *
 * Thirty offers across nine providers, five accelerators, both interconnect
 * classes and both reliability tiers. Every price brackets the block we hold in
 * the same part: we buy H200 committed at 189¢ while the market quotes 259–335
 * on demand, H100 at 171 against 189–295, B200 at 305 against 459–549. That
 * gap is the entire business, and a capacity screen that cannot show it is
 * asking a seller to take the spread on faith.
 *
 * `observedMinutesAgo` is spread from two minutes to four days on purpose.
 * Inventory prices move hourly; a four-day-old quote is not a price, and the
 * staleness treatment in the UI has nothing to say unless something is stale.
 */
const INVENTORY: readonly ListingSpec[] = [
  // ------------------------------------------------------------- CoreWeave
  {
    domain: 'coreweave.com', cloudId: 'cw-us-east-nj', slug: 'coreweave',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 289, provisioningMinutes: 25, observedMinutesAgo: 4,
    images: ['ubuntu-22-04-cuda-12-6', 'pytorch-2-6'],
  },
  {
    domain: 'coreweave.com', cloudId: 'cw-us-east-nj', slug: 'coreweave',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 64,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 100_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Low',
    onDemandCents: 275, provisioningMinutes: 120, observedMinutesAgo: 9,
  },
  {
    domain: 'coreweave.com', cloudId: 'cw-us-east-nj', slug: 'coreweave',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 265, provisioningMinutes: 20, observedMinutesAgo: 6,
  },
  {
    domain: 'coreweave.com', cloudId: 'cw-us-east-nj', slug: 'coreweave',
    gpuType: 'B200', socket: 'SXM6', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 6_400, internetMbps: 25_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Low',
    onDemandCents: 549, provisioningMinutes: 45, observedMinutesAgo: 12,
  },
  {
    domain: 'coreweave.com', cloudId: 'cw-us-east-nj', slug: 'coreweave',
    gpuType: 'L40S', socket: 'PCIe', gpuCount: 4,
    interconnectType: 'Ethernet', interconnectGbps: 100, internetMbps: 10_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'High',
    onDemandCents: 112, provisioningMinutes: 10, observedMinutesAgo: 180,
  },

  // ---------------------------------------------------------------- Nebius
  {
    domain: 'nebius.com', cloudId: 'nb-eu-north-fi', slug: 'nebius',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 239, provisioningMinutes: 15, observedMinutesAgo: 7,
    images: ['ubuntu-22-04-cuda-12-4'],
  },
  {
    domain: 'nebius.com', cloudId: 'nb-eu-north-fi', slug: 'nebius',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 128,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 100_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'Medium',
    onDemandCents: 219, provisioningMinutes: 240, observedMinutesAgo: 11,
  },
  {
    domain: 'nebius.com', cloudId: 'nb-eu-north-fi', slug: 'nebius',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 269, provisioningMinutes: 20, observedMinutesAgo: 120,
  },

  // ---------------------------------------------------------------- Crusoe
  {
    domain: 'crusoe.ai', cloudId: 'cr-us-central-tx', slug: 'crusoe',
    gpuType: 'B200', socket: 'SXM6', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 6_400, internetMbps: 25_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Low',
    onDemandCents: 489, provisioningMinutes: 60, observedMinutesAgo: 8,
  },
  {
    // Sold out upstream. Search must never return it, which is the case the
    // stock filter exists for and nothing else in the book exercises.
    domain: 'crusoe.ai', cloudId: 'cr-us-central-tx', slug: 'crusoe',
    gpuType: 'B200', socket: 'SXM6', gpuCount: 64,
    interconnectType: 'Infiniband', interconnectGbps: 6_400, internetMbps: 100_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Unavailable',
    onDemandCents: 459, provisioningMinutes: 180, observedMinutesAgo: 1_560,
  },
  {
    domain: 'crusoe.ai', cloudId: 'cr-us-central-tx', slug: 'crusoe',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 255, provisioningMinutes: 30, observedMinutesAgo: 14,
  },
  {
    domain: 'crusoe.ai', cloudId: 'cr-us-central-tx', slug: 'crusoe',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 16,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 25_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Medium',
    onDemandCents: 279, provisioningMinutes: 45, observedMinutesAgo: 180,
  },

  // ---------------------------------------------------------------- Lambda
  {
    domain: 'lambda.ai', cloudId: 'lb-us-west-ca', slug: 'lambda',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-west', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 249, provisioningMinutes: 20, observedMinutesAgo: 5,
  },
  {
    domain: 'lambda.ai', cloudId: 'lb-us-west-ca', slug: 'lambda',
    gpuType: 'A100_80GB', socket: 'SXM4', gpuCount: 8,
    interconnectType: 'RoCE', interconnectGbps: 400, internetMbps: 10_000,
    region: 'us-west', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 179, provisioningMinutes: 10, observedMinutesAgo: 540,
  },
  {
    // Two days old. The price is probably wrong and the UI should say so.
    domain: 'lambda.ai', cloudId: 'lb-us-west-ca', slug: 'lambda',
    gpuType: 'B200', socket: 'SXM6', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 6_400, internetMbps: 25_000,
    region: 'us-west', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 479, provisioningMinutes: 90, observedMinutesAgo: 2_880,
  },

  // ------------------------------------------------------------ Together AI
  {
    domain: 'together.ai', cloudId: 'tg-us-west', slug: 'together',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-west', country: 'United States', tier: 'secure_cloud', stock: 'Medium',
    onDemandCents: 299, provisioningMinutes: 30, observedMinutesAgo: 18,
  },
  {
    // Four days. Effectively a rumour of a price.
    domain: 'together.ai', cloudId: 'tg-us-west', slug: 'together',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 32,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 100_000,
    region: 'us-west', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 275, provisioningMinutes: 120, observedMinutesAgo: 5_760,
  },

  // ----------------------------------------------------------- Voltage Park
  {
    domain: 'voltagepark.com', cloudId: 'vp-us-central', slug: 'voltagepark',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 215, provisioningMinutes: 15, observedMinutesAgo: 21,
  },
  {
    // The cheapest fast fabric on the book, and the largest single offer in it.
    domain: 'voltagepark.com', cloudId: 'vp-us-central', slug: 'voltagepark',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 256,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 100_000,
    region: 'us-central', country: 'United States', tier: 'secure_cloud', stock: 'Low',
    onDemandCents: 199, provisioningMinutes: 300, observedMinutesAgo: 360,
  },

  // ------------------------------------------------------------- Datacrunch
  {
    domain: 'datacrunch.io', cloudId: 'dc-eu-north-fi', slug: 'datacrunch',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 8,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 10_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 259, provisioningMinutes: 20, observedMinutesAgo: 13,
  },
  {
    domain: 'datacrunch.io', cloudId: 'dc-eu-north-fi', slug: 'datacrunch',
    gpuType: 'A100_80GB', socket: 'SXM4', gpuCount: 4,
    interconnectType: 'Ethernet', interconnectGbps: 200, internetMbps: 10_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'High',
    onDemandCents: 165, provisioningMinutes: 8, observedMinutesAgo: 120,
  },
  {
    domain: 'datacrunch.io', cloudId: 'dc-eu-north-fi', slug: 'datacrunch',
    gpuType: 'L40S', socket: 'PCIe', gpuCount: 2,
    interconnectType: 'Ethernet', interconnectGbps: 25, internetMbps: 5_000,
    region: 'eu-north', country: 'Finland', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 89, provisioningMinutes: 5, observedMinutesAgo: 1_860,
  },

  // ---------------------------------------------------------------- RunPod
  {
    domain: 'runpod.io', cloudId: 'rp-secure-us-east', slug: 'runpod',
    gpuType: 'A100_80GB', socket: 'SXM4', gpuCount: 8,
    interconnectType: 'Ethernet', interconnectGbps: 200, internetMbps: 10_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Available',
    onDemandCents: 169, provisioningMinutes: 6, observedMinutesAgo: 34,
  },
  {
    domain: 'runpod.io', cloudId: 'rp-community-us-west', slug: 'runpod',
    gpuType: 'A100_80GB', socket: 'SXM4', gpuCount: 8,
    interconnectType: 'Ethernet', interconnectGbps: 100, internetMbps: 2_500,
    region: 'us-west', country: 'United States', tier: 'community_cloud', stock: 'Available',
    onDemandCents: 129, communityCents: 109, variable: true, spot: true,
    provisioningMinutes: 2, prepaidHours: 0, observedMinutesAgo: 2,
  },
  {
    domain: 'runpod.io', cloudId: 'rp-community-us-west', slug: 'runpod',
    gpuType: 'A100_80GB', socket: 'PCIe', gpuCount: 1,
    interconnectType: 'Ethernet', interconnectGbps: 10, internetMbps: 1_000,
    region: 'us-west', country: 'United States', tier: 'community_cloud', stock: 'High',
    onDemandCents: 114, communityCents: 99, variable: true, spot: true,
    provisioningMinutes: 1, observedMinutesAgo: 3,
  },
  {
    domain: 'runpod.io', cloudId: 'rp-community-us-west', slug: 'runpod',
    gpuType: 'L40S', socket: 'PCIe', gpuCount: 1,
    interconnectType: 'Ethernet', interconnectGbps: 10, internetMbps: 1_000,
    region: 'us-west', country: 'United States', tier: 'community_cloud', stock: 'Medium',
    onDemandCents: 79, communityCents: 69, variable: true,
    provisioningMinutes: 2, observedMinutesAgo: 16,
  },
  {
    domain: 'runpod.io', cloudId: 'rp-community-us-west', slug: 'runpod',
    gpuType: 'RTX_4090', socket: 'PCIe', gpuCount: 1,
    interconnectType: 'Ethernet', interconnectGbps: 10, internetMbps: 1_000,
    region: 'us-west', country: 'United States', tier: 'community_cloud', stock: 'High',
    onDemandCents: 59, communityCents: 44, variable: true, spot: true,
    provisioningMinutes: 1, observedMinutesAgo: 6,
  },
  {
    // Community H100 at 189¢ against 249¢ secure for the same silicon. The
    // whole tier argument, in one row.
    domain: 'runpod.io', cloudId: 'rp-community-us-west', slug: 'runpod',
    gpuType: 'H100_80GB', socket: 'PCIe', gpuCount: 1,
    interconnectType: 'Ethernet', interconnectGbps: 25, internetMbps: 1_000,
    region: 'us-west', country: 'United States', tier: 'community_cloud', stock: 'Low',
    onDemandCents: 189, communityCents: 169, variable: true,
    provisioningMinutes: 3, observedMinutesAgo: 22,
  },

  // -------------------------------------------------------------- Meridian
  {
    // A broker's quote sits above every operator's on the same part, which is
    // exactly what the qualification note on its supply deal argues about.
    domain: 'meridian-compute.example', cloudId: 'md-resale-us-east', slug: 'meridian',
    gpuType: 'H100_80GB', socket: 'SXM5', gpuCount: 64,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 25_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Medium',
    onDemandCents: 295, provisioningMinutes: 480, observedMinutesAgo: 540,
  },
  {
    domain: 'meridian-compute.example', cloudId: 'md-resale-us-east', slug: 'meridian',
    gpuType: 'H200', socket: 'SXM5', gpuCount: 128,
    interconnectType: 'Infiniband', interconnectGbps: 3_200, internetMbps: 100_000,
    region: 'us-east', country: 'United States', tier: 'secure_cloud', stock: 'Low',
    onDemandCents: 335, provisioningMinutes: 720, observedMinutesAgo: 4_320,
  },
];

/** Hours a shape actually delivers. The seed's own reconciliation check. */
function shapedGpuHours(shape: { intervals: string[]; quantities: number[] }): number {
  let total = 0;
  for (let i = 0; i < shape.quantities.length; i++) {
    const from = Date.parse(shape.intervals[i] ?? '');
    const to = Date.parse(shape.intervals[i + 1] ?? '');
    if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) continue;
    total += ((to - from) / 3_600_000) * (shape.quantities[i] ?? 0);
  }
  return total;
}

/**
 * Build the shape from a block's tranches.
 *
 * The API's own validator refuses a commitment whose `totalGpuHours` exceeds
 * what its shape can deliver — a block promising more hours than its ramp
 * allows is not a pessimistic estimate, it is an impossible contract. The seed
 * has to clear the same bar, so it throws rather than writing one. The previous
 * A100 pool did not: a 25% ramp over a 90-day block described 61,056 hours
 * against 64,973 contracted.
 */
function shapeFor(
  startsAt: Date,
  endsAt: Date,
  ramp: readonly Tranche[],
  finalGpus: number,
  totalGpuHours: number,
  name: string,
): { intervals: string[]; quantities: number[] } | null {
  if (ramp.length === 0) {
    const flat = ((endsAt.getTime() - startsAt.getTime()) / 3_600_000) * finalGpus;
    if (totalGpuHours - flat > 0.01) {
      throw new Error(`Demo block "${name}" contracts more hours than its envelope holds.`);
    }
    return null;
  }

  const intervals = [startsAt.toISOString()];
  const quantities: number[] = [];
  let cursor = startsAt.getTime();
  for (const tranche of ramp) {
    cursor += tranche.days * 86_400_000;
    intervals.push(new Date(cursor).toISOString());
    quantities.push(tranche.gpus);
  }
  intervals.push(endsAt.toISOString());
  quantities.push(finalGpus);

  const shape = { intervals, quantities };
  if (cursor >= endsAt.getTime()) {
    throw new Error(`Demo block "${name}" has tranches running past its own end date.`);
  }
  if (totalGpuHours - shapedGpuHours(shape) > 0.01) {
    throw new Error(`Demo block "${name}" contracts more hours than its shape delivers.`);
  }
  return shape;
}

/**
 * Correct the supplier taxonomy, without overwriting anybody's own judgement.
 *
 * Scoped to rows still carrying the base seed's `neocloud` default, so a person
 * who has already retyped an account keeps their answer.
 */
async function correctSupplierTypes(context: DemoContext): Promise<number> {
  const { db } = context;
  let corrected = 0;
  for (const correction of SUPPLIER_TYPE_CORRECTIONS) {
    const updated = await db
      .update(accounts)
      .set({ supplierType: correction.type, updatedAt: new Date() })
      .where(
        and(eq(accounts.domain, correction.domain), eq(accounts.supplierType, 'neocloud')),
      )
      .returning({ id: accounts.id });
    corrected += updated.length;
  }
  return corrected;
}

/** The buy-side marketplace, in the shape the Prime sync would have written it. */
async function seedInventory(
  context: DemoContext,
  accountIds: Map<string, string>,
  siteIds: Map<string, string>,
): Promise<number> {
  const { db, at } = context;
  let written = 0;

  for (const spec of INVENTORY) {
    const gpu = GPU_SPECS[spec.gpuType];
    if (!gpu) continue;

    /*
     * Node totals, as the upstream reports them, kept verbatim in `raw` while
     * the columns hold the per-GPU figures. That asymmetry is `mapListing`'s
     * whole job, and a seeded row that skipped it would not survive being
     * compared with a synced one.
     */
    const raw: Record<string, unknown> = {
      cloudId: spec.cloudId,
      provider: spec.slug,
      dataCenter: spec.cloudId,
      gpuType: spec.gpuType,
      gpuCount: spec.gpuCount,
      gpuMemory: gpu.memoryGb * spec.gpuCount,
      security: spec.tier,
      stockStatus: spec.stock,
      prices: {
        onDemand: (spec.onDemandCents * spec.gpuCount) / 100,
        communityPrice: spec.communityCents != null ? spec.communityCents / 100 : null,
        isVariable: spec.variable ?? false,
        currency: 'USD',
      },
      note: 'Illustrative demo inventory. Not a live quote.',
    };

    const row: NewInventoryListing = {
      // The sync leaves both of these null — it has no way to resolve a
      // provider slug to an account. A seeded row can, and the Capacity screen
      // is materially better for it, so this is a superset of the synced shape
      // rather than a departure from it.
      accountId: accountIds.get(spec.domain) ?? null,
      siteId: siteIds.get(spec.domain) ?? null,
      externalCloudId: spec.cloudId,
      providerSlug: spec.slug,
      gpuType: spec.gpuType,
      socket: spec.socket,
      gpuCount: spec.gpuCount,
      gpuMemoryGb: gpu.memoryGb,
      vcpu: gpu.vcpu * spec.gpuCount,
      memoryGb: gpu.ramGb * spec.gpuCount,
      diskGb: gpu.diskGb * spec.gpuCount,
      internetMbps: spec.internetMbps,
      interconnectGbps: spec.interconnectGbps,
      interconnectType: spec.interconnectType,
      region: spec.region,
      country: spec.country,
      securityTier: spec.tier,
      stockStatus: spec.stock,
      isSpot: spec.spot ?? false,
      provisioningMinutes: spec.provisioningMinutes,
      prepaidHours: spec.prepaidHours != null ? String(spec.prepaidHours) : null,
      onDemandPriceCents: spec.onDemandCents,
      communityPriceCents: spec.communityCents ?? null,
      priceIsVariable: spec.variable ?? false,
      currency: 'USD',
      images: spec.images ?? [],
      raw,
      // `seed`, not `prime_api`: these rows did not come from the upstream, and
      // the provenance column is not the place to pretend otherwise. It also
      // keeps them out of the sync's stale sweep, which marks anything
      // `prime_api` it did not just see as Unavailable.
      source: 'seed',
      observedAt: at(-spec.observedMinutesAgo / 1_440),
    };

    // Upserted on the same natural key the sync uses, so a reseed converges on
    // the same rows instead of accumulating near-duplicates.
    await db
      .insert(inventoryListings)
      .values(row)
      .onConflictDoUpdate({
        target: [
          inventoryListings.externalCloudId,
          inventoryListings.gpuType,
          inventoryListings.socket,
          inventoryListings.gpuCount,
          inventoryListings.securityTier,
        ],
        set: {
          // Re-linked on every run, which the sync's own `set` list cannot do.
          // `--clear` removes the invented broker's account and leaves its
          // listings behind with a null account; a reseed has to heal that.
          accountId: row.accountId,
          siteId: row.siteId,
          stockStatus: row.stockStatus,
          onDemandPriceCents: row.onDemandPriceCents,
          communityPriceCents: row.communityPriceCents,
          priceIsVariable: row.priceIsVariable,
          interconnectGbps: row.interconnectGbps,
          interconnectType: row.interconnectType,
          provisioningMinutes: row.provisioningMinutes,
          region: row.region,
          country: row.country,
          images: row.images,
          raw: row.raw,
          observedAt: row.observedAt,
          updatedAt: new Date(),
        },
      });
    written += 1;
  }

  return written;
}

/** The two invented counterparties and the pipeline entries that justify them. */
async function seedInventedSuppliers(
  context: DemoContext,
  ownerUserId: string | null,
  accountIds: Map<string, string>,
): Promise<void> {
  const { db, prefix, at } = context;

  for (const supplier of inventedSuppliers(prefix)) {
    const [existing] = await db
      .select({ id: accounts.id })
      .from(accounts)
      .where(eq(accounts.name, supplier.name))
      .limit(1);

    const [created] = existing
      ? []
      : await db
          .insert(accounts)
          .values({
            name: supplier.name,
            domain: supplier.domain,
            side: 'supply',
            supplierType: supplier.type,
            country: supplier.country,
            description: supplier.description,
            source: 'seed',
            confidence: 'confirmed',
            lastActivityAt: at(-supplier.lastActivityDaysAgo),
          })
          .returning({ id: accounts.id });

    const accountId = existing?.id ?? created?.id;
    if (!accountId) continue;
    accountIds.set(supplier.domain, accountId);

    const [existingDeal] = await db
      .select({ id: supplyDeals.id })
      .from(supplyDeals)
      .where(eq(supplyDeals.name, supplier.deal.name))
      .limit(1);
    if (existingDeal) continue;

    const d = supplier.deal;
    const closed = d.stage === 'rejected';
    await db.insert(supplyDeals).values({
      accountId,
      name: d.name,
      stage: d.stage,
      stageChangedAt: at(-supplier.lastActivityDaysAgo),
      ownerUserId,
      gpuType: d.gpuType,
      gpuCount: d.gpuCount,
      interconnectType: d.interconnectType,
      targetCostPerGpuHourCents: d.targetCostPerGpuHourCents,
      termMonths: d.termMonths,
      availableFrom: at(d.availableInDays),
      technicalVerdict: d.technicalVerdict,
      technicalVerdictBy: ownerUserId,
      technicalVerdictAt: at(-supplier.lastActivityDaysAgo - 2),
      technicalNotes: d.technicalNotes,
      financialVerdict: d.financialVerdict,
      financialVerdictBy: ownerUserId,
      financialVerdictAt: at(-supplier.lastActivityDaysAgo),
      financialNotes: d.financialNotes,
      rejectionReason: d.rejectionReason,
      closedAt: closed ? at(-supplier.lastActivityDaysAgo) : null,
      lastActivityAt: at(-supplier.lastActivityDaysAgo),
    });
  }
}

/**
 * Seed the supply side, and return the commitment id for each supplier domain.
 *
 * That map is the join the demand book is built on: an allocation is a claim
 * against a specific block, so the demand slice cannot run until this one has
 * said which block is which. The lapsed Lambda block is deliberately NOT in it —
 * it ended five weeks ago, and nothing may be sold out of it.
 */
export async function seedSupply(context: DemoContext): Promise<Map<string, string>> {
  const { db, prefix, at, hours } = context;
  const commitmentIds = new Map<string, string>();
  const accountIds = new Map<string, string>();
  const siteIds = new Map<string, string>();

  // The same user the rest of the seed adopts. Verdicts are attributable or
  // they are vibes, and the two diligence gates exist to record who accepted
  // what — a null there means the gate was never reached, not that nobody owns
  // it, so leaving them empty on a passed gate misreports the record.
  const [owner] = await db.select({ id: users.id }).from(users).orderBy(users.createdAt).limit(1);
  const ownerUserId = owner?.id ?? null;

  const retyped = await correctSupplierTypes(context);

  for (const supplier of supplyBlocks(prefix)) {
    const [account] = await db
      .select({ id: accounts.id })
      .from(accounts)
      .where(eq(accounts.domain, supplier.domain))
      .limit(1);
    if (!account) {
      console.log(`  skipped ${supplier.domain} — run the base seed first`);
      continue;
    }
    accountIds.set(supplier.domain, account.id);

    // Resolved before the commitment guard rather than after it, so a reseed
    // can still attach today's inventory to the site it created last time.
    const siteName = `${prefix}${supplier.site.name}`;
    const [existingSite] = await db
      .select({ id: sites.id })
      .from(sites)
      .where(and(eq(sites.accountId, account.id), eq(sites.name, siteName)))
      .limit(1);
    const [newSite] = existingSite
      ? []
      : await db
          .insert(sites)
          .values({
            accountId: account.id,
            name: siteName,
            externalDataCenterId: supplier.site.externalDataCenterId,
            country: supplier.site.country,
            countryCode: supplier.site.countryCode,
            region: supplier.site.region,
            city: supplier.site.city,
            jurisdiction: supplier.site.jurisdiction,
            powerMw: supplier.site.powerMw,
            pue: supplier.site.pue,
            uptimeHistoryPct: supplier.site.uptimeHistoryPct,
            certifications: supplier.site.certifications,
            notes: 'Illustrative demo site. Not a real facility record.',
          })
          .returning({ id: sites.id });
    const siteId = existingSite?.id ?? newSite?.id;
    if (siteId) siteIds.set(supplier.domain, siteId);

    const c = supplier.commitment;
    const [existing] = await db
      .select({ id: capacityCommitments.id })
      .from(capacityCommitments)
      .where(eq(capacityCommitments.name, c.name))
      .limit(1);
    if (existing) {
      if (supplier.sellable) commitmentIds.set(supplier.domain, existing.id);
      continue;
    }

    const startsAt = at(c.startDay);
    const endsAt = at(c.startDay + c.days);
    const totalGpuHours = hours(c.gpuCount, c.days, c.efficiency);
    const shape = shapeFor(startsAt, endsAt, c.ramp, c.finalGpus, Number(totalGpuHours), c.name);

    // Cost is charged against the FULL commitment. Both money figures derive
    // from that same total, so a floor or a prepayment can never drift away
    // from the block it belongs to.
    const totalCostCents = Math.round(Number(totalGpuHours) * c.costPerGpuHourCents);
    const floorPct = Number(c.takeOrPayFloorPct);
    const prepaidPct = Number(c.prepaidPct);

    // Written before the commitment so the block can point back at the deal it
    // came from — the provenance link the schema has always had a column for.
    const [deal] = await db
      .insert(supplyDeals)
      .values({
        accountId: account.id,
        siteId,
        name: `${prefix}${c.gpuCount}× ${c.gpuType} — ${supplier.domain}`,
        stage: supplier.deal.stage,
        stageChangedAt: startsAt,
        ownerUserId,
        gpuType: c.gpuType,
        gpuCount: c.gpuCount,
        interconnectType: c.interconnectType,
        targetCostPerGpuHourCents: c.costPerGpuHourCents,
        termMonths: Math.round(c.days / 30),
        availableFrom: startsAt,
        technicalVerdict: supplier.deal.technicalVerdict,
        technicalVerdictBy: ownerUserId,
        technicalVerdictAt: at(c.startDay - 21),
        technicalNotes: supplier.deal.technicalNotes,
        financialVerdict: supplier.deal.financialVerdict,
        financialVerdictBy: ownerUserId,
        financialVerdictAt: at(c.startDay - 14),
        financialNotes: supplier.deal.financialNotes,
        // The app closes a deal the moment it reaches live, churned or
        // rejected; a seeded one that leaves the date empty would be the only
        // deal in the book the lifecycle rules do not apply to.
        closedAt: supplier.deal.stage === 'churned' ? endsAt : startsAt,
        lastActivityAt: supplier.deal.stage === 'churned' ? endsAt : at(-3),
      })
      .returning({ id: supplyDeals.id });

    const [commitment] = await db
      .insert(capacityCommitments)
      .values({
        accountId: account.id,
        siteId,
        supplyDealId: deal?.id,
        name: c.name,
        gpuType: c.gpuType,
        socket: c.socket,
        gpuCount: c.gpuCount,
        interconnectType: c.interconnectType,
        securityTier: c.securityTier ?? 'secure_cloud',
        startsAt,
        endsAt,
        totalGpuHours,
        costPerGpuHourCents: c.costPerGpuHourCents,
        shape,
        minimumSpendCents: floorPct > 0 ? Math.round((totalCostCents * floorPct) / 100) : null,
        isAutoRenew: c.isAutoRenew,
        noticeDays: c.noticeDays,
        takeOrPayFloorPct: c.takeOrPayFloorPct,
        prepaidPct: c.prepaidPct,
        prepaidAmountCents:
          prepaidPct > 0 ? Math.round((totalCostCents * prepaidPct) / 100) : null,
        usefulLifeYears: '5',
        depreciationStartAt: startsAt,
        costOfCapitalBps: c.costOfCapitalBps,
        financingInstrument: c.financingInstrument,
        oversubscriptionPct: c.oversubscriptionPct ?? '0',
        notes: c.notes,
      })
      .returning({ id: capacityCommitments.id });

    if (commitment && supplier.sellable) commitmentIds.set(supplier.domain, commitment.id);

    if (supplier.paper) {
      await seedSupplyPaper(context, {
        accountId: account.id,
        domain: supplier.domain,
        capacityCommitmentId: commitment?.id,
        days: c.days,
        takeOrPayFloorPct: c.takeOrPayFloorPct,
        prepaidPct: c.prepaidPct,
      });
    }
  }

  // The remaining supplier accounts carry no block, but they quote prices, so
  // their inventory has to resolve to an account like everyone else's.
  for (const domain of ['together.ai', 'voltagepark.com', 'datacrunch.io']) {
    const [account] = await db
      .select({ id: accounts.id })
      .from(accounts)
      .where(eq(accounts.domain, domain))
      .limit(1);
    if (account) accountIds.set(domain, account.id);
  }

  await seedInventedSuppliers(context, ownerUserId, accountIds);

  // A supply deal still in diligence, so that pipeline is not all `live`.
  const [lambda] = await db
    .select({ id: accounts.id })
    .from(accounts)
    .where(eq(accounts.domain, 'lambda.ai'))
    .limit(1);
  if (lambda) {
    const LAMBDA_DEAL = `${prefix}256× H200 — pricing`;
    const [existingLambdaDeal] = await db
      .select({ id: supplyDeals.id })
      .from(supplyDeals)
      .where(and(eq(supplyDeals.accountId, lambda.id), eq(supplyDeals.name, LAMBDA_DEAL)))
      .limit(1);
    if (!existingLambdaDeal) {
      await db.insert(supplyDeals).values({
        accountId: lambda.id,
        name: LAMBDA_DEAL,
        stage: 'financial_diligence',
        stageChangedAt: at(-9),
        ownerUserId,
        gpuType: 'H200',
        gpuCount: 256,
        interconnectType: 'Infiniband',
        targetCostPerGpuHourCents: 176,
        termMonths: 12,
        availableFrom: at(45),
        technicalVerdict: 'pass',
        technicalVerdictBy: ownerUserId,
        technicalVerdictAt: at(-16),
        technicalNotes: 'Fabric verified. Storage throughput per GPU is below spec — flagged.',
        // A supplier we let go a quarter ago, back with a newer part. The
        // second time round the price is the whole conversation.
        financialNotes:
          'Awaiting a firm quote on the committed tranche. The lapsed H100 block on this ' +
          'account renewed at 209¢; anything above 180¢ here goes the same way.',
        lastActivityAt: at(-3),
      });
    }
  }

  const listings = await seedInventory(context, accountIds, siteIds);

  console.log(
    `  ${listings} inventory listings across ${new Set(INVENTORY.map((l) => l.domain)).size} ` +
      `providers, ${retyped} supplier archetype(s) corrected`,
  );

  return commitmentIds;
}
