import { eq } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { createPgliteClient } from '@platform-modules/db/pglite'
import { MixedCurrencyError } from '../errors.js'
import { createDbCartStore, pushSchema } from './index.js'
import { cart as cartTable, cartSchema } from './schema.js'
import type { Cart, PriceSnapshot } from '../types.js'

const VARIANT_A = '11111111-1111-4111-8111-111111111111'
const VARIANT_B = '22222222-2222-4222-8222-222222222222'

const usdPrice = (amount: bigint): PriceSnapshot => ({
  amount,
  currency: 'USD',
  priceMode: 'inclusive',
})

const eurPrice = (amount: bigint): PriceSnapshot => ({
  amount,
  currency: 'EUR',
  priceMode: 'inclusive',
})

function makeCart(id: string, lines: Cart['lines']): Cart {
  const subtotal = lines.reduce(
    (sum, line) => sum + line.price.amount * BigInt(line.qty),
    0n,
  )
  return {
    id,
    currency: lines[0]?.price.currency ?? '',
    lines,
    subtotal,
  }
}

async function freshStore() {
  const db = createPgliteClient({ schema: cartSchema })
  await pushSchema(db)
  return { db, store: createDbCartStore(db) }
}

describe('createDbCartStore', () => {
  it('load returns null for a missing cart', async () => {
    const { store } = await freshStore()
    expect(await store.load('00000000-0000-4000-8000-000000000099')).toBeNull()
  })

  it('save→load roundtrip preserves lines and bigint amount', async () => {
    const { store } = await freshStore()
    const saved = makeCart('00000000-0000-4000-8000-000000000001', [
      {
        lineId: '33333333-3333-4333-8333-333333333333',
        variantId: VARIANT_A,
        qty: 2,
        price: usdPrice(1999n),
        vendorId: null,
      },
    ])

    await store.save(saved)
    const loaded = await store.load(saved.id)

    expect(loaded).not.toBeNull()
    expect(loaded!.lines).toHaveLength(1)
    expect(typeof loaded!.lines[0]!.price.amount).toBe('bigint')
    expect(loaded!.lines[0]!.price.amount).toBe(1999n)
    expect(loaded!.lines[0]!.qty).toBe(2)
    expect(loaded!.subtotal).toBe(3998n)
    expect(typeof loaded!.subtotal).toBe('bigint')
  })

  it('merge sums duplicate variant qty and persists', async () => {
    const { db, store } = await freshStore()
    const guestId = '00000000-0000-4000-8000-000000000010'
    const userCartId = '00000000-0000-4000-8000-000000000020'
    const userId = 'user-smoke-1'

    await store.save(
      makeCart(guestId, [
        {
          lineId: '44444444-4444-4444-8444-444444444444',
          variantId: VARIANT_A,
          qty: 2,
          price: usdPrice(1000n),
          vendorId: null,
        },
      ]),
    )
    await store.save(
      makeCart(userCartId, [
        {
          lineId: '55555555-5555-4555-8555-555555555555',
          variantId: VARIANT_A,
          qty: 1,
          price: usdPrice(1200n),
          vendorId: null,
        },
      ]),
    )
    await db.update(cartTable).set({ userId }).where(eq(cartTable.id, userCartId))

    const merged = await store.merge(guestId, userId)
    expect(merged.lines).toHaveLength(1)
    expect(merged.lines[0]?.qty).toBe(3)
    expect(merged.lines[0]?.price.amount).toBe(1200n)
    expect(merged.subtotal).toBe(3600n)
    expect(await store.load(guestId)).toBeNull()

    const reloaded = await store.load(merged.id)
    expect(reloaded?.lines[0]?.qty).toBe(3)
  })

  it('merge keeps the user-cart price snapshot on duplicate variants', async () => {
    const { db, store } = await freshStore()
    const guestId = '00000000-0000-4000-8000-000000000030'
    const userCartId = '00000000-0000-4000-8000-000000000040'
    const userId = 'user-price-wins'

    await store.save(
      makeCart(guestId, [
        {
          lineId: '66666666-6666-4666-8666-666666666666',
          variantId: VARIANT_A,
          qty: 1,
          price: usdPrice(2000n),
          vendorId: null,
        },
      ]),
    )
    await store.save(
      makeCart(userCartId, [
        {
          lineId: '77777777-7777-4777-8777-777777777777',
          variantId: VARIANT_A,
          qty: 1,
          price: usdPrice(1500n),
          vendorId: null,
        },
      ]),
    )
    await db.update(cartTable).set({ userId }).where(eq(cartTable.id, userCartId))

    const merged = await store.merge(guestId, userId)
    expect(merged.lines[0]?.price.amount).toBe(1500n)
    expect(merged.lines[0]?.qty).toBe(2)
  })

  it('merge throws MixedCurrencyError on currency mismatch', async () => {
    const { db, store } = await freshStore()
    const guestId = '00000000-0000-4000-8000-000000000050'
    const userCartId = '00000000-0000-4000-8000-000000000060'
    const userId = 'user-currency-mismatch'

    await store.save(
      makeCart(guestId, [
        {
          lineId: '88888888-8888-4888-8888-888888888888',
          variantId: VARIANT_A,
          qty: 1,
          price: eurPrice(1000n),
          vendorId: null,
        },
      ]),
    )
    await store.save(
      makeCart(userCartId, [
        {
          lineId: '99999999-9999-4999-8999-999999999999',
          variantId: VARIANT_B,
          qty: 1,
          price: usdPrice(1000n),
          vendorId: null,
        },
      ]),
    )
    await db.update(cartTable).set({ userId }).where(eq(cartTable.id, userCartId))

    await expect(store.merge(guestId, userId)).rejects.toBeInstanceOf(MixedCurrencyError)
  })
})
