import { eq } from 'drizzle-orm'
import { product, variant, variantPrice } from '@platform-modules/commerce-catalog'
import { order } from '@platform-modules/commerce-orders'
import type { Querier, TransactionalDatabase } from '@platform-modules/db'
import type { CheckoutDeps } from './types.js'
import type { CheckoutDbSchema } from './schema.js'
import type { FulfillmentDbSchema } from '@platform-modules/commerce-fulfillment'
import {
  createFakeIntentStore,
  createFakePaymentProvider,
  countAccessGrants,
  fakeFulfillmentPorts,
} from './testing/index.js'

export const BUYER_ID = '33333333-3333-4333-8333-333333333333'
export const OTHER_USER_ID = '44444444-4444-4444-8444-444444444444'
export const VARIANT_DIGITAL = '22222222-2222-4222-8222-222222222222'
export const VARIANT_STALE = '55555555-5555-4555-8555-555555555555'

export function createTrackingLedger() {
  const insertedKeys = new Set<string>()
  const calls: Array<{ key: string; delta: bigint }> = []

  return {
    calls,
    ledger: {
      async appendEntry(
        _db: unknown,
        input: { key: string; delta: bigint; reason: string },
      ) {
        calls.push({ key: input.key, delta: input.delta })
        if (insertedKeys.has(input.key)) {
          return { inserted: false, id: null }
        }
        insertedKeys.add(input.key)
        return { inserted: true, id: `le-${input.key}` }
      },
    },
  }
}

export async function seedDigitalVariant(
  db: TransactionalDatabase<CheckoutDbSchema>,
  opts: {
    variantId: string
    amount: bigint
    currency?: string
    priceMode?: 'exclusive' | 'inclusive'
    status?: 'active' | 'draft'
    vendorId?: string | null
  },
): Promise<void> {
  const productId = crypto.randomUUID()
  const currency = opts.currency ?? 'USD'
  await db.insert(product).values({
    id: productId,
    kind: 'digital',
    slug: `product-${opts.variantId.slice(0, 8)}`,
    title: 'Digital Item',
    status: opts.status ?? 'active',
    media: [],
    tags: [],
    vendorId: opts.vendorId ?? null,
  })
  await db.insert(variant).values({
    id: opts.variantId,
    productId,
    sku: `sku-${opts.variantId.slice(0, 8)}`,
    attributes: {},
  })
  await db.insert(variantPrice).values({
    variantId: opts.variantId,
    currency,
    amount: opts.amount,
    priceMode: opts.priceMode ?? 'exclusive',
  })
}

export function buildDigitalCart(variantId: string, amount: bigint, currency = 'USD') {
  return {
    id: 'cart-1',
    currency,
    subtotal: amount,
    lines: [
      {
        lineId: 'line-1',
        variantId,
        qty: 1,
        price: { amount, currency, priceMode: 'exclusive' as const },
        vendorId: null,
      },
    ],
  }
}

export function buildCheckoutDeps(
  db: TransactionalDatabase<CheckoutDbSchema>,
  overrides: Partial<{
    provider: ReturnType<typeof createFakePaymentProvider>
    intentStore: ReturnType<typeof createFakeIntentStore>
    fulfillment: ReturnType<typeof fakeFulfillmentPorts>
  }> = {},
): CheckoutDeps {
  const { ledger } = createTrackingLedger()
  const provider = overrides.provider ?? createFakePaymentProvider()
  const intentStore = overrides.intentStore ?? createFakeIntentStore()
  const fulfillment =
    overrides.fulfillment ??
    fakeFulfillmentPorts({
      db: db as unknown as TransactionalDatabase<FulfillmentDbSchema>,
    })

  return {
    db,
    provider,
    ledger,
    intentStore,
    fulfillment,
  }
}

export function baseCheckoutInput(idempotencyKey = crypto.randomUUID()) {
  return {
    cart: buildDigitalCart(VARIANT_DIGITAL, 1000n),
    buyerRef: { userId: BUYER_ID },
    priceMode: 'exclusive' as const,
    currency: 'USD',
    buyerCountry: 'DE',
    idempotencyKey,
  }
}

export async function countGrantsForOrder(
  db: TransactionalDatabase<CheckoutDbSchema>,
  orderId: string,
): Promise<number> {
  return countAccessGrants(db as unknown as Querier<FulfillmentDbSchema>, orderId)
}

export async function setOrderStatus(
  db: TransactionalDatabase<CheckoutDbSchema>,
  orderId: string,
  status: string,
  chargeRef?: string,
): Promise<void> {
  await db
    .update(order)
    .set({
      status: status as typeof order.$inferSelect.status,
      ...(chargeRef !== undefined ? { chargeRef } : {}),
      updatedAt: new Date(),
    })
    .where(eq(order.id, orderId))
}

export async function ageOrder(
  db: TransactionalDatabase<CheckoutDbSchema>,
  orderId: string,
  olderThanMs: number,
): Promise<void> {
  const aged = new Date(Date.now() - olderThanMs - 1000)
  await db
    .update(order)
    .set({ updatedAt: aged, createdAt: aged })
    .where(eq(order.id, orderId))
}
