/**
 * The paper on both sides of the book.
 *
 * Upstream: an MSA per supplier, a negotiated SLA hanging off it, and the
 * dated obligations that are what actually get missed. Downstream: the
 * customer paper — master agreements, DPAs and order forms — which is where
 * the renewal machinery lives, because the lifecycle service reads contracts
 * with `side = 'demand'` and nothing else.
 */
import { and, desc, eq } from 'drizzle-orm';
import {
  accounts,
  allocations,
  contractObligations,
  contracts,
  demandDeals,
  slaMetricTargets,
  slaTerms,
} from '../../schema/index';
import type { DemoContext } from './index';

/**
 * Dated obligations per supplier, spread deliberately across the year.
 *
 * `kind` is one of the five the schema allows. The near-term Nebius notice is
 * kept so the renewal alarm still has something to fire on today.
 */
const OBLIGATION_SCHEDULE: Record<
  string,
  { title: string; kind: 'renewal_notice' | 'payment' | 'true_up'; inDays: number; description: string }[]
> = {
  'nebius.com': [
    {
      title: 'Renewal notice',
      kind: 'renewal_notice',
      inDays: 21,
      description: '90 days notice required to prevent auto-renewal.',
    },
    {
      title: 'Quarterly instalment',
      kind: 'payment',
      inDays: 75,
      description: 'Committed spend invoiced quarterly in arrears.',
    },
  ],
  'coreweave.com': [
    {
      title: 'Renewal notice',
      kind: 'renewal_notice',
      inDays: 95,
      description: '90 days notice required to prevent auto-renewal.',
    },
    {
      title: 'Prepayment drawdown reconciliation',
      kind: 'payment',
      inDays: 40,
      description: 'Reconcile the 25% prepayment against hours actually drawn.',
    },
    {
      title: 'Take-or-pay true-up',
      kind: 'true_up',
      inDays: 130,
      // The obligation that turns idle capacity from a metric into an invoice.
      description: 'Shortfall against the 100% floor becomes payable at the true-up date.',
    },
  ],
  'crusoe.ai': [
    {
      title: 'Renewal notice',
      kind: 'renewal_notice',
      inDays: 160,
      description: '90 days notice required to prevent auto-renewal.',
    },
  ],
  'runpod.io': [
    {
      title: 'Renewal notice',
      kind: 'renewal_notice',
      inDays: 250,
      description: '90 days notice required to prevent auto-renewal.',
    },
  ],
};

/** What the supply slice knows about a block, and all this one needs of it. */
export interface SupplyPaper {
  accountId: string;
  domain: string;
  /** Absent only if the commitment insert returned nothing. */
  capacityCommitmentId: string | undefined;
  days: number;
  takeOrPayFloorPct: string;
  prepaidPct: string;
}

/**
 * Write the MSA, the SLA beneath it, its terms, and the obligation schedule.
 *
 * Called from inside the supplier loop rather than in a pass of its own,
 * because the SLA and the obligations are children of the MSA and the MSA is a
 * child of the commitment that was just written.
 */
export async function seedSupplyPaper(context: DemoContext, supply: SupplyPaper): Promise<void> {
  const { db, prefix, at } = context;

  const [msa] = await db
    .insert(contracts)
    .values({
      accountId: supply.accountId,
      type: 'msa',
      status: 'executed',
      side: 'supply',
      title: `${prefix}MSA — ${supply.domain}`,
      capacityCommitmentId: supply.capacityCommitmentId,
      // The anchor tenant's paper predates the block by months. Without one
      // contract genuinely in the past, every `contract_effective` event on
      // the calendar sits in the same fortnight and the view teaches nothing.
      effectiveAt: supply.domain === 'coreweave.com' ? at(-150) : at(-60),
      expiresAt: at(supply.days + 60),
      isAutoRenew: true,
      noticeDays: 90,
      takeOrPayFloorPct: supply.takeOrPayFloorPct,
      prepaidPct: supply.prepaidPct,
      terminationTier: supply.prepaidPct !== '0' ? '1_prepaid' : '2_take_or_pay',
      governingLaw: 'New York',
    })
    .returning();

  if (!msa) return;

  // A negotiated SLA with fee abatement — the remedy that actually matters
  // on the supply side, and the one a credits-only model cannot express.
  const [sla] = await db
    .insert(contracts)
    .values({
      accountId: supply.accountId,
      type: 'sla',
      status: 'executed',
      side: 'supply',
      title: `${prefix}SLA — ${supply.domain}`,
      parentContractId: msa.id,
      effectiveAt: at(-60),
    })
    .returning();

  if (sla) {
    await db.insert(slaTerms).values({
      contractId: sla.id,
      kind: 'negotiated',
      uptimeTargetPct: '99.500',
      measurementUnit: 'node',
      measurementWindow: 'monthly',
      remedyType: 'fee_abatement',
      abatementTriggerValue: 2,
      abatementTriggerUnit: 'business_days',
      nodeReplacementHours: 24,
      claimDeadlineValue: 30,
      claimDeadlineUnit: 'days',
      creditCapPct: '50.000',
      sparePoolObligation: 'Spares held on site sufficient to replace failed nodes and switches.',
      sparePoolScope: ['compute_nodes', 'network_switches'],
      rcaDeliveryHours: 72,
      maintenanceClasses: [
        { class: 'planned', noticeValue: 5, noticeUnit: 'business_days', excludedFromUptime: true },
        { class: 'emergency', noticeValue: 24, noticeUnit: 'hours', excludedFromUptime: false },
      ],
    });
  }

  /*
   * Obligations spread across the year rather than bunched.
   *
   * Three of the four used to fall on the same day at +200, which made
   * every quarter after this one look empty and the current one look
   * uneventful. They are the dated things most likely to be missed, so a
   * demo that cannot show one falling due in each quarter is not showing
   * the feature at all. Payment and true-up dates are here for the same
   * reason: a renewal notice is not the only deadline that costs money.
   */
  const obligationsFor = OBLIGATION_SCHEDULE[supply.domain] ?? [];
  for (const obligation of obligationsFor) {
    await db.insert(contractObligations).values({
      contractId: msa.id,
      title: `${prefix}${obligation.title} — ${supply.domain}`,
      kind: obligation.kind,
      dueAt: at(obligation.inDays),
      description: obligation.description,
    });
  }
}

/* -------------------------------------------------------------------------
 * The customer paper.
 * ---------------------------------------------------------------------- */

type PaperStatus =
  | 'draft'
  | 'in_review'
  | 'in_negotiation'
  | 'out_for_signature'
  | 'executed';

interface PaperObligation {
  title: string;
  /** One of the five the schema allows. */
  kind: 'renewal_notice' | 'milestone' | 'payment' | 'review' | 'true_up';
  inDays: number;
  description: string;
}

/** Service levels as an exhibit, plus any additional committed metrics. */
interface PaperSla {
  terms: Omit<typeof slaTerms.$inferInsert, 'contractId'>;
  metrics?: Omit<typeof slaMetricTargets.$inferInsert, 'slaTermId'>[];
}

interface Paper {
  /** Stable within an account; how a child names its parent. */
  key: string;
  type: 'msa' | 'dpa' | 'order_form';
  status: PaperStatus;
  /** Goes between the prefix and the account name to make the title. */
  label: string;
  parent?: string;
  effectiveInDays?: number;
  expiresInDays?: number;
  isAutoRenew?: boolean;
  noticeDays?: number;
  takeOrPayFloorPct?: string;
  prepaidPct?: string;
  terminationTier?: string;
  assignableOnDefault?: boolean;
  assignmentDeadlineBusinessDays?: number;
  externalReference?: string;
  contractingPartyName?: string;
  /** Order forms carry the money; a master agreement has no value of its own. */
  carriesDealValue?: boolean;
  /** Point the order form at the block that fulfils it. See the schema note. */
  linksCommitment?: boolean;
  notes?: string;
  sla?: PaperSla;
  obligations?: PaperObligation[];
}

interface AccountPaper {
  /** Account name without the prefix; the lookup adds it back. */
  account: string;
  governingLaw: string;
  paper: Paper[];
}

/**
 * Every demo contract says so on its face, on both sides of the book.
 */
const DEMO_CONTRACT_NOTE = 'Illustrative demo data. Not a real contract.';

/**
 * What we grant a customer is not what we hold from a supplier, and the demo
 * has to show the gap.
 *
 * Upstream from CoreWeave we hold fee abatement capped at 50% with 24-hour
 * node replacement (see `seedSupplyPaper`). Downstream to the customer on the
 * same capacity we grant service credits capped at 25% with 48 hours. That
 * spread is the risk the business is actually paid to carry, and it is only
 * legible if both ends of it are in the database.
 */
const HALCYON_SLA: PaperSla = {
  terms: {
    kind: 'negotiated',
    uptimeTargetPct: '99.000',
    measurementUnit: 'node',
    measurementWindow: 'monthly',
    remedyType: 'service_credit',
    nodeReplacementHours: 48,
    mttrHours: 8,
    supportResponseHours: 1,
    claimDeadlineValue: 30,
    claimDeadlineUnit: 'days',
    creditExpiryMonths: 12,
    isSoleRemedy: true,
    rcaDeliveryHours: 96,
    creditSchedule: [
      { belowPct: 99, creditPct: 5 },
      { belowPct: 97, creditPct: 10 },
      { belowPct: 95, creditPct: 25 },
    ],
    creditCapPct: '25.000',
    maintenanceClasses: [
      { class: 'planned', noticeValue: 7, noticeUnit: 'business_days', allowancePerPeriodHours: 8, excludedFromUptime: true },
      { class: 'emergency', noticeValue: 4, noticeUnit: 'hours', excludedFromUptime: false },
    ],
    exclusions: 'Planned maintenance within the monthly allowance, force majeure, and faults in customer-supplied images or code.',
  },
  // A rack-scale cluster is sold at two levels at once. Recording only the
  // headline node figure would misstate what was promised.
  metrics: [
    { metric: 'node_availability_pct', targetValue: '99.000', unit: 'percent' },
    { metric: 'uptime_pct', targetValue: '95.000', unit: 'percent_per_rack' },
    { metric: 'support_response_hours', targetValue: '1.000', unit: 'hours' },
  ],
};

/**
 * The other shape entirely: a reliability tier with credits, sitting at master
 * level so the order form beneath it inherits rather than restates it.
 *
 * `kind` is `credits_policy` deliberately. There is a published target figure
 * and there is no uptime guarantee behind it, and the schema is emphatic that
 * the two must not be shown as the same thing.
 */
const NORTHWIND_SLA: PaperSla = {
  terms: {
    kind: 'credits_policy',
    uptimeTargetPct: '99.500',
    measurementUnit: 'instance',
    measurementWindow: 'monthly',
    remedyType: 'service_credit',
    supportResponseHours: 8,
    claimDeadlineValue: 10,
    claimDeadlineUnit: 'days',
    creditExpiryMonths: 6,
    isSoleRemedy: true,
    creditSchedule: [
      { belowPct: 99.5, creditPct: 5 },
      { belowPct: 98, creditPct: 10 },
    ],
    creditCapPct: '10.000',
    exclusions: 'Maintenance windows, force majeure, customer-caused faults, and any capacity drawn from the community tier.',
  },
};

/**
 * The demand book's paper, keyed by the account names `demand.ts` writes.
 *
 * Read alongside that file: `msaExecuted` and `dpaExecuted` on each deal are
 * assertions, and these rows are the evidence behind them. Where a deal says
 * the DPA is not executed, the DPA here is genuinely unsigned — Verity's sits
 * in review, which is why an EU deal in procurement has not deployed.
 *
 * Dates are chosen so the renewal machinery has all three of its states to
 * show at once: a notice deadline already missed, one about to open, and paper
 * whose term runs with the capacity behind it and needs nothing yet.
 *
 * Built from the prefix rather than declared with it baked in, for the reason
 * given on `DemoContext`.
 */
function demandPaperBook(prefix: string): AccountPaper[] {
  return [
    {
      account: 'Halcyon Research',
      governingLaw: 'Delaware',
      paper: [
        {
          key: 'msa',
          type: 'msa',
          status: 'executed',
          label: 'MSA',
          externalReference: 'HAL-MSA-0114',
          // The master term is annual and renews itself; the order form
          // beneath it runs with the block, which is why the two expiries
          // differ. Effective 313 days ago, so a 365-day term leaves 52 days
          // to run and a 60-day notice deadline that passed eight days ago.
          // Missing that deadline is not a missed reminder: the term has
          // renewed for another year unless the customer agrees otherwise.
          effectiveInDays: -313,
          expiresInDays: 52,
          isAutoRenew: true,
          noticeDays: 60,
          // A frontier lab negotiates step-in. The link that makes it
          // enforceable is `capacityCommitmentId` on the order form below.
          assignableOnDefault: true,
          assignmentDeadlineBusinessDays: 10,
          obligations: [
            {
              title: 'Renewal notice',
              kind: 'renewal_notice',
              inDays: -8,
              description:
                'Sixty days written notice to stop the master term renewing for a further twelve months. The date has passed.',
            },
            {
              title: 'Quarterly service review',
              kind: 'review',
              inDays: 26,
              description:
                'Contractual review of utilisation and incident history; the report is owed five business days beforehand.',
            },
          ],
        },
        {
          key: 'dpa',
          type: 'dpa',
          status: 'executed',
          label: 'DPA',
          parent: 'msa',
          externalReference: 'HAL-DPA-0114',
          // Coterminous with the master, and renewing with it. The notice
          // that governs both is on the MSA, so this carries none of its own.
          effectiveInDays: -313,
          expiresInDays: 52,
          isAutoRenew: true,
        },
        {
          key: 'order_form',
          type: 'order_form',
          status: 'executed',
          label: 'Order form — H200 reserved cluster',
          parent: 'msa',
          externalReference: 'HAL-OF-0221',
          // Dated to the block it draws on, not to the master term.
          effectiveInDays: -30,
          expiresInDays: 335,
          isAutoRenew: false,
          takeOrPayFloorPct: '90',
          prepaidPct: '20',
          terminationTier: '1_prepaid',
          carriesDealValue: true,
          linksCommitment: true,
          sla: HALCYON_SLA,
          obligations: [
            {
              title: 'Quarterly instalment',
              kind: 'payment',
              inDays: 12,
              description:
                'Committed fees invoiced quarterly in advance; the 20% prepayment is credited against the final quarter.',
            },
            {
              title: 'Committed-hours true-up',
              kind: 'true_up',
              inDays: 44,
              // The floor is what makes the backlog real: unused hours are
              // still owed, exactly as they are to the supplier upstream.
              description:
                'Draw below the 90% committed floor becomes payable at the true-up date.',
            },
          ],
        },
      ],
    },
    {
      account: 'Northwind Robotics',
      governingLaw: 'Delaware',
      paper: [
        {
          key: 'msa',
          type: 'msa',
          status: 'executed',
          label: 'MSA',
          externalReference: 'NWR-MSA-0908',
          // 287 days in, so 78 days left and the 60-day notice deadline opens
          // in 18: the state a deal desk should be acting on now rather than
          // discovering later.
          effectiveInDays: -287,
          expiresInDays: 78,
          isAutoRenew: true,
          noticeDays: 60,
          sla: NORTHWIND_SLA,
          obligations: [
            {
              title: 'Renewal notice',
              kind: 'renewal_notice',
              inDays: 18,
              description:
                'Sixty days notice to stop the master term renewing. The successor MSA is already out for signature.',
            },
            {
              title: 'Annual security review',
              kind: 'review',
              inDays: 132,
              description:
                'Customer right to review the hosting environment; the evidence pack is owed within 30 days of request.',
            },
          ],
        },
        {
          key: 'dpa',
          type: 'dpa',
          status: 'executed',
          label: 'DPA',
          parent: 'msa',
          externalReference: 'NWR-DPA-0908',
          effectiveInDays: -287,
          expiresInDays: 78,
          isAutoRenew: true,
        },
        {
          key: 'order_form',
          type: 'order_form',
          status: 'executed',
          label: 'Order form — B200 evaluation cluster',
          parent: 'msa',
          externalReference: 'NWR-OF-0114',
          effectiveInDays: -30,
          expiresInDays: 240,
          isAutoRenew: false,
          // An evaluation, so no floor and a short exit — the opposite end of
          // the backlog-quality scale from Halcyon, and the reason the two
          // cannot be summed unweighted.
          takeOrPayFloorPct: '0',
          terminationTier: '3_cancellable',
          carriesDealValue: true,
          linksCommitment: true,
          obligations: [
            {
              title: 'Production conversion decision',
              kind: 'milestone',
              inDays: 75,
              description:
                'The evaluation converts to the production rate at this date or the order form lapses.',
            },
          ],
        },
        {
          key: 'renewal_msa',
          type: 'msa',
          status: 'out_for_signature',
          label: 'MSA renewal — successor term',
          externalReference: 'NWR-MSA-0908-R1',
          // Starts the day the current term ends. Out for signature is where
          // most renewals actually sit, and nothing in the demo showed it.
          effectiveInDays: 78,
          expiresInDays: 443,
          isAutoRenew: true,
          noticeDays: 60,
          notes: 'Signature blocks with the customer; commercial terms agreed.',
        },
      ],
    },
    {
      account: 'Verity Health AI',
      governingLaw: 'Germany',
      paper: [
        {
          key: 'msa',
          type: 'msa',
          status: 'executed',
          label: 'MSA',
          externalReference: 'VER-MSA-0512',
          // Signed, well inside its term, and deliberately not up for renewal:
          // if every account showed a renewal signal the facet would be
          // telling nobody anything.
          effectiveInDays: -96,
          expiresInDays: 269,
          isAutoRenew: false,
          // The affiliate that signs is not the account. Assuming otherwise
          // misfiles the counterparty on exactly the deals large enough to
          // matter, which is why the column exists.
          contractingPartyName: `${prefix}Verity Health AI GmbH`,
          obligations: [
            {
              title: 'Data residency attestation',
              kind: 'review',
              inDays: 58,
              description:
                'Written attestation each quarter that no personal data left the EU processing region.',
            },
          ],
        },
        {
          key: 'dpa',
          type: 'dpa',
          status: 'in_review',
          label: 'DPA and sub-processor schedule',
          parent: 'msa',
          // `dpaExecuted: false` on the deal in demand.ts is an assertion, and
          // this is the row behind it. Unsigned paper carries no dates.
          notes: 'With the customer privacy team. EU processing cannot start until this is executed.',
          obligations: [
            {
              title: 'Sub-processor schedule review',
              kind: 'review',
              inDays: 9,
              description:
                'Customer privacy review of the sub-processor list; the committed EU allocation is dated from execution.',
            },
          ],
        },
      ],
    },
    {
      account: 'Aurelian Systems',
      governingLaw: 'Delaware',
      paper: [
        {
          key: 'msa',
          type: 'msa',
          status: 'in_negotiation',
          label: 'MSA',
          // The account sitting in the `legal` stage. No effective date and no
          // expiry, because neither exists until it is signed — and because a
          // dated unsigned contract would fire the expiry alarm on paper
          // nobody has agreed to.
          assignableOnDefault: true,
          assignmentDeadlineBusinessDays: 10,
          notes: 'Third redline exchange. Open points: liability cap, step-in rights, audit frequency.',
          obligations: [
            {
              title: 'Redlines returned to counsel',
              kind: 'milestone',
              inDays: 5,
              description: 'Customer counsel expects our mark-up of the liability and indemnity clauses.',
            },
            {
              title: 'Security questionnaire response',
              kind: 'review',
              inDays: 11,
              description: 'Vendor security assessment; the deal cannot leave legal until it is returned.',
            },
          ],
        },
        {
          key: 'order_form',
          type: 'order_form',
          status: 'draft',
          label: 'Order form — 24-month committed capacity',
          parent: 'msa',
          takeOrPayFloorPct: '85',
          terminationTier: '2_take_or_pay',
          carriesDealValue: true,
          notes: 'Drafted against the current proposal. Not issued until the MSA is executed.',
        },
      ],
    },
    {
      account: 'Tessellate Labs',
      governingLaw: 'England and Wales',
      paper: [
        {
          key: 'msa',
          type: 'msa',
          status: 'draft',
          label: 'MSA',
          // Why the hold on the A100 pool cannot convert: there is no paper.
          notes: 'Standard terms issued; the customer has not returned comments.',
        },
      ],
    },
  ];
}

/**
 * Write the customer paper, and the obligations that make it actionable.
 *
 * Runs as a pass of its own after the demand book, rather than inside its
 * loop, because it is written per account rather than per deal and because
 * `demand.ts` skips an account it has already seeded — which would leave the
 * paper unwritten on a second run against a half-seeded database.
 *
 * Every insert checks for its own row first. A contract is identified by its
 * account and its title, an obligation by its contract and its title, so a
 * repeat run adds nothing even if an earlier one stopped halfway.
 */
export async function seedDemandPaper(context: DemoContext): Promise<void> {
  const { db, prefix, at } = context;
  let contractsPresent = 0;
  let contractsAdded = 0;
  let obligationsPresent = 0;
  let obligationsAdded = 0;

  for (const entry of demandPaperBook(prefix)) {
    const accountName = `${prefix}${entry.account}`;
    const [account] = await db
      .select({ id: accounts.id })
      .from(accounts)
      .where(eq(accounts.name, accountName))
      .limit(1);
    if (!account) {
      console.log(`  skipped paper for ${entry.account} — no such demo account`);
      continue;
    }

    /*
     * The deal is read rather than passed in, and the money is taken from it
     * rather than restated here. An order form is the commercial specifics
     * under a master agreement, so its value is the deal's own term value; a
     * figure typed in twice is a figure that will disagree with itself the
     * first time the demand book is retuned.
     */
    const [deal] = await db
      .select({ id: demandDeals.id, acvCents: demandDeals.acvCents, tcvCents: demandDeals.tcvCents })
      .from(demandDeals)
      .where(eq(demandDeals.accountId, account.id))
      .orderBy(desc(demandDeals.acvCents))
      .limit(1);

    // Which block fulfils the order form. Looked up through the allocation
    // because that is where the demand book records the choice.
    const [allocation] = deal
      ? await db
          .select({ capacityCommitmentId: allocations.capacityCommitmentId })
          .from(allocations)
          .where(eq(allocations.demandDealId, deal.id))
          .limit(1)
      : [];

    const idByKey = new Map<string, string>();
    for (const paper of entry.paper) {
      const title = `${prefix}${paper.label} — ${entry.account}`;
      const [existing] = await db
        .select({ id: contracts.id })
        .from(contracts)
        .where(and(eq(contracts.accountId, account.id), eq(contracts.title, title)))
        .limit(1);

      let contractId = existing?.id;
      if (!contractId) {
        const effectiveAt = paper.effectiveInDays == null ? null : at(paper.effectiveInDays);
        const [inserted] = await db
          .insert(contracts)
          .values({
            accountId: account.id,
            type: paper.type,
            status: paper.status,
            side: 'demand',
            title,
            externalReference: paper.externalReference,
            demandDealId: deal?.id,
            capacityCommitmentId: paper.linksCommitment
              ? (allocation?.capacityCommitmentId ?? undefined)
              : undefined,
            parentContractId: paper.parent ? idByKey.get(paper.parent) : undefined,
            contractingPartyName: paper.contractingPartyName,
            takeOrPayFloorPct: paper.takeOrPayFloorPct,
            prepaidPct: paper.prepaidPct,
            terminationTier: paper.terminationTier,
            assignableOnDefault: paper.assignableOnDefault ?? false,
            assignmentDeadlineBusinessDays: paper.assignmentDeadlineBusinessDays,
            effectiveAt,
            expiresAt: paper.expiresInDays == null ? null : at(paper.expiresInDays),
            // Executed paper was signed the day it took effect, or on the day
            // of the run for anything effective in the future.
            executedAt:
              paper.status === 'executed' && effectiveAt
                ? new Date(Math.min(effectiveAt.getTime(), at(0).getTime()))
                : null,
            isAutoRenew: paper.isAutoRenew ?? false,
            noticeDays: paper.noticeDays,
            valueCents: paper.carriesDealValue ? (deal?.tcvCents ?? deal?.acvCents) : undefined,
            governingLaw: entry.governingLaw,
            notes: paper.notes ? `${paper.notes} ${DEMO_CONTRACT_NOTE}` : DEMO_CONTRACT_NOTE,
          })
          .returning({ id: contracts.id });
        contractId = inserted?.id;
        if (contractId) contractsAdded += 1;
      }
      if (!contractId) continue;
      contractsPresent += 1;
      idByKey.set(paper.key, contractId);

      if (paper.sla) {
        const [existingSla] = await db
          .select({ id: slaTerms.id })
          .from(slaTerms)
          .where(eq(slaTerms.contractId, contractId))
          .limit(1);
        let slaTermId = existingSla?.id;
        if (!slaTermId) {
          const [insertedSla] = await db
            .insert(slaTerms)
            .values({ ...paper.sla.terms, contractId })
            .returning({ id: slaTerms.id });
          slaTermId = insertedSla?.id;
        }
        if (slaTermId) {
          for (const metric of paper.sla.metrics ?? []) {
            const [existingMetric] = await db
              .select({ id: slaMetricTargets.id })
              .from(slaMetricTargets)
              .where(
                and(
                  eq(slaMetricTargets.slaTermId, slaTermId),
                  eq(slaMetricTargets.metric, metric.metric),
                ),
              )
              .limit(1);
            if (!existingMetric) {
              await db.insert(slaMetricTargets).values({ ...metric, slaTermId });
            }
          }
        }
      }

      for (const obligation of paper.obligations ?? []) {
        const obligationTitle = `${prefix}${obligation.title} — ${entry.account}`;
        const [existingObligation] = await db
          .select({ id: contractObligations.id })
          .from(contractObligations)
          .where(
            and(
              eq(contractObligations.contractId, contractId),
              eq(contractObligations.title, obligationTitle),
            ),
          )
          .limit(1);
        obligationsPresent += 1;
        if (existingObligation) continue;
        await db.insert(contractObligations).values({
          contractId,
          title: obligationTitle,
          kind: obligation.kind,
          dueAt: at(obligation.inDays),
          description: obligation.description,
        });
        obligationsAdded += 1;
      }
    }
  }

  console.log(
    `  ${contractsPresent} demand-side contracts (${contractsAdded} new) and ` +
      `${obligationsPresent} obligations (${obligationsAdded} new) — ` +
      'one renewal notice already missed, one opening within the month',
  );
}
