// One typed client method per transfer endpoint (docs/11-BACKEND-PHASE1.md §5.4).
// In-memory Stock Core (lib/api/mock-data.ts) — no backend API calls.
import { PagedResponse } from "@/types/common"
import {
  CreateTransferRequest,
  DispatchTransferResponse,
  ReceiveTransferLineInput,
  ReceiveTransferResponse,
  StockTransfer,
  StockTransferSummary,
  TransferStatus,
} from "@/types/stock"
import {
  MockTransferLine,
  allocateTransferId,
  allocateTransferLineId,
  consumeFifo,
  mockDelay,
  mockStockTransfers,
  postLedgerEntry,
  receiveLayer,
} from "@/lib/api/mock-data"

export interface ListTransfersParams {
  page?: number
  pageSize?: number
  status?: TransferStatus
  srcWarehouseId?: number
  destWarehouseId?: number
}

function toPublicLine(line: MockTransferLine) {
  return {
    transferLineId: line.transferLineId,
    itemId: line.itemId,
    srcBinId: line.srcBinId,
    destBinId: line.destBinId,
    batchId: line.batchId,
    qty: line.qty,
  }
}

function toSummary(t: (typeof mockStockTransfers)[number]): StockTransferSummary {
  return {
    transferId: t.transferId,
    docNo: t.docNo,
    srcWarehouseId: t.srcWarehouseId,
    destWarehouseId: t.destWarehouseId,
    status: t.status,
    createdAt: t.createdAt,
  }
}

function toPublic(t: (typeof mockStockTransfers)[number]): StockTransfer {
  return { ...toSummary(t), createdBy: t.createdBy, lines: t.lines.map(toPublicLine) }
}

export const stockTransfersApi = {
  list(params: ListTransfersParams = {}): Promise<PagedResponse<StockTransferSummary>> {
    const filtered = mockStockTransfers
      .filter((t) => !params.status || t.status === params.status)
      .filter((t) => !params.srcWarehouseId || t.srcWarehouseId === params.srcWarehouseId)
      .filter((t) => !params.destWarehouseId || t.destWarehouseId === params.destWarehouseId)
      .map(toSummary)
      .sort((a, b) => b.transferId - a.transferId)

    const page = params.page ?? 1
    const pageSize = params.pageSize ?? 20
    const start = (page - 1) * pageSize
    const items = filtered.slice(start, start + pageSize)

    return mockDelay({
      items,
      pagination: { page, pageSize, totalItems: filtered.length, totalPages: Math.ceil(filtered.length / pageSize) || 0 },
    })
  },

  get(transferId: number): Promise<StockTransfer> {
    const t = mockStockTransfers.find((x) => x.transferId === transferId)
    if (!t) return Promise.reject(new Error(`Mock transfer ${transferId} not found`))
    return mockDelay(toPublic(t))
  },

  create(request: CreateTransferRequest): Promise<StockTransfer> {
    const transferId = allocateTransferId()
    const t = {
      transferId,
      docNo: `TRF-2026-${String(transferId).padStart(5, "0")}`,
      srcWarehouseId: request.srcWarehouseId,
      destWarehouseId: request.destWarehouseId,
      status: "Draft" as TransferStatus,
      createdBy: 17,
      createdAt: new Date().toISOString(),
      lines: request.lines.map((l) => ({
        transferLineId: allocateTransferLineId(),
        itemId: l.itemId,
        srcBinId: l.srcBinId ?? null,
        destBinId: l.destBinId ?? null,
        batchId: l.batchId ?? null,
        qty: l.qty,
        dispatchedChunks: [],
      })),
    }
    mockStockTransfers.push(t)
    return mockDelay(toPublic(t))
  },

  dispatch(transferId: number): Promise<DispatchTransferResponse> {
    const t = mockStockTransfers.find((x) => x.transferId === transferId)
    if (!t) return Promise.reject(new Error(`Mock transfer ${transferId} not found`))
    if (t.status !== "Draft") return Promise.reject(new Error(`${t.docNo} has already been dispatched.`))

    const consumedLayers: { layerId: number; qtyConsumed: number; unitCost: number }[] = []
    const ledgerRefs: number[] = []

    try {
      for (const line of t.lines) {
        const chunks = consumeFifo(line.itemId, t.srcWarehouseId, line.qty)
        line.dispatchedChunks = chunks
        for (const chunk of chunks) {
          const ledger = postLedgerEntry({
            itemId: line.itemId,
            warehouseId: t.srcWarehouseId,
            binId: line.srcBinId,
            batchId: line.batchId,
            userId: t.createdBy,
            direction: "Out",
            qtyBase: chunk.qtyConsumed,
            unitCost: chunk.unitCost,
            sourceDocType: "Transfer",
            sourceDocId: t.transferId,
          })
          consumedLayers.push(chunk)
          ledgerRefs.push(ledger.ledgerId)
        }
      }
    } catch (err) {
      return Promise.reject(err)
    }

    t.status = "InTransit"
    return mockDelay({ transferId: t.transferId, status: t.status, consumedLayers, ledgerRefs })
  },

  receive(transferId: number, lines: ReceiveTransferLineInput[]): Promise<ReceiveTransferResponse> {
    const t = mockStockTransfers.find((x) => x.transferId === transferId)
    if (!t) return Promise.reject(new Error(`Mock transfer ${transferId} not found`))
    if (t.status !== "InTransit") return Promise.reject(new Error(`${t.docNo} is not in transit.`))

    const createdLayers: { layerId: number; warehouseId: number; qtyReceived: number; unitCost: number }[] = []
    const ledgerRefs: number[] = []

    for (const input of lines) {
      const line = t.lines.find((l) => l.transferLineId === input.transferLineId)
      if (!line) continue
      // Cost-preserving (FR-STK-06): one destination layer per dispatched chunk, at its exact source cost.
      for (const chunk of line.dispatchedChunks) {
        const { layer, ledger } = receiveLayer({
          itemId: line.itemId,
          warehouseId: t.destWarehouseId,
          binId: line.destBinId,
          batchId: line.batchId,
          qty: chunk.qtyConsumed,
          unitCost: chunk.unitCost,
          userId: t.createdBy,
          sourceDocType: "Transfer",
          sourceDocId: t.transferId,
        })
        createdLayers.push({
          layerId: layer.layerId,
          warehouseId: layer.warehouseId,
          qtyReceived: layer.qtyReceived,
          unitCost: layer.unitCost,
        })
        ledgerRefs.push(ledger.ledgerId)
      }
    }

    t.status = "Received"
    return mockDelay({ transferId: t.transferId, status: t.status, createdLayers, ledgerRefs })
  },
}
