/**
 * Seed the database.
 *
 * Idempotent: running it twice does not duplicate anything. Safe against a
 * database that already has real data, because every insert is keyed and
 * conflicts are ignored rather than overwritten.
 *
 * What gets seeded, and why:
 *
 *   • **Prime Intellect as an account, with its people.** Public research,
 *     every record confidence-graded and cited. See `people.ts`.
 *   • **A handful of named neoclouds** as supply-side accounts, so the supply
 *     pipeline is not an empty screen on first run.
 *   • **A worked example** of the thing PIG exists for: one capacity
 *     commitment, two allocations against it, and therefore a real margin
 *     number and a real idle-capacity alert on the dashboard.
 *   • **The Motion starter library.** Authored content shipped with the
 *     product rather than invented data, which is why it lives here and not in
 *     `demo/`. See `motion/index.ts`.
 *
 * The example is clearly labelled, and the company buying is invented. Real
 * named companies appear here only with a source; commercial terms attached to
 * one would be a fabricated record about someone else's business. The same rule
 * `demo.ts` states, obeyed here too.
 */
import { and, eq, ne } from 'drizzle-orm';
import { createDatabase } from '../client';
import {
  accounts,
  allocations,
  capacityCommitments,
  contacts,
  demandDeals,
  teamMemberships,
  users,
} from '../schema/index';
import { seedMotionLibrary } from './motion/index';
import { PRIME_INTELLECT_PEOPLE, PUBLIC_CUSTOMER_REFERENCES, UNRESOLVED_NAMES } from './people';

const db = createDatabase();

async function seed() {
  console.log('Seeding PIG…\n');

  // ------------------------------------------------------- Prime Intellect
  const [prime] = await db
    .insert(accounts)
    .values({
      name: 'Prime Intellect',
      domain: 'primeintellect.ai',
      website: 'https://www.primeintellect.ai',
      description:
        'Aggregates GPU capacity across many providers and sells compute, RL ' +
        'post-training, inference and evaluations. The company PIG was designed for.',
      side: 'both',
      customerSegment: 'frontier_lab',
      country: 'United States',
      region: 'San Francisco',
      source: 'seed',
      sourceUrl: 'https://www.primeintellect.ai/',
      confidence: 'confirmed',
    })
    .onConflictDoNothing({ target: accounts.domain })
    .returning();

  const primeId =
    prime?.id ??
    (
      await db
        .select({ id: accounts.id })
        .from(accounts)
        .where(eq(accounts.domain, 'primeintellect.ai'))
        .limit(1)
    )[0]?.id;

  if (!primeId) throw new Error('Could not resolve the Prime Intellect account.');

  /*
   * Existence checks rather than ON CONFLICT.
   *
   * `onConflictDoNothing()` with no target is a no-op unless a unique
   * constraint is actually violated, and there is deliberately no unique index
   * on (account, name) — two people at one company really can share a name.
   * So idempotency is enforced here, in the seed, rather than by bending the
   * schema to suit it.
   */
  const existingContactNames = new Set(
    (
      await db
        .select({ fullName: contacts.fullName })
        .from(contacts)
        .where(eq(contacts.accountId, primeId))
    ).map((row) => row.fullName),
  );

  let peopleAdded = 0;
  for (const person of PRIME_INTELLECT_PEOPLE) {
    if (existingContactNames.has(person.fullName)) continue;
    const [created] = await db
      .insert(contacts)
      .values({
        accountId: primeId,
        fullName: person.fullName,
        title: person.title,
        affiliation: person.affiliation,
        confidence: person.confidence,
        confidenceNote: person.confidenceNote,
        sourceUrl: person.sourceUrl,
        githubHandle: person.githubHandle,
        twitterHandle: person.twitterHandle,
        linkedinUrl: person.linkedinUrl,
        websiteUrl: person.websiteUrl,
        isDecisionMaker: person.isDecisionMaker ?? false,
        // Recorded as a real date so "who has left?" is answerable without
        // parsing prose out of a note field.
        departedAt: person.departed ? new Date('2026-01-01') : null,
        source: 'seed',
        // Deliberately no email. None are published, and inferring one from a
        // name and a domain is unreliable and discourteous.
        email: null,
      })
      .onConflictDoNothing()
      .returning();
    if (created) peopleAdded += 1;
  }
  console.log(`  Prime Intellect: ${peopleAdded} contact(s) seeded, all graded and cited.`);

  // --------------------------------------------------- customer references
  for (const reference of PUBLIC_CUSTOMER_REFERENCES) {
    /*
     * An existence check, not `onConflictDoNothing()`.
     *
     * These accounts have no domain, and the only unique index on `accounts`
     * is on the domain — so there was nothing to conflict on and the clause
     * was a no-op, exactly as the README warns. Every run added another Ramp
     * and another Zapier. Nobody noticed because the CI idempotency gate
     * counts `contacts`, and the contact insert below already had its own
     * existence check.
     */
    const [alreadyPresent] = await db
      .select({ id: accounts.id })
      .from(accounts)
      .where(eq(accounts.name, reference.account))
      .limit(1);
    if (alreadyPresent) continue;

    const [account] = await db
      .insert(accounts)
      .values({
        name: reference.account,
        side: 'demand',
        customerSegment: 'enterprise',
        description: `Named publicly as a Prime Intellect customer reference.`,
        source: 'seed',
        sourceUrl: reference.sourceUrl,
        confidence: 'confirmed',
      })
      .onConflictDoNothing()
      .returning();

    if (account) {
      const [existingRef] = await db
        .select({ id: contacts.id })
        .from(contacts)
        .where(eq(contacts.fullName, reference.person))
        .limit(1);
      if (existingRef) continue;

      await db
        .insert(contacts)
        .values({
          accountId: account.id,
          fullName: reference.person,
          title: reference.title,
          // Named as a reference in marketing material, which is evidence of a
          // relationship — not evidence of employment at the seller.
          affiliation: 'customer_reference',
          confidence: 'confirmed',
          sourceUrl: reference.sourceUrl,
          source: 'seed',
          email: null,
        })
        .onConflictDoNothing();
    }
  }
  console.log(`  ${PUBLIC_CUSTOMER_REFERENCES.length} public customer reference(s) seeded.`);

  // --------------------------------------------------------- supply accounts
  const SUPPLIERS = [
    { name: 'CoreWeave', domain: 'coreweave.com', type: 'neocloud' as const },
    { name: 'Nebius', domain: 'nebius.com', type: 'neocloud' as const },
    { name: 'Crusoe', domain: 'crusoe.ai', type: 'neocloud' as const },
    { name: 'Lambda', domain: 'lambda.ai', type: 'neocloud' as const },
    { name: 'Together AI', domain: 'together.ai', type: 'neocloud' as const },
    { name: 'Voltage Park', domain: 'voltagepark.com', type: 'neocloud' as const },
    { name: 'RunPod', domain: 'runpod.io', type: 'neocloud' as const },
    { name: 'Datacrunch', domain: 'datacrunch.io', type: 'neocloud' as const },
  ];

  let suppliersAdded = 0;
  for (const supplier of SUPPLIERS) {
    const [created] = await db
      .insert(accounts)
      .values({
        name: supplier.name,
        domain: supplier.domain,
        side: 'supply',
        supplierType: supplier.type,
        description: 'GPU cloud provider. Publicly documented; commercial terms are not.',
        source: 'seed',
        confidence: 'confirmed',
      })
      .onConflictDoNothing({ target: accounts.domain })
      .returning();
    if (created) suppliersAdded += 1;
  }
  console.log(`  ${suppliersAdded} supply-side account(s) seeded.`);

  // ------------------------------------------------------- a worked example
  //
  // Illustrative only, and labelled as such. It exists so the dashboard has a
  // real margin figure and a real idle-capacity alert on first run, rather
  // than empty states that make the product look like it does nothing.
  //
  // The supplier is real and its block is illustrative; the buyer is invented
  // outright. Naming a real company as the counterparty to an invented ACV with
  // MSA and DPA flagged executed is a fabricated commercial record about that
  // company, which no amount of an `EXAMPLE — ` prefix on the neighbouring rows
  // makes acceptable.
  const [supplier] = await db
    .select({ id: accounts.id })
    .from(accounts)
    .where(eq(accounts.domain, 'coreweave.com'))
    .limit(1);

  const EXAMPLE_COMMITMENT = 'EXAMPLE — 256× H100 reserved, 6 months';
  const [existingExample] = await db
    .select({ id: capacityCommitments.id })
    .from(capacityCommitments)
    .where(eq(capacityCommitments.name, EXAMPLE_COMMITMENT))
    .limit(1);

  // Before anything is created: repair the databases that already carry the
  // example booked against a real company.
  await rehomeExampleDeal();

  if (supplier && !existingExample) {
    const start = new Date();
    const end = new Date(start.getTime() + 180 * 86_400_000);

    const [commitment] = await db
      .insert(capacityCommitments)
      .values({
        accountId: supplier.id,
        name: EXAMPLE_COMMITMENT,
        gpuType: 'H100_80GB',
        socket: 'SXM5',
        gpuCount: 256,
        interconnectType: 'Infiniband',
        securityTier: 'secure_cloud',
        startsAt: start,
        endsAt: end,
        // 256 GPUs × 24h × 180d, at 92% of wall-clock to allow for maintenance.
        totalGpuHours: String(Math.round(256 * 24 * 180 * 0.92)),
        // Bulk reserved pricing sits well below market on-demand — that spread
        // is the business. Figures are plausible rather than sourced.
        costPerGpuHourCents: 160,
        takeOrPayFloorPct: '100',
        prepaidPct: '20',
        usefulLifeYears: '5',
        notes:
          'Illustrative seed data, not a real contract. Delete once you have entered ' +
          'your own commitments.',
      })
      .onConflictDoNothing()
      .returning();

    if (commitment) {
      const customerId = await ensureExampleCustomer();

      if (customerId) {
        const [deal] = await db
          .insert(demandDeals)
          .values({
            accountId: customerId,
            name: EXAMPLE_DEAL,
            productLine: 'compute_reserved',
            stage: 'deployment',
            acvCents: 340_000_00,
            termMonths: 6,
            msaExecuted: true,
            dpaExecuted: true,
          })
          .onConflictDoNothing()
          .returning();

        if (deal) {
          /*
           * Sold: 70% of the block at $2.45/GPU-hr against $1.60 cost.
           *
           * These proportions are chosen to demonstrate the point of the
           * product rather than to flatter it. Because cost is charged against
           * the FULL commitment, a 53% markup only clears break-even once
           * roughly 65% of the block is sold — so this example lands at about
           * +10% margin while still leaving 20% idle, and both the healthy
           * margin and the idle-capacity alert are visible at once.
           *
           * Drop the sold share to 55% and the same block goes underwater.
           * That sensitivity is the whole argument for tracking this.
           */
          await db.insert(allocations).values({
            capacityCommitmentId: commitment.id,
            demandDealId: deal.id,
            gpuHours: String(Math.round(Number(commitment.totalGpuHours) * 0.70)),
            pricePerGpuHourCents: 245,
            startsAt: start,
            endsAt: end,
            status: 'committed',
            guaranteeType: 'guaranteed',
            priority: 10,
          });

          // Internal research burn: real cost, no revenue. Leaving this out is
          // exactly how a book looks healthier than it is.
          await db.insert(allocations).values({
            capacityCommitmentId: commitment.id,
            internalTeam: 'research',
            gpuHours: String(Math.round(Number(commitment.totalGpuHours) * 0.08)),
            pricePerGpuHourCents: 0,
            startsAt: start,
            endsAt: end,
            status: 'active',
            guaranteeType: 'internal',
            priority: 200,
            notes: 'Internal research consumption — costs real money, earns none.',
          });

          console.log(
            `  Worked example seeded against ${EXAMPLE_CUSTOMER} (fictional): 1 commitment, ` +
              '2 allocations (one of them internal research burn), ~+10% margin with 20% ' +
              'still idle.',
          );
        }
      }
    }
  } else if (existingExample) {
    console.log('  Worked example already present — skipped.');
  }

  // ------------------------------------------------ the Motion starter library
  const motion = await seedMotionLibrary(db);
  console.log(
    `  ${motion.total} Motion starter template(s) (${motion.added} new, ${motion.refreshed} ` +
      'refreshed) — authored content, shared and system-owned, version 1 of their lineages.',
  );
  if (motion.held.length) {
    console.log(
      `  ${motion.held.length} starter template(s) have newer wording here and were LEFT ALONE, ` +
        `because they are in use or have been adopted: ${motion.held.join(', ')}. ` +
        'Publish a new version rather than editing one an engagement was cut from.',
    );
  }
  for (const bad of motion.rejected) {
    console.error(`  SKIPPED a starter template with a value outside the ontology: ${bad}`);
  }

  // ---------------------------------------------------------- the dev user
  //
  // Only when the table is empty. With authentication disabled in development
  // the API adopts the first user it finds, so creating one unconditionally
  // could hand a local session to the wrong identity.
  //
  // Skipped entirely in production: a platform-admin row with no auth subject
  // is unreachable (nobody can sign in as it), but leaving an admin-flagged
  // placeholder in a real deployment is untidy at best and a trap at worst.
  const existing = await db.select({ id: users.id }).from(users).limit(1);
  if (existing.length === 0 && process.env.NODE_ENV !== 'production') {
    const [devUser] = await db
      .insert(users)
      .values({
        email: 'dev@localhost',
        name: 'Development User',
        handle: 'dev',
        title: 'Local development',
        isPlatformAdmin: true,
      })
      .returning();

    if (devUser) {
      for (const team of ['supply', 'demand', 'research'] as const) {
        await db
          .insert(teamMemberships)
          .values({ userId: devUser.id, team, role: 'admin', isPrimary: team === 'demand' })
          .onConflictDoNothing();
      }
      console.log('  Development user created (dev@localhost), on all three teams.');
    }
  } else if (existing.length === 0) {
    console.log(
      '  Skipped the development user (NODE_ENV=production). Sign in and create a ' +
        'profile; an address in PIG_ADMIN_EMAILS bootstraps the first admin.',
    );
  }

  console.log('\nUnresolved names, recorded rather than invented:');
  for (const unresolved of UNRESOLVED_NAMES) {
    console.log(`  ${unresolved.name}: ${unresolved.note.split('.')[0]}.`);
  }

  console.log('\nDone. No email addresses were seeded or inferred.');
}

// -------------------------------------------------- the example's counterparty

const EXAMPLE_CUSTOMER = 'EXAMPLE — Fenwick Labs';
const EXAMPLE_DEAL = 'EXAMPLE — post-training cluster';

/**
 * The invented company the worked example is sold to.
 *
 * An existence check rather than `onConflictDoNothing()`, for the same reason
 * as the customer references above: this account deliberately has no domain —
 * it is not a real company and must never be mistaken for one — and `domain`
 * is the only unique index on `accounts`, so a conflict clause would have
 * nothing to fire on and every run would add another Fenwick Labs.
 */
async function ensureExampleCustomer(): Promise<string | undefined> {
  const [existing] = await db
    .select({ id: accounts.id })
    .from(accounts)
    .where(eq(accounts.name, EXAMPLE_CUSTOMER))
    .limit(1);
  if (existing) return existing.id;

  const [created] = await db
    .insert(accounts)
    .values({
      name: EXAMPLE_CUSTOMER,
      side: 'demand',
      customerSegment: 'applied_ai_startup',
      country: 'United States',
      description:
        'Fictional company, invented so the worked example has a buyer. Not a ' +
        'customer, not a real business, and safe to delete along with the example.',
      source: 'seed',
      confidence: 'confirmed',
    })
    .returning();

  return created?.id;
}

/**
 * Move the example deal off whatever account an older seed attached it to.
 *
 * This example used to be booked against Ramp — a real company, seeded from a
 * public reference on primeintellect.ai — complete with an invented ACV and
 * both MSA and DPA flagged executed. Fixing the insert is not enough on its
 * own: the worked-example section is skipped entirely whenever the commitment
 * already exists, so a database seeded before the fix would keep it, and
 * `pnpm db:demo -- --clear` never touched it because that only matches the
 * `DEMO — ` prefix.
 *
 * Moved rather than deleted. The allocations hang off the deal, and they are
 * what give the dashboard its margin figure and its idle-capacity alert; the
 * problem was only ever which account the row pointed at.
 */
async function rehomeExampleDeal(): Promise<void> {
  const [present] = await db
    .select({ id: demandDeals.id })
    .from(demandDeals)
    .where(eq(demandDeals.name, EXAMPLE_DEAL))
    .limit(1);
  if (!present) return;

  const customerId = await ensureExampleCustomer();
  if (!customerId) return;

  const moved = await db
    .update(demandDeals)
    .set({ accountId: customerId })
    .where(and(eq(demandDeals.name, EXAMPLE_DEAL), ne(demandDeals.accountId, customerId)))
    .returning({ id: demandDeals.id });

  if (moved.length > 0) {
    console.log(`  Worked example moved off a real company and onto ${EXAMPLE_CUSTOMER}.`);
  }
}

seed()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error('Seed failed:', error);
    process.exit(1);
  });
