import { sql } from 'drizzle-orm'
import type { TransactionalDatabase } from '@platform-modules/db'
import { LabelPurchaseError } from '../errors.js'
import type { CarrierAdapter } from '../seams.js'
import type { FulfillmentSchema } from '../schema.js'
import { assertUuid, type Label } from '../types.js'
import { firstRow, rowToShipment } from './shipment.js'

function defaultIdempotencyKey(shipmentId: string): string {
  return `label:${shipmentId}`
}

async function loadShipment(
  db: TransactionalDatabase<FulfillmentSchema>,
  shipmentId: string,
) {
  const res = await db.execute(sql`
    SELECT
      id,
      order_id,
      line_ids,
      address,
      status,
      carrier_kind,
      label_id,
      tracking_number,
      created_at,
      updated_at
    FROM shipment
    WHERE id = ${shipmentId}::uuid
  `)
  const row = firstRow(res)
  if (!row) {
    throw new LabelPurchaseError(shipmentId, 'shipment not found')
  }
  return rowToShipment(row)
}

export async function buyShippingLabel(
  db: TransactionalDatabase<FulfillmentSchema>,
  shipmentId: string,
  carrier: CarrierAdapter,
  idempotencyKey?: string,
): Promise<Label> {
  assertUuid(shipmentId, 'shipmentId')

  const key = idempotencyKey ?? defaultIdempotencyKey(shipmentId)
  const shipment = await loadShipment(db, shipmentId)

  if (shipment.labelId && shipment.trackingNumber) {
    return {
      id: shipment.labelId,
      trackingNumber: shipment.trackingNumber,
    }
  }

  let label: Label
  try {
    label = await carrier.buyLabel({
      shipmentId,
      address: shipment.address,
      idempotencyKey: key,
    })
  } catch (e) {
    const detail = e instanceof Error ? e.message : String(e)
    throw new LabelPurchaseError(shipmentId, detail)
  }

  await db.transaction(async (tx) => {
    await tx.execute(sql`
      UPDATE shipment
      SET
        label_id = ${label.id},
        tracking_number = ${label.trackingNumber},
        status = 'labeled',
        carrier_kind = ${carrier.kind},
        updated_at = NOW()
      WHERE id = ${shipmentId}::uuid
        AND status = 'pending'
    `)
  })

  return label
}
