import { sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { admin, lifecycleFixture, writeDeps } from './lifecycle.test-helpers.js'
import { applyPermanentContentDelete, createContentEntry, updateContentEntry } from './lifecycle.js'
import { preparePermanentContentDelete } from './hierarchy.js'

describe('content hierarchy',()=>{
  it('rejects cycles and permanently deletes a parent while reparenting surviving children',async()=>{
    const fx=await lifecycleFixture()
    try{
      const deps=writeDeps()
      const root=await fx.db.transaction((tx)=>createContentEntry(tx,{operationId:'c:root',slug:'root',type:'book',title:'Root',body:'x'},admin,deps))
      const parent=await fx.db.transaction((tx)=>createContentEntry(tx,{operationId:'c:parent',slug:'parent',type:'book',title:'Parent',body:'x',parentId:root.id},admin,deps))
      const child=await fx.db.transaction((tx)=>createContentEntry(tx,{operationId:'c:child',slug:'child',type:'book',title:'Child',body:'x',parentId:parent.id},admin,deps))
      await expect(fx.db.transaction((tx)=>updateContentEntry(tx,root.id,{operationId:'cycle',expectedUpdatedAt:root.updatedAt,patch:{parentId:child.id}},admin,deps))).rejects.toMatchObject({code:'cycle'})

      const plan=await fx.db.transaction((tx)=>preparePermanentContentDelete(tx,[{id:parent.id,expectedUpdatedAt:parent.updatedAt}],admin))
      expect(plan.reparent).toEqual([expect.objectContaining({childId:child.id,fromParentId:parent.id,toParentId:root.id})])
      const result=await fx.db.transaction((tx)=>applyPermanentContentDelete(tx,plan,'delete:parent',admin,{outbox:deps.outbox}))
      expect(result).toEqual([expect.objectContaining({id:parent.id})])
      const rows=await fx.db.transaction((tx)=>tx.execute<{id:string;parentId:string|null}>(sql`SELECT id,parent_id AS "parentId" FROM content_entries ORDER BY slug`))
      expect(rows.find((row)=>row.id===parent.id)).toBeUndefined()
      expect(rows.find((row)=>row.id===child.id)?.parentId).toBe(root.id)
      const tombstones=await fx.db.transaction((tx)=>tx.execute<{entryId:string}>(sql`SELECT entry_id AS "entryId" FROM content_tombstones WHERE operation_id = 'delete:parent'`))
      expect(tombstones).toEqual([{entryId:parent.id}])
      const replay=await fx.db.transaction((tx)=>applyPermanentContentDelete(tx,plan,'delete:parent',admin,{outbox:deps.outbox}))
      expect(replay).toEqual(result)
    }finally{await fx.stop()}
  },90_000)

  it('fails closed when an entry points at stale type/status definition revisions',async()=>{
    const fx=await lifecycleFixture()
    try{
      const deps=writeDeps()
      const entry=await fx.db.transaction((tx)=>createContentEntry(tx,{operationId:'drift:create',slug:'drift',type:'book',title:'Drift',body:'x'},admin,deps))
      await fx.db.transaction((tx)=>tx.execute(sql`UPDATE content_entries SET status_definition_revision = 999 WHERE id = ${entry.id}`))
      await expect(fx.db.transaction((tx)=>preparePermanentContentDelete(tx,[{id:entry.id,expectedUpdatedAt:entry.updatedAt}],admin))).rejects.toMatchObject({code:'access-denied'})
    }finally{await fx.stop()}
  },90_000)

})
