import { getTableColumns, getTableName } from 'drizzle-orm'
import { getTableConfig } from 'drizzle-orm/pg-core'
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
import { ledgerEntries } from '@platform-modules/ledger'
import * as schema from './schema.js'
import { closeTestDb, makeTestDb, type TestDb } from './test/pglite-db.js'

describe('affiliate schema (Axis B)', () => {
  it('exports the affiliate-owned tables and ships no migration', () => {
    for (const name of [
      'referralLinksTable',
      'referralsTable',
      'affiliateEntriesTable',
      'affiliatePayoutsTable',
      'affiliateEnrollmentsTable',
      'fraudEventsTable',
    ]) {
      expect(schema, `missing export ${name}`).toHaveProperty(name)
    }
    expect(schema.affiliateSchema).toBeDefined()
    expect(schema.affiliateSchema.affiliateEntriesTable).toBe(schema.affiliateEntriesTable)
  })

  it('drops the retired wallet clone tables from the schema contract', () => {
    expect('affiliateCreditLedgerTable' in schema).toBe(false)
    expect('affiliateWalletBalancesTable' in schema).toBe(false)
    expect(schema.affiliateSchema).not.toHaveProperty('affiliateCreditLedgerTable')
    expect(schema.affiliateSchema).not.toHaveProperty('affiliateWalletBalancesTable')
  })

  it('affiliatePayoutsTable.ledgerEntryId references affiliate_entries.entry_id', () => {
    const cols = getTableColumns(schema.affiliatePayoutsTable)
    expect(cols.ledgerEntryId).toBeDefined()

    const { foreignKeys } = getTableConfig(schema.affiliatePayoutsTable)
    const ledgerEntryFk = foreignKeys.find((fk) =>
      fk.reference().columns.some((col) => col.name === 'ledger_entry_id'),
    )
    expect(ledgerEntryFk).toBeDefined()
    expect(getTableName(ledgerEntryFk!.reference().foreignTable)).toBe('affiliate_entries')
    expect(ledgerEntryFk!.reference().foreignColumns.map((col) => col.name)).toEqual(['entry_id'])
  })
})

describe('affiliate_entries table (expand phase)', () => {
  let testDb: TestDb

  beforeAll(async () => {
    testDb = await makeTestDb()
  })

  afterAll(async () => {
    await closeTestDb()
  })

  it('exports affiliateEntriesTable with the §3.1 columns and indexes', () => {
    expect(schema.affiliateEntriesTable).toBeDefined()
    expect(schema.affiliateEntryTypeEnum).toBeDefined()
    expect(schema.affiliateSchema.affiliateEntriesTable).toBe(schema.affiliateEntriesTable)
    expect(getTableName(schema.affiliateEntriesTable)).toBe('affiliate_entries')

    const cols = getTableColumns(schema.affiliateEntriesTable)
    expect(cols.entryId.primary).toBe(true)
    expect(cols.ownerId.notNull).toBe(true)
    expect(cols.entryType.notNull).toBe(true)
    expect(cols.sourceType.notNull).toBe(true)
    expect(cols.sourceId.notNull).toBe(true)
    expect(cols.referralId.notNull).toBe(false)
    expect(cols.resolvedPct.notNull).toBe(false)
    expect(cols.memo.notNull).toBe(false)
    expect(cols.consumedAt.notNull).toBe(false)

    const { indexes } = getTableConfig(schema.affiliateEntriesTable)
    const indexNames = indexes.map((idx) => idx.config.name)
    expect(indexNames).toContain('affiliate_entries_idem_uq')
    expect(indexNames).toContain('affiliate_entries_source_idx')
    expect(indexNames).toContain('affiliate_entries_unconsumed_idx')

    const unconsumedIdx = indexes.find((idx) => idx.config.name === 'affiliate_entries_unconsumed_idx')
    expect(unconsumedIdx?.config.where).toBeDefined()
  })

  it('affiliate_entry_type enum includes redemption', () => {
    expect(schema.affiliateEntryTypeEnum.enumValues).toContain('redemption')
    expect(schema.affiliateEntryTypeEnum.enumValues).toEqual([
      'affiliate_commission',
      'referral_reward',
      'refund_clawback',
      'adjustment',
      'redemption',
    ])
  })

  it('rejects duplicate (entryType, sourceType, sourceId) via unique index', async () => {
    const entryId1 = crypto.randomUUID()
    const entryId2 = crypto.randomUUID()

    await testDb.insert(ledgerEntries).values({
      id: entryId1,
      delta: 100n,
      reason: 'earn',
      idempotencyKey: `idem-${entryId1}`,
    })
    await testDb.insert(ledgerEntries).values({
      id: entryId2,
      delta: 200n,
      reason: 'earn',
      idempotencyKey: `idem-${entryId2}`,
    })

    await testDb.insert(schema.affiliateEntriesTable).values({
      entryId: entryId1,
      ownerId: 'owner-1',
      entryType: 'affiliate_commission',
      sourceType: 'order',
      sourceId: 'ord-1',
    })

    await expect(
      testDb.insert(schema.affiliateEntriesTable).values({
        entryId: entryId2,
        ownerId: 'owner-2',
        entryType: 'affiliate_commission',
        sourceType: 'order',
        sourceId: 'ord-1',
      }),
    ).rejects.toThrow()
  })
})
