import { sql } from 'drizzle-orm'
import { Miniflare } from 'miniflare'
import { describe, expect, it } from 'vitest'
import { createD1Client } from '@platform-modules/db/sqlite/d1'
import { startPg } from './pg-harness.js'
import { canonicalContentMigrationHash, contentModelForwardMigrationSql } from './migrations/content-model.js'
import { contentSchema } from './schema.js'
import {
  applyContentCodeReconciliation,
  applyContentStatusChange,
  applyContentTypeChange,
  contentBackupDestinationHash,
  previewContentCodeReconciliation,
  previewContentStatusChange,
  previewContentTypeChange,
  restoreContentBackup,
  type ContentBackupManifest,
  type ContentBackupPayload,
  type ContentBackupStore,
  type ContentDefinitionAuthorization,
  type ContentDefinitionEffects,
  type ContentDefinitionEvent,
  type ContentPrincipal,
} from './definition-lifecycle.js'
import {
  ContentDefinitionError,
  createContentType,
  defineContentType,
  resolveContentType,
  type ContentDefinitionExecutor,
  type ContentTypeCapabilities,
  type ContentTypeDefinition,
  type ContentTypeLabels,
} from './registry.js'
import {
  createContentStatus,
  defineContentStatus,
  resolveContentStatus,
  type ContentStatusDefinition,
} from './status.js'

interface TestD1Statement {
  bind(...values: unknown[]): TestD1Statement
  run(): Promise<unknown>
  all(): Promise<{ results: unknown[] }>
}
interface TestD1Binding {
  prepare(query: string): TestD1Statement
  exec(query: string): Promise<unknown>
}
function execD1(binding: TestD1Binding, statement: string): Promise<unknown> {
  return binding.exec(statement.replace(/\s+/g, ' ').trim())
}

const LEGACY_D1_DDL = `CREATE TABLE content_entries (
  id TEXT PRIMARY KEY, slug TEXT NOT NULL, type TEXT NOT NULL, title TEXT NOT NULL,
  body TEXT NOT NULL, status TEXT NOT NULL, visibility TEXT NOT NULL,
  published_at TEXT, author TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
)`
const LEGACY_PG_DDL = `CREATE TABLE content_entries (
  id uuid PRIMARY KEY, slug text NOT NULL, type text NOT NULL, title text NOT NULL,
  body text NOT NULL, status text NOT NULL, visibility text NOT NULL,
  published_at timestamptz(3), author text NOT NULL,
  created_at timestamptz(3) NOT NULL, updated_at timestamptz(3) NOT NULL
)`

interface Fixture {
  name: 'd1' | 'postgres'
  db: ContentDefinitionExecutor
  close(): Promise<void>
}

async function d1Fixture(): Promise<Fixture> {
  const mf = new Miniflare({
    modules: true,
    script: `export default { fetch() { return new Response('ok') } }`,
    d1Databases: { DB: '00000000-0000-4000-8000-000000000031' },
  })
  const binding = await mf.getD1Database('DB') as unknown as TestD1Binding
  await execD1(binding, LEGACY_D1_DDL)
  for (const statement of contentModelForwardMigrationSql('d1')) await execD1(binding, statement)
  const db = createD1Client(binding, { schema: contentSchema })
  return { name: 'd1', db, close: () => mf.dispose() }
}

async function pgFixture(): Promise<Fixture> {
  const pg = await startPg()
  await pg.db.execute(sql.raw(LEGACY_PG_DDL))
  for (const statement of contentModelForwardMigrationSql('postgres')) await pg.db.execute(sql.raw(statement))
  const db: ContentDefinitionExecutor = {
    execute: <Row extends Record<string, unknown> = Record<string, unknown>>(query: import('drizzle-orm').SQLWrapper) =>
      pg.db.transaction((tx) => tx.execute<Row>(query)),
  }
  return { name: 'postgres', db, close: () => pg.stop() }
}

function labels(name = 'Book'): ContentTypeLabels {
  return {
    name: `${name}s`, singularName: name, menuName: `${name}s`, nameAdminBar: name,
    addNew: 'Add New', addNewItem: `Add New ${name}`, editItem: `Edit ${name}`, newItem: `New ${name}`,
    viewItem: `View ${name}`, viewItems: `View ${name}s`, searchItems: `Search ${name}s`, notFound: 'Not found',
    notFoundInTrash: 'Not found in trash', parentItemColon: `Parent ${name}:`, allItems: `All ${name}s`,
    archives: `${name} Archives`, attributes: `${name} Attributes`, insertIntoItem: `Insert into item`,
    uploadedToThisItem: `Uploaded to item`, featuredImage: 'Featured image', setFeaturedImage: 'Set featured image',
    removeFeaturedImage: 'Remove featured image', useFeaturedImage: 'Use featured image', filterItemsList: 'Filter list',
    filterByDate: 'Filter by date', itemsListNavigation: 'List navigation', itemsList: 'Items list', itemPublished: 'Published',
    itemPublishedPrivately: 'Published privately', itemRevertedToDraft: 'Reverted', itemScheduled: 'Scheduled',
    itemUpdated: 'Updated', itemLink: 'Item link', itemLinkDescription: 'Item link description',
  }
}
function capabilities(): ContentTypeCapabilities {
  return {
    manageType: 'manage_type', migrateAll: 'migrate_all', manageTerms: 'manage_terms', create: 'create', read: 'read',
    readPrivate: 'read_private', readProtected: 'read_protected', editOwn: 'edit_own', editOthers: 'edit_others',
    editPrivate: 'edit_private', editPublished: 'edit_published', publish: 'publish', deleteOwn: 'delete_own',
    deleteOthers: 'delete_others', deletePrivate: 'delete_private', deletePublished: 'delete_published',
  }
}
function typeDefinition(key = 'book', overrides: Partial<ContentTypeDefinition> = {}): ContentTypeDefinition {
  return {
    key, labels: labels(key === 'book' ? 'Book' : 'Shadow'), public: false, hierarchical: true,
    excludeFromSearch: true, publiclyQueryable: false, showUi: true, showInMenu: true, showInNavMenus: false,
    showInAdminBar: true, capabilities: capabilities(), supports: ['title', 'editor', 'excerpt', 'pageAttributes'],
    taxonomies: [], hasArchive: false, rewrite: false, queryVariable: false, canExport: true, deleteWithAuthor: false,
    rest: false, defaultTemplateKey: 'generic', statusKeys: ['draft', 'review'], active: true, ...overrides,
  }
}
function statusDefinition(key: string, overrides: Partial<ContentStatusDefinition> = {}): ContentStatusDefinition {
  return {
    key, label: key === 'draft' ? 'Draft' : 'Review', published: false, internal: false, excludeFromSearch: true,
    publiclyQueryable: false, showInAdminAll: true, showInAdminStatusFilter: true,
    dateLabel: 'lastModified', transitionInput: 'none', ...overrides,
  }
}

const principal: ContentPrincipal = Object.freeze({ id: 'admin-1', tenantId: 'tenant-a', capabilities: new Set<string>() })
function authorization(policyVersion = 'policy-v1'): ContentDefinitionAuthorization {
  return { assert: async () => ({ policyVersion }) }
}
function effects(): ContentDefinitionEffects & { events: ContentDefinitionEvent[] } {
  const events: ContentDefinitionEvent[] = []
  return {
    events,
    audit: { record: async (_tx, event) => { events.push(event) } },
    outbox: { enqueue: async () => {} },
  }
}

async function seedDefinitions(fixture: Fixture, fx: ContentDefinitionEffects): Promise<void> {
  await createContentStatus(fixture.db, principal, { definition: statusDefinition('draft'), operationId: `${fixture.name}:create:draft` }, authorization(), fx)
  await createContentStatus(fixture.db, principal, { definition: statusDefinition('review'), operationId: `${fixture.name}:create:review` }, authorization(), fx)
  await createContentType(fixture.db, principal, { definition: typeDefinition(), operationId: `${fixture.name}:create:book` }, authorization(), fx)
}

async function seedEntries(db: ContentDefinitionExecutor): Promise<void> {
  const now = '2026-08-21T12:00:00.000Z'
  await db.execute(sql`INSERT INTO content_entries
    (id, slug, type, title, body, status, visibility, published_at, author, created_at, updated_at,
     parent_id, menu_order, template_key, excerpt, last_edited_by, type_definition_revision, status_definition_revision)
    VALUES
    (${'11111111-1111-4111-8111-111111111111'}, 'root-book', 'book', 'Root', '', 'review', 'public', NULL, 'author-a', ${now}, ${now}, NULL, 0, NULL, '', 'author-a', 1, 1),
    (${'22222222-2222-4222-8222-222222222222'}, 'child-book', 'book', 'Child', '', 'review', 'public', NULL, 'author-a', ${now}, ${now}, ${'11111111-1111-4111-8111-111111111111'}, 2, NULL, 'purge me', 'author-a', 1, 1)`)
}

function matchingBackups(): ContentBackupStore & { verified: number } {
  const store = {
    verified: 0,
    async verify(_tx: ContentDefinitionExecutor, backupId: string, scopeHash: string, corpusVersion: string, corpusHash: string, expectedCounts: Readonly<Record<string, number>>) {
      store.verified += 1
      return { id: backupId, scopeHash, corpusVersion, corpusHash, itemCounts: expectedCounts, byteCount: 128, immutable: true as const }
    },
    async read() { throw new Error('not used in purge test') },
  }
  return store
}

async function exercise(fixture: Fixture) {
  const fx = effects()
  await seedDefinitions(fixture, fx)

  await createContentType(fixture.db, principal, { definition: typeDefinition('shadow'), operationId: `${fixture.name}:create:shadow` }, authorization(), fx)
  const codeShadow = defineContentType(typeDefinition('shadow', { labels: labels('Code Shadow') }))
  const resolvedShadow = await resolveContentType(fixture.db, 'shadow', { codeTypes: [codeShadow] })
  expect(resolvedShadow.origin).toBe('code')
  expect(resolvedShadow.shadowedDbVersion).toBe(1)
  expect(resolvedShadow.labels.name).toBe('Code Shadows')

  await createContentStatus(fixture.db, principal, { definition: statusDefinition('custom'), operationId: `${fixture.name}:create:custom` }, authorization(), fx)
  const codeCustom = defineContentStatus(statusDefinition('custom', { label: 'Code Custom' }))
  const resolvedCustom = await resolveContentStatus(fixture.db, 'custom', { codeStatuses: [codeCustom] })
  expect(resolvedCustom.origin).toBe('code')
  expect(resolvedCustom.shadowedDbVersion).toBe(1)
  expect(resolvedCustom.label).toBe('Code Custom')

  await seedEntries(fixture.db)
  const strategies = [
    { kind: 'mapStatus', mappings: { review: 'draft' } },
    { kind: 'flattenHierarchy' },
    { kind: 'purgeFeatureData', features: ['excerpt'], backupId: 'backup-excerpt-1' },
  ] as const
  const impact = await previewContentTypeChange(fixture.db, principal, {
    key: 'book', expectedVersion: 1,
    patch: { hierarchical: false, statusKeys: ['draft'], supports: ['title', 'editor', 'pageAttributes'] },
    strategies,
  }, authorization())
  expect(impact.affectedCounts).toMatchObject({ entries: 2, 'status:review': 2, hierarchy: 1, 'feature:excerpt': 1 })
  expect(impact.incompatibilities).toEqual(expect.arrayContaining(['status:review', 'hierarchy', 'feature:excerpt']))

  const backups = matchingBackups()
  const changed = await applyContentTypeChange(fixture.db, principal, {
    impactToken: impact.token, operationId: `${fixture.name}:change:book`, strategies,
  }, authorization(), fx, { destructive: { backups } })
  expect(changed).toMatchObject({ key: 'book', version: 2, revision: 2, hierarchical: false, statusKeys: ['draft'] })
  expect(backups.verified).toBe(1)

  const rows = await fixture.db.execute<{ id: string; status: string; parentId: string | null; excerpt: string }>(sql`SELECT id, status, parent_id AS "parentId", excerpt FROM content_entries WHERE type = 'book' ORDER BY id`)
  expect(rows).toEqual([
    expect.objectContaining({ id: '11111111-1111-4111-8111-111111111111', status: 'draft', parentId: null, excerpt: '' }),
    expect.objectContaining({ id: '22222222-2222-4222-8222-222222222222', status: 'draft', parentId: null, excerpt: '' }),
  ])

  const replay = await applyContentTypeChange(fixture.db, principal, {
    impactToken: impact.token, operationId: `${fixture.name}:change:book`, strategies,
  }, authorization(), fx, { destructive: { backups } })
  expect(replay).toEqual(changed)
  expect(backups.verified).toBe(1)

  const driftImpact = await previewContentTypeChange(fixture.db, principal, {
    key: 'book', expectedVersion: 2, patch: { description: 'description after preview' }, strategies: [{ kind: 'reject' }],
  }, authorization())
  await fixture.db.execute(sql`UPDATE content_entries SET updated_at = ${'2026-08-21T12:01:00.000Z'} WHERE id = ${'22222222-2222-4222-8222-222222222222'}`)
  await expect(applyContentTypeChange(fixture.db, principal, {
    impactToken: driftImpact.token, operationId: `${fixture.name}:drifted`, strategies: [{ kind: 'reject' }],
  }, authorization(), fx)).rejects.toMatchObject({ code: 'stale-impact' })

  const statusImpact = await previewContentStatusChange(fixture.db, principal, {
    key: 'review', expectedVersion: 1, delete: true, strategy: { kind: 'reject' },
  }, authorization())
  expect(statusImpact.affectedCounts.entries).toBe(0)
  const deleted = await applyContentStatusChange(fixture.db, principal, {
    impactToken: statusImpact.token, operationId: `${fixture.name}:delete:review`, strategy: { kind: 'reject' },
  }, authorization(), fx)
  expect(deleted).toBeNull()
  await expect(resolveContentStatus(fixture.db, 'review')).rejects.toMatchObject({ code: 'definition-not-found' })

  const versionRows = await fixture.db.execute<{ definitionKind: string; definitionKey: string; revision: number | string }>(sql`SELECT definition_kind AS "definitionKind", definition_key AS "definitionKey", revision FROM content_definition_versions WHERE definition_key IN ('book','review') ORDER BY definition_kind, definition_key, revision`)
  const normalizedVersions = versionRows.map((row) => ({ ...row, revision: Number(row.revision) }))
  expect(normalizedVersions).toEqual(expect.arrayContaining([
    { definitionKind: 'type', definitionKey: 'book', revision: 1 },
    { definitionKind: 'type', definitionKey: 'book', revision: 2 },
    { definitionKind: 'status', definitionKey: 'review', revision: 1 },
    { definitionKind: 'status', definitionKey: 'review', revision: 2 },
  ]))
  expect(fx.events.filter((event) => event.kind === 'dbDefinitionChanged')).toHaveLength(2)

  return {
    rows: rows.map((row) => ({ ...row, parentId: row.parentId ?? null })),
    book: changed && { key: changed.key, version: changed.version, revision: changed.revision, hash: changed.canonicalHash },
    versions: normalizedVersions,
  }
}


async function exerciseCodeReconciliation(fixture: Fixture) {
  const fx = effects()
  await seedDefinitions(fixture, fx)
  await seedEntries(fixture.db)
  const nextBook = defineContentType(typeDefinition('book', {
    labels: labels('Code Book'),
    hierarchical: false,
    statusKeys: ['draft'],
    supports: ['title', 'editor', 'pageAttributes'],
  }))
  const nextDraft = defineContentStatus(statusDefinition('draft', { label: 'Code Draft' }))
  const next = Object.freeze({
    version: 'deploy-2026-08-21.1',
    types: Object.freeze([nextBook]),
    statuses: Object.freeze([nextDraft]),
  })
  const expectedCurrentVersion = await canonicalContentMigrationHash({ types: [], statuses: [] })
  const typeStrategies = Object.freeze({
    book: Object.freeze([
      { kind: 'mapStatus', mappings: { review: 'draft' } },
      { kind: 'flattenHierarchy' },
      { kind: 'retainDormantFeatureData', features: ['excerpt'] },
    ] as const),
  })
  const statusStrategies = Object.freeze({})
  const plan = await previewContentCodeReconciliation(fixture.db, principal, {
    expectedCurrentVersion,
    next,
    typeStrategies,
    statusStrategies,
  }, authorization())
  expect(plan.typeImpacts).toHaveLength(1)
  expect(plan.typeImpacts.find((impact) => impact.definitionKey === 'book')?.affectedCounts).toMatchObject({ entries: 2, 'status:review': 2, hierarchy: 1 })

  const result = await applyContentCodeReconciliation(fixture.db, principal, {
    planToken: plan.token,
    operationId: `${fixture.name}:reconcile:code`,
    next,
    typeStrategies,
    statusStrategies,
  }, authorization(), fx)
  expect(result.version).toBe(next.version)
  const resolvedBook = await resolveContentType(fixture.db, 'book')
  expect(resolvedBook).toMatchObject({ origin: 'code', labels: { name: 'Code Books' }, shadowedDbVersion: 1 })
  const resolvedDraft = await resolveContentStatus(fixture.db, 'draft')
  expect(resolvedDraft).toMatchObject({ origin: 'code', label: 'Code Draft', shadowedDbVersion: 1 })
  const rows = await fixture.db.execute<{ status: string; parentId: string | null; excerpt: string }>(sql`SELECT status, parent_id AS "parentId", excerpt FROM content_entries WHERE type = 'book' ORDER BY id`)
  expect(rows.every((row) => row.status === 'draft' && row.parentId === null)).toBe(true)
  expect(rows.some((row) => row.excerpt === 'purge me')).toBe(true)
  const event = fx.events.find((item) => item.kind === 'codeRegistryReconciled')
  expect(event).toMatchObject({ kind: 'codeRegistryReconciled', toVersion: next.version })
  if (event?.kind === 'codeRegistryReconciled') {
    expect(event.affectedCounts['type:book:status:review']).toBe(2)
    expect(event.changedKeys).toEqual(expect.arrayContaining(['type:book', 'status:draft']))
  }

  const replay = await applyContentCodeReconciliation(fixture.db, principal, {
    planToken: plan.token,
    operationId: `${fixture.name}:reconcile:code`,
    next,
    typeStrategies,
    statusStrategies,
  }, authorization(), fx)
  expect(replay).toEqual(result)

  return {
    rows: rows.map((row) => ({ ...row, parentId: row.parentId ?? null })),
    book: { origin: resolvedBook.origin, version: resolvedBook.version, revision: resolvedBook.revision, shadowedDbVersion: resolvedBook.shadowedDbVersion },
    draft: { origin: resolvedDraft.origin, version: resolvedDraft.version, revision: resolvedDraft.revision, shadowedDbVersion: resolvedDraft.shadowedDbVersion },
  }
}


function restorableBackups(payload: ContentBackupPayload): ContentBackupStore & {
  verified: number
  manifest?: ContentBackupManifest
} {
  const store: ContentBackupStore & { verified: number; manifest?: ContentBackupManifest } = {
    verified: 0,
    async verify(_tx, backupId, scopeHash, corpusVersion, corpusHash, expectedCounts) {
      store.verified += 1
      const manifest: ContentBackupManifest = {
        id: backupId,
        scopeHash,
        corpusVersion,
        corpusHash,
        itemCounts: Object.freeze({ ...expectedCounts }),
        byteCount: 256,
        immutable: true,
      }
      if (store.manifest && (
        store.manifest.id !== manifest.id
        || store.manifest.scopeHash !== manifest.scopeHash
        || store.manifest.corpusVersion !== manifest.corpusVersion
        || store.manifest.corpusHash !== manifest.corpusHash
      )) throw new Error('backup verification identity drifted')
      store.manifest = store.manifest ?? manifest
      return store.manifest
    },
    async read(_tx, manifest) {
      if (!store.manifest || manifest.id !== store.manifest.id) throw new Error('unexpected backup manifest')
      return { manifest: store.manifest, payload: payload as unknown as import('./schema.js').ContentSchemaValue }
    },
  }
  return store
}

async function exerciseRestore(fixture: Fixture) {
  const fx = effects()
  await seedDefinitions(fixture, fx)
  await seedEntries(fixture.db)
  const payload: ContentBackupPayload = Object.freeze({
    version: 1,
    entries: Object.freeze([
      Object.freeze({ id: '22222222-2222-4222-8222-222222222222', excerpt: 'purge me' }),
    ]),
    typeDefinitions: Object.freeze([
      Object.freeze({ key: 'book', definition: typeDefinition() }),
    ]),
  })
  const backups = restorableBackups(payload)
  const strategies = Object.freeze([
    { kind: 'purgeFeatureData' as const, features: Object.freeze(['excerpt'] as const), backupId: 'backup-restore-1' },
  ])
  const impact = await previewContentTypeChange(fixture.db, principal, {
    key: 'book',
    expectedVersion: 1,
    patch: { supports: ['title', 'editor', 'pageAttributes'] },
    strategies,
  }, authorization())
  expect(impact.affectedCounts['feature:excerpt']).toBe(1)
  const changed = await applyContentTypeChange(fixture.db, principal, {
    impactToken: impact.token,
    operationId: `${fixture.name}:purge:restore`,
    strategies,
  }, authorization(), fx, { destructive: { backups } })
  expect(changed).toMatchObject({ version: 2, revision: 2, supports: ['editor', 'pageAttributes', 'title'] })
  expect(backups.manifest).toBeDefined()
  const afterPurge = await fixture.db.execute<{ excerpt: string }>(sql`SELECT excerpt FROM content_entries WHERE id = ${'22222222-2222-4222-8222-222222222222'}`)
  expect(afterPurge[0]?.excerpt).toBe('')

  const destinationCorpusHash = await contentBackupDestinationHash(fixture.db, payload)
  const restored = await restoreContentBackup(fixture.db, principal, {
    manifest: backups.manifest!,
    destinationCorpusHash,
    operationId: `${fixture.name}:restore:backup`,
  }, authorization(), fx, { backups })
  expect(restored).toMatchObject({
    manifestId: 'backup-restore-1',
    destinationCorpusHash,
    restoredCounts: { entries: 1, typeDefinitions: 1 },
  })
  const restoredBook = await resolveContentType(fixture.db, 'book')
  expect(restoredBook).toMatchObject({ version: 3, revision: 3, supports: ['editor', 'excerpt', 'pageAttributes', 'title'] })
  const restoredRows = await fixture.db.execute<{ id: string; excerpt: string; revision: number | string }>(sql`SELECT id, excerpt, type_definition_revision AS revision FROM content_entries WHERE type = 'book' ORDER BY id`)
  expect(restoredRows.map((row) => ({ id: row.id, excerpt: row.excerpt, revision: Number(row.revision) }))).toEqual([
    { id: '11111111-1111-4111-8111-111111111111', excerpt: '', revision: 3 },
    { id: '22222222-2222-4222-8222-222222222222', excerpt: 'purge me', revision: 3 },
  ])
  expect(backups.verified).toBe(2)

  const replay = await restoreContentBackup(fixture.db, principal, {
    manifest: backups.manifest!,
    destinationCorpusHash,
    operationId: `${fixture.name}:restore:backup`,
  }, authorization(), fx, { backups })
  expect(replay).toEqual(restored)
  expect(backups.verified).toBe(2)
  expect((await resolveContentType(fixture.db, 'book')).revision).toBe(3)

  return {
    restoredCounts: restored.restoredCounts,
    book: { version: restoredBook.version, revision: restoredBook.revision, hash: restoredBook.canonicalHash },
    rows: restoredRows.map((row) => ({ id: row.id, excerpt: row.excerpt, revision: Number(row.revision) })),
  }
}

async function exerciseRevocation(fixture: Fixture) {
  const fx = effects()
  await seedDefinitions(fixture, fx)
  await seedEntries(fixture.db)
  const impact = await previewContentTypeChange(fixture.db, principal, {
    key: 'book', expectedVersion: 1, patch: { description: 'authorized preview' }, strategies: [{ kind: 'reject' }],
  }, authorization())
  let queries = 0
  const guarded: ContentDefinitionExecutor = {
    execute: <Row extends Record<string, unknown> = Record<string, unknown>>(query: import('drizzle-orm').SQLWrapper) => {
      queries += 1
      return fixture.db.execute<Row>(query)
    },
  }
  const revoked: ContentDefinitionAuthorization = {
    assert: async () => { throw new ContentDefinitionError('definition-conflict', 'revoked') },
  }
  await expect(applyContentTypeChange(guarded, principal, {
    impactToken: impact.token,
    operationId: `${fixture.name}:revoked`,
    strategies: [{ kind: 'reject' }],
  }, revoked, fx)).rejects.toMatchObject({ detail: 'revoked' })
  expect(queries).toBe(0)
  const current = await resolveContentType(fixture.db, 'book')
  expect(current).toMatchObject({ version: 1, revision: 1 })
  return { version: current.version, revision: current.revision, queries }
}

describe('content definition lifecycle', () => {
  it('keeps authorization ahead of registry lookup on denied creation', async () => {
    let queried = false
    const db: ContentDefinitionExecutor = { execute: async () => { queried = true; return [] } }
    const denied: ContentDefinitionAuthorization = { assert: async () => { throw new ContentDefinitionError('definition-conflict', 'denied') } }
    await expect(createContentType(db, principal, { definition: typeDefinition(), operationId: 'denied' }, denied, effects()))
      .rejects.toMatchObject({ detail: 'denied' })
    expect(queried).toBe(false)
  })

  it('restores a verified destructive backup under new immutable revisions on D1/Postgres', async () => {
    const d1 = await d1Fixture()
    const pg = await pgFixture()
    try {
      const [d1Result, pgResult] = await Promise.all([exerciseRestore(d1), exerciseRestore(pg)])
      expect(pgResult).toEqual(d1Result)
    } finally {
      await pg.close()
      await d1.close()
    }
  }, 90_000)

  it('rejects revoked apply before impact or receipt lookup on D1/Postgres', async () => {
    const d1 = await d1Fixture()
    const pg = await pgFixture()
    try {
      const [d1Result, pgResult] = await Promise.all([exerciseRevocation(d1), exerciseRevocation(pg)])
      expect(pgResult).toEqual(d1Result)
    } finally {
      await pg.close()
      await d1.close()
    }
  }, 90_000)

  it('routes DB-to-code collisions and narrowing through corpus-bound reconciliation on D1/Postgres', async () => {
    const d1 = await d1Fixture()
    const pg = await pgFixture()
    try {
      const [d1Result, pgResult] = await Promise.all([exerciseCodeReconciliation(d1), exerciseCodeReconciliation(pg)])
      expect(pgResult).toEqual(d1Result)
    } finally {
      await pg.close()
      await d1.close()
    }
  }, 90_000)

  it('proves registry collisions, impact-bound destructive changes, replay, drift rejection, and immutable revisions on D1/Postgres', async () => {
    const d1 = await d1Fixture()
    const pg = await pgFixture()
    try {
      const [d1Result, pgResult] = await Promise.all([exercise(d1), exercise(pg)])
      expect(pgResult).toEqual(d1Result)
    } finally {
      await pg.close()
      await d1.close()
    }
  }, 90_000)
})
