import { eq } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { product } from '@platform-modules/commerce-catalog'
import { DEFAULT_COMMISSION_BPS } from './types.js'
import {
  VendorAlreadyExistsError,
  VendorAuthorizationError,
  VendorStateError,
  isVendorAlreadyExistsError,
  isVendorAuthorizationError,
  isVendorStateError,
} from './errors.js'
import { vendor } from './schema.js'
import { applyAsVendor, approveVendor, suspendVendor } from './vendor.js'
import { ADMIN, USER_A, freshDb, vendorCount } from './test-helpers.js'

describe('applyAsVendor', () => {
  it('apply-creates-pending', async () => {
    const db = await freshDb()
    const created = await db.transaction((tx) =>
      applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A' }),
    )

    expect(created.status).toBe('pending')
    expect(created.commissionBps).toBe(DEFAULT_COMMISSION_BPS)
    expect(created.ownerUserId).toBe(USER_A)
    expect(created.name).toBe('Shop A')

    const [row] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(row?.status).toBe('pending')
    expect(row?.commissionBps).toBe(DEFAULT_COMMISSION_BPS)
  })

  it('apply-ignores-supplied-rate', async () => {
    const db = await freshDb()
    const forced = {
      ownerUserId: USER_A,
      name: 'Shop A',
      commissionBps: 0,
    } as Parameters<typeof applyAsVendor>[1] & { commissionBps: number }

    const created = await db.transaction((tx) => applyAsVendor(tx, forced))
    expect(created.commissionBps).toBe(DEFAULT_COMMISSION_BPS)

    const [row] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(row?.commissionBps).toBe(DEFAULT_COMMISSION_BPS)
    expect(row?.commissionBps).not.toBe(0)
  })

  it('apply-duplicate-owner', async () => {
    const db = await freshDb()
    await db.transaction((tx) => applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A' }))

    await expect(
      db.transaction((tx) => applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A Again' })),
    ).rejects.toSatisfy((e) => isVendorAlreadyExistsError(e))

    try {
      await db.transaction((tx) =>
        applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A Again' }),
      )
    } catch (e) {
      expect(e).toBeInstanceOf(VendorAlreadyExistsError)
      expect((e as VendorAlreadyExistsError).httpStatus).toBe(409)
      expect((e as VendorAlreadyExistsError).context.ownerUserId).toBe(USER_A)
      expect(String(e)).not.toMatch(/23505/)
    }

    expect(await vendorCount(db)).toBe(1)
  })
})

describe('approveVendor', () => {
  it('approve-admin-only', async () => {
    const db = await freshDb()
    const created = await db.transaction((tx) =>
      applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A' }),
    )

    await expect(
      db.transaction((tx) => approveVendor(tx, created.id, { userId: USER_A })),
    ).rejects.toSatisfy((e) => isVendorAuthorizationError(e))

    const [unchanged] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(unchanged?.status).toBe('pending')

    await db.transaction((tx) => approveVendor(tx, created.id, ADMIN))

    const [approved] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(approved?.status).toBe('approved')
  })

  it('approve-illegal-transition', async () => {
    const db = await freshDb()
    const created = await db.transaction((tx) =>
      applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A' }),
    )
    await db.transaction((tx) => suspendVendor(tx, created.id, ADMIN))

    await expect(
      db.transaction((tx) => approveVendor(tx, created.id, ADMIN)),
    ).rejects.toSatisfy((e) => isVendorStateError(e))

    const [unchanged] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(unchanged?.status).toBe('suspended')
  })
})

describe('suspendVendor', () => {
  it('suspend-admin-only', async () => {
    const db = await freshDb()
    const created = await db.transaction((tx) =>
      applyAsVendor(tx, { ownerUserId: USER_A, name: 'Shop A' }),
    )
    await db.transaction((tx) => approveVendor(tx, created.id, ADMIN))

    await expect(
      db.transaction((tx) => suspendVendor(tx, created.id, { userId: USER_A })),
    ).rejects.toSatisfy((e) => isVendorAuthorizationError(e))

    const [stillApproved] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(stillApproved?.status).toBe('approved')

    await db.transaction((tx) => suspendVendor(tx, created.id, ADMIN))

    const [suspended] = await db.select().from(vendor).where(eq(vendor.id, created.id))
    expect(suspended?.status).toBe('suspended')
  })
})
