import { eq } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { appendEntry } from './append.js'
import { ledgerEntries } from './schema.js'
import {
  countLedgerRows,
  createTestDb,
  ledgerRowForKey,
} from './test-fixture.js'

describe('appendEntry', () => {
  it('first append inserts and returns inserted:true', async () => {
    const db = await createTestDb()

    const result = await db.transaction((tx) =>
      appendEntry(tx, {
        key: 'grant-1',
        delta: 100n,
        reason: 'grant',
        ref: { source: 'test' },
      }),
    )

    expect(result).toEqual({ inserted: true, id: expect.any(String) })
    expect(await countLedgerRows(db)).toBe(1)
    expect(await ledgerRowForKey(db, 'grant-1')).toEqual({ delta: 100n })
  })

  it('same key again returns inserted:false with no second row', async () => {
    const db = await createTestDb()
    const input = { key: 'grant-2', delta: 50n, reason: 'grant' as const }

    await db.transaction((tx) => appendEntry(tx, input))
    const second = await db.transaction((tx) => appendEntry(tx, input))

    expect(second).toEqual({ inserted: false, id: null })
    expect(await countLedgerRows(db)).toBe(1)
  })

  it('concurrent double-append of the same key yields exactly one row', async () => {
    const db = await createTestDb()
    const input = { key: 'race-1', delta: 25n, reason: 'grant' as const }

    const [first, second] = await Promise.all([
      db.transaction((tx) => appendEntry(tx, input)),
      db.transaction((tx) => appendEntry(tx, input)),
    ])

    const insertedCount = [first.inserted, second.inserted].filter(Boolean).length
    expect(insertedCount).toBe(1)
    expect(await countLedgerRows(db)).toBe(1)
  })

  it('returns the inserted row id, matching the persisted entry', async () => {
    const db = await createTestDb()
    const result = await db.transaction((tx) =>
      appendEntry(tx, { key: 'grant-id', delta: 100n, reason: 'grant' }),
    )
    expect(result.inserted).toBe(true)
    expect(result.id).toMatch(/^[0-9a-f-]{36}$/) // uuid
    const [row] = await db
      .select({ id: ledgerEntries.id })
      .from(ledgerEntries)
      .where(eq(ledgerEntries.idempotencyKey, 'grant-id'))
    expect(row).toBeDefined()
    expect(result.id).toBe(row?.id)
  })

  it('persists the entry currency when provided', async () => {
    const db = await createTestDb()

    await db.transaction((tx) =>
      appendEntry(tx, { key: 'grant-currency', delta: 10n, currency: 'USD', reason: 'grant' }),
    )

    const [row] = await db
      .select({ currency: ledgerEntries.currency })
      .from(ledgerEntries)
      .where(eq(ledgerEntries.idempotencyKey, 'grant-currency'))
      .limit(1)

    expect(row?.currency).toBe('USD')
  })

  it('different key appends a new row', async () => {
    const db = await createTestDb()

    await db.transaction((tx) =>
      appendEntry(tx, { key: 'a', delta: 10n, reason: 'grant' }),
    )
    await db.transaction((tx) =>
      appendEntry(tx, { key: 'b', delta: 20n, reason: 'grant' }),
    )

    expect(await countLedgerRows(db)).toBe(2)
  })
})
