import { sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { createPgliteClient } from '@platform-modules/db/pglite'
import { isInventoryValidationError } from './errors.js'
import { consume } from './consume.js'
import { pushSchema } from './migrate.js'
import { release } from './release.js'
import { reserve } from './reserve.js'
import { inventorySchema } from './schema.js'
import { setInventory } from './set-inventory.js'

const SKU_ID = '33333333-3333-4333-8333-333333333333'
const NOW = new Date('2026-06-19T12:00:00.000Z')

async function freshDb() {
  const db = createPgliteClient({ schema: inventorySchema })
  await pushSchema(db)
  return db
}

async function readSold(db: Awaited<ReturnType<typeof freshDb>>, skuId: string) {
  const res = await db.execute(sql`
    SELECT quantity_sold FROM inventory_item WHERE sku_id = ${skuId}::uuid
  `)
  const rows = (Array.isArray(res) ? res : res.rows) as Array<{ quantity_sold: number }>
  return Number(rows[0]?.quantity_sold ?? 0)
}

describe('consume', () => {
  it('marks consumed, increments quantity_sold, and returns true', async () => {
    const db = await freshDb()
    await db.transaction((tx) => setInventory(tx, { skuId: SKU_ID, quantityTotal: 10 }))

    const { reservationId } = await db.transaction((tx) =>
      reserve(tx, { skuId: SKU_ID, qty: 3, holderRef: 'h1', now: NOW }),
    )

    const consumed = await db.transaction((tx) => consume(tx, reservationId))
    expect(consumed).toBe(true)
    expect(await readSold(db, SKU_ID)).toBe(3)

    const rowRes = await db.execute(sql`
      SELECT consumed_at FROM stock_reservation WHERE id = ${reservationId}::uuid
    `)
    const rows = (Array.isArray(rowRes) ? rowRes : rowRes.rows) as Array<{ consumed_at: unknown }>
    expect(rows[0]?.consumed_at).not.toBeNull()
  })

  it('returns false on second consume without double-incrementing sold', async () => {
    const db = await freshDb()
    await db.transaction((tx) => setInventory(tx, { skuId: SKU_ID, quantityTotal: 10 }))

    const { reservationId } = await db.transaction((tx) =>
      reserve(tx, { skuId: SKU_ID, qty: 2, holderRef: 'h1', now: NOW }),
    )

    expect(await db.transaction((tx) => consume(tx, reservationId))).toBe(true)
    expect(await db.transaction((tx) => consume(tx, reservationId))).toBe(false)
    expect(await readSold(db, SKU_ID)).toBe(2)
  })

  it('returns false when the reservation was already released', async () => {
    const db = await freshDb()
    await db.transaction((tx) => setInventory(tx, { skuId: SKU_ID, quantityTotal: 10 }))

    const { reservationId } = await db.transaction((tx) =>
      reserve(tx, { skuId: SKU_ID, qty: 2, holderRef: 'h1', now: NOW }),
    )

    await db.transaction((tx) => release(tx, reservationId))
    expect(await db.transaction((tx) => consume(tx, reservationId))).toBe(false)
    expect(await readSold(db, SKU_ID)).toBe(0)
  })

  it('rejects a malformed reservationId with InventoryValidationError', async () => {
    const db = await freshDb()
    await expect(
      db.transaction((tx) => consume(tx, 'not-a-uuid')),
    ).rejects.toSatisfy((e) => isInventoryValidationError(e))
  })

  it('holderRef discovery: reservation findable by holder_ref after reserve (table-split discriminator)', async () => {
    // Guard against the table-split bug: reserve() must write to stock_reservation
    // so a webhook-style SELECT … WHERE holder_ref = X finds the row.
    const db = await freshDb()
    await db.transaction((tx) => setInventory(tx, { skuId: SKU_ID, quantityTotal: 10 }))

    const HOLDER = 'pi_test_holderref_discriminator'
    await db.transaction((tx) =>
      reserve(tx, { skuId: SKU_ID, qty: 3, holderRef: HOLDER, now: NOW }),
    )

    const discoveryRes = await db.execute(sql`
      SELECT id, sku_id, qty FROM stock_reservation WHERE holder_ref = ${HOLDER}
    `)
    const rows = (Array.isArray(discoveryRes) ? discoveryRes : discoveryRes.rows) as Array<{
      id: string
      sku_id: string
      qty: number
    }>

    expect(rows).toHaveLength(1)
    expect(Number(rows[0]?.qty)).toBe(3)

    const reservationId = rows[0]!.id
    const consumed = await db.transaction((tx) => consume(tx, reservationId))
    expect(consumed).toBe(true)
    expect(await readSold(db, SKU_ID)).toBe(3)
  })
})
