import { eq } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import type { Actor } from '@platform-modules/commerce-orders'
import {
  VendorAlreadyExistsError,
  VendorAuthorizationError,
  VendorNotFoundError,
  VendorStateError,
} from './errors.js'
import { vendor, type MarketplaceDbSchema } from './schema.js'
import {
  DEFAULT_COMMISSION_BPS,
  UUID_RE,
  type ApplyAsVendorInput,
  type Vendor,
  type VendorStatus,
} from './types.js'

function isUniqueViolation(e: unknown): boolean {
  let cur: unknown = e
  while (cur) {
    const code = (cur as { code?: unknown })?.code
    const msg = cur instanceof Error ? cur.message : String(cur)
    if (code === '23505' || /vendor_owner_user_id_uq|duplicate key|unique constraint/i.test(msg)) {
      return true
    }
    cur = cur instanceof Error ? (cur as Error & { cause?: unknown }).cause : undefined
  }
  return false
}

function toVendor(row: typeof vendor.$inferSelect): Vendor {
  return {
    id: row.id,
    ownerUserId: row.ownerUserId,
    name: row.name,
    status: row.status,
    commissionBps: row.commissionBps,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
  }
}

function assertApproveTransition(from: VendorStatus, to: VendorStatus, vendorId: string): void {
  if (from === 'pending' && to === 'approved') return
  throw new VendorStateError({ vendorId, from, to })
}

function assertSuspendTransition(from: VendorStatus, to: VendorStatus, vendorId: string): void {
  if ((from === 'pending' || from === 'approved') && to === 'suspended') return
  throw new VendorStateError({ vendorId, from, to })
}

export async function applyAsVendor(
  tx: Transaction<MarketplaceDbSchema>,
  input: ApplyAsVendorInput,
): Promise<Vendor> {
  try {
    const [row] = await tx
      .insert(vendor)
      .values({
        ownerUserId: input.ownerUserId,
        name: input.name,
        status: 'pending',
        commissionBps: DEFAULT_COMMISSION_BPS,
      })
      .returning()
    if (!row) throw new Error('applyAsVendor: insert returned no row')
    return toVendor(row)
  } catch (e) {
    if (isUniqueViolation(e)) {
      throw new VendorAlreadyExistsError({ ownerUserId: input.ownerUserId })
    }
    throw e
  }
}

export async function approveVendor(
  tx: Transaction<MarketplaceDbSchema>,
  vendorId: string,
  admin: Actor,
): Promise<void> {
  if (!admin.isAdmin) throw new VendorAuthorizationError()
  if (!UUID_RE.test(vendorId)) throw new VendorNotFoundError({ vendorId })

  const [row] = await tx.select().from(vendor).where(eq(vendor.id, vendorId)).limit(1)
  if (!row) throw new VendorNotFoundError({ vendorId })

  const to: VendorStatus = 'approved'
  assertApproveTransition(row.status, to, vendorId)

  await tx
    .update(vendor)
    .set({ status: to, updatedAt: new Date() })
    .where(eq(vendor.id, vendorId))
}

export async function suspendVendor(
  tx: Transaction<MarketplaceDbSchema>,
  vendorId: string,
  admin: Actor,
): Promise<void> {
  if (!admin.isAdmin) throw new VendorAuthorizationError()
  if (!UUID_RE.test(vendorId)) throw new VendorNotFoundError({ vendorId })

  const [row] = await tx.select().from(vendor).where(eq(vendor.id, vendorId)).limit(1)
  if (!row) throw new VendorNotFoundError({ vendorId })

  const to: VendorStatus = 'suspended'
  assertSuspendTransition(row.status, to, vendorId)

  await tx
    .update(vendor)
    .set({ status: to, updatedAt: new Date() })
    .where(eq(vendor.id, vendorId))
}
