import { sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { createPgliteClient } from '../../db/src/postgres/pglite.js'
import { pushSchema } from './migrate.js'
import { meteringSchema } from './schema.js'

async function database() {
  const db = createPgliteClient({ schema: meteringSchema })
  await pushSchema(db)
  return db
}

async function seedUsageEvent(db: Awaited<ReturnType<typeof database>>) {
  await db.execute(sql`
    INSERT INTO metering_usage_event (
      id, tenant_id, account, meter, period_id, units, kind, reservation_id, rule_version
    ) VALUES (
      'event-1', 'tenant-1', 'account-1', 'api.calls', 'period-1', 4, 'commit',
      'reservation-1', 'rule-v1'
    )
  `)
}

async function rejection(operation: Promise<unknown>): Promise<string> {
  try {
    await operation
    return ''
  } catch (error) {
    const messages: string[] = []
    let current: unknown = error
    while (current instanceof Error) {
      messages.push(current.message)
      current = current.cause
    }
    return messages.join(' | ')
  }
}

async function unitsOf(db: Awaited<ReturnType<typeof database>>): Promise<string | undefined> {
  const result = await db.execute(sql`SELECT units FROM metering_usage_event WHERE id = 'event-1'`)
  const rows = (Array.isArray(result) ? result : (result as { rows?: unknown[] }).rows) ?? []
  const row = rows[0] as { units?: unknown } | undefined
  return row?.units === undefined ? undefined : String(row.units)
}

describe('pushSchema', () => {
  it('rejects UPDATE of a usage event', async () => {
    const db = await database()
    await seedUsageEvent(db)

    expect(
      await rejection(db.execute(sql`UPDATE metering_usage_event SET units = 99 WHERE id = 'event-1'`)),
    ).toMatch(/append-only/)
    expect(await unitsOf(db)).toBe('4')
  })

  it('rejects DELETE of a usage event', async () => {
    const db = await database()
    await seedUsageEvent(db)

    expect(
      await rejection(db.execute(sql`DELETE FROM metering_usage_event WHERE id = 'event-1'`)),
    ).toMatch(/append-only/)
    expect(await unitsOf(db)).toBe('4')
  })

  it('rejects TRUNCATE of the usage-event table', async () => {
    const db = await database()
    await seedUsageEvent(db)

    expect(await rejection(db.execute(sql`TRUNCATE metering_usage_event`))).toMatch(/append-only/)
    expect(await unitsOf(db)).toBe('4')
  })

  it('admits INSERT of a usage event', async () => {
    const db = await database()
    await seedUsageEvent(db)

    expect(await unitsOf(db)).toBe('4')
  })

  it('is idempotent across repeated pushes', async () => {
    const db = await database()
    await pushSchema(db)
    await seedUsageEvent(db)

    expect(
      await rejection(db.execute(sql`DELETE FROM metering_usage_event WHERE id = 'event-1'`)),
    ).toMatch(/append-only/)
  })
})
