import { sql } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import type { FulfillmentSchema } from '../schema.js'
import { assertUuid, type Address, type Shipment, type ShipmentStatus } from '../types.js'

type SqlRow = Record<string, unknown>

export function firstRow(res: unknown): SqlRow | undefined {
  const rows = (Array.isArray(res) ? res : (res as { rows?: SqlRow[] }).rows) ?? []
  return rows[0]
}

function parseJsonField<T>(value: unknown): T {
  if (typeof value === 'string') {
    return JSON.parse(value) as T
  }
  return value as T
}

export function rowToShipment(row: SqlRow): Shipment {
  const createdAt = row.created_at
  const updatedAt = row.updated_at
  return {
    id: String(row.id),
    orderId: String(row.order_id),
    lineIds: parseJsonField<string[]>(row.line_ids),
    address: parseJsonField<Address>(row.address),
    status: String(row.status) as ShipmentStatus,
    carrierKind: row.carrier_kind == null ? null : String(row.carrier_kind),
    labelId: row.label_id == null ? null : String(row.label_id),
    trackingNumber: row.tracking_number == null ? null : String(row.tracking_number),
    createdAt:
      createdAt instanceof Date ? createdAt : new Date(String(createdAt)),
    updatedAt:
      updatedAt instanceof Date ? updatedAt : new Date(String(updatedAt)),
  }
}

export async function createShipment(
  tx: Transaction<FulfillmentSchema>,
  input: {
    orderId: string
    lineIds: string[]
    address: Address
  },
): Promise<Shipment> {
  assertUuid(input.orderId, 'orderId')

  const id = crypto.randomUUID()
  const lineIdsJson = JSON.stringify(input.lineIds)
  const addressJson = JSON.stringify(input.address)

  const insertRes = await tx.execute(sql`
    INSERT INTO shipment (id, order_id, line_ids, address, status)
    VALUES (
      ${id}::uuid,
      ${input.orderId}::uuid,
      ${lineIdsJson}::jsonb,
      ${addressJson}::jsonb,
      'pending'
    )
    RETURNING
      id,
      order_id,
      line_ids,
      address,
      status,
      carrier_kind,
      label_id,
      tracking_number,
      created_at,
      updated_at
  `)

  const inserted = firstRow(insertRes)
  if (!inserted) {
    throw new Error('createShipment: insert returned no row')
  }
  return rowToShipment(inserted)
}
