import type { Order, OrderLine } from '@platform-modules/commerce-orders'
import type { TransactionalDatabase } from '@platform-modules/db'
import type { BuyLabelArgs, CarrierAdapter, FulfillmentPorts, StorageAdapter } from './seams.js'
import type { FulfillmentDbSchema } from './schema.js'
import type {
  Address,
  CarrierTrackResult,
  CarrierWebhookResult,
  FulfillmentNotification,
  Label,
} from './types.js'

export interface MemoryCarrierAdapterOptions {
  buyLabel?: (args: BuyLabelArgs) => Promise<Label>
  track?: (trackingNumber: string) => Promise<CarrierTrackResult>
  verifyWebhook?: (req: Request) => Promise<CarrierWebhookResult | null>
}

export interface MemoryCarrierAdapter extends CarrierAdapter {
  readonly buyLabelCallCount: number
}

const DEFAULT_LABEL: Label = {
  id: 'label-memory-1',
  trackingNumber: 'TRACK-MEMORY-001',
}

const DEFAULT_TRACK: CarrierTrackResult = {
  trackingNumber: 'TRACK-MEMORY-001',
  status: 'in_transit',
}

export function createMemoryCarrierAdapter(
  options: MemoryCarrierAdapterOptions = {},
): MemoryCarrierAdapter {
  let buyLabelCallCount = 0

  return {
    kind: 'memory',
    capabilities: { webhooks: true, tracking: true },

    get buyLabelCallCount() {
      return buyLabelCallCount
    },

    async buyLabel(args) {
      buyLabelCallCount += 1
      if (options.buyLabel) {
        return options.buyLabel(args)
      }
      return { ...DEFAULT_LABEL, id: `label-${args.idempotencyKey}` }
    },

    async track(trackingNumber) {
      if (options.track) {
        return options.track(trackingNumber)
      }
      return { ...DEFAULT_TRACK, trackingNumber }
    },

    async verifyWebhook(req) {
      if (options.verifyWebhook) {
        return options.verifyWebhook(req)
      }
      return null
    },
  }
}

export interface StoragePutRecord {
  key: string
  body: Uint8Array | ArrayBuffer
}

export interface MemoryStorageAdapter extends StorageAdapter {
  readonly puts: StoragePutRecord[]
}

export function createMemoryStorageAdapter(): MemoryStorageAdapter {
  const puts: StoragePutRecord[] = []

  return {
    puts,

    async put(key, body) {
      puts.push({ key, body })
    },

    async signedUrl(key, ttlSeconds) {
      const expiresAt = new Date(Date.now() + ttlSeconds * 1000)
      return {
        url: `https://memory-storage.test/${encodeURIComponent(key)}?ttl=${ttlSeconds}`,
        expiresAt,
      }
    },
  }
}

export const DEFAULT_MEMORY_ADDRESS: Address = {
  line1: '1 Memory Lane',
  city: 'Testville',
  postalCode: '12345',
  country: 'US',
}

export interface MemoryFulfillmentPortsOptions {
  db: TransactionalDatabase<FulfillmentDbSchema>
  storage?: StorageAdapter
  notify?: (notification: FulfillmentNotification) => Promise<void>
  resolveBlobKey?: (line: OrderLine) => Promise<string>
  resolveShippingAddress?: (order: Order) => Promise<Address>
  carriers?: FulfillmentPorts['carriers']
}

export interface MemoryFulfillmentPorts extends FulfillmentPorts {
  readonly notifications: FulfillmentNotification[]
}

export function createMemoryFulfillmentPorts(
  options: MemoryFulfillmentPortsOptions,
): MemoryFulfillmentPorts {
  const notifications: FulfillmentNotification[] = []
  const storage = options.storage ?? createMemoryStorageAdapter()

  return {
    db: options.db,
    storage,
    carriers: options.carriers,
    get notifications() {
      return notifications
    },
    resolveBlobKey:
      options.resolveBlobKey ?? (async (line) => `blob:${line.variantId}`),
    resolveShippingAddress:
      options.resolveShippingAddress ?? (async () => DEFAULT_MEMORY_ADDRESS),
    notify: async (notification) => {
      notifications.push(notification)
      await options.notify?.(notification)
    },
  }
}
