/**
 * `--clear`: take the invented book back out again.
 *
 * Everything the demo seed writes is either prefixed `DEMO — ` or hangs off a
 * row that is, so every delete here is scoped to one of those two things. A
 * teardown that runs against a database with real records in it is exactly the
 * situation this command is for — someone demoing on top of their own data —
 * and it must leave that data untouched.
 */
import { eq, inArray, like } from 'drizzle-orm';
import { unstampDemoActivity } from './activities';
import {
  accounts,
  activities,
  allocations,
  calendarEntries,
  capacityCommitments,
  capacityRequests,
  complianceArtifacts,
  contacts,
  contractObligations,
  contracts,
  demandDeals,
  exportAuthorizations,
  learnResources,
  slaTerms,
  teamMemberships,
  users,
  supplyDeals,
} from '../../schema/index';
import type { DemoContext } from './index';

export async function clear(context: DemoContext): Promise<void> {
  const { db, prefix } = context;

  console.log('Removing demo data…');
  // Ordered so foreign keys never block a delete.
  const demoAccounts = await db
    .select({ id: accounts.id })
    .from(accounts)
    .where(like(accounts.name, `${prefix}%`));
  const ids = demoAccounts.map((a) => a.id);

  /*
   * The children of the demo paper and the demo deals, named explicitly.
   *
   * These three deletes — obligations, SLA terms, capacity requests — used to
   * run with NO WHERE CLAUSE. `pnpm db:demo -- --clear` is a documented
   * command, so anyone who ran the demo book on top of their own data lost
   * every renewal deadline, every negotiated SLA term and every customer
   * capacity request in the database along with it: the three tables whose
   * rows are hardest to reconstruct, because they record what was negotiated
   * rather than what can be re-imported. Scoping them by parent id keeps the
   * blast radius inside the demo book and keeps the delete order valid, since
   * each of the three is a child of a row deleted a few lines further down.
   */
  const demoContracts = await db
    .select({ id: contracts.id })
    .from(contracts)
    .where(like(contracts.title, `${prefix}%`));
  const contractIds = demoContracts.map((c) => c.id);

  const demoDemandDeals = await db
    .select({ id: demandDeals.id })
    .from(demandDeals)
    .where(like(demandDeals.name, `${prefix}%`));
  const demandDealIds = demoDemandDeals.map((d) => d.id);

  await db.delete(calendarEntries).where(like(calendarEntries.title, `${prefix}%`));
  await db.delete(learnResources).where(like(learnResources.title, `${prefix}%`));
  await db.delete(allocations).where(like(allocations.notes, `${prefix}%`));
  // Guarded rather than relying on `inArray` with an empty list, which is a
  // condition different drivers have historically disagreed about.
  if (contractIds.length > 0) {
    await db.delete(contractObligations).where(inArray(contractObligations.contractId, contractIds));
    await db.delete(slaTerms).where(inArray(slaTerms.contractId, contractIds));
  }
  await db.delete(contracts).where(like(contracts.title, `${prefix}%`));
  if (demandDealIds.length > 0) {
    await db.delete(capacityRequests).where(inArray(capacityRequests.demandDealId, demandDealIds));
  }
  await db.delete(demandDeals).where(like(demandDeals.name, `${prefix}%`));
  await db.delete(supplyDeals).where(like(supplyDeals.name, `${prefix}%`));
  await db.delete(capacityCommitments).where(like(capacityCommitments.name, `${prefix}%`));
  // Before the rows go: the supplier accounts are real and survive `--clear`,
  // but their lastActivityAt is computed from demo correspondence. Recompute it
  // from whatever non-demo activity remains first — once the activities are
  // deleted there is no way to tell which accounts the demo book had touched.
  await unstampDemoActivity(context);

  await db.delete(activities).where(like(activities.subject, `${prefix}%`));
  for (const id of ids) {
    // Compliance rows cascade on the account anyway; deleted explicitly so the
    // order of removal stays readable rather than relying on the constraint.
    await db.delete(exportAuthorizations).where(eq(exportAuthorizations.accountId, id));
    await db.delete(complianceArtifacts).where(eq(complianceArtifacts.accountId, id));
    await db.delete(contacts).where(eq(contacts.accountId, id));
  }
  await db.delete(accounts).where(like(accounts.name, `${prefix}%`));

  // The invented sellers the demand book creates to own its records. Their
  // foreign keys are `on delete set null`, so leaving them behind corrupts
  // nothing — but a teardown that leaves four fictional colleagues in the team
  // list has not finished the job.
  const demoSellers = await db.select({ id: users.id }).from(users).where(like(users.name, `${prefix}%`));
  if (demoSellers.length > 0) {
    const sellerIds = demoSellers.map((seller) => seller.id);
    await db.delete(teamMemberships).where(inArray(teamMemberships.userId, sellerIds));
    await db.delete(users).where(inArray(users.id, sellerIds));
  }

  console.log(`Removed ${ids.length} demo account(s) and everything hanging from them.`);
}
