import { EntityManager, In } from 'typeorm'
import { v4 as uuidv4 } from 'uuid'

import { Domain } from '@things-factory/shell'
import { User } from '@things-factory/auth-base'
import {
  ReleaseGood,
  DeliveryOrderPatch,
  generateDeliveryOrder,
  OrderInventory,
  ORDER_INVENTORY_STATUS,
  OrderNoGenerator,
  WorksheetDetailInfo
} from '@things-factory/sales-base'
import { Worksheet, WorksheetDetail, WORKSHEET_TYPE, WORKSHEET_STATUS } from '@things-factory/worksheet-base'
import { generateInventoryHistory, Inventory, INVENTORY_TRANSACTION_TYPE } from '@things-factory/warehouse-base'

export const loadByQty = {
  async loadByQty(_: any, { worksheetDetailPatch }, context: any) {
    try {
      const { domain, user, tx }: { domain: Domain; user: User; tx: EntityManager } = context.state
      console.time('load')
      const updatedWorksheetDetails = worksheetDetailPatch.orderInventories
      const releaseGoodNo = worksheetDetailPatch.releaseGoodNo
      const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
        where: { domain, name: releaseGoodNo },
        relations: ['bizplace', 'orderInventories']
      })

      if (!releaseGood) {
        throw new Error('Release good not found')
      }

      const worksheet: Worksheet = await tx.getRepository(Worksheet).findOne({
        where: {
          domain,
          releaseGood,
          type: WORKSHEET_TYPE.LOADING,
          status: In([
            WORKSHEET_STATUS.EXECUTING,
            WORKSHEET_STATUS.PARTIAL_EXECUTING
          ])
        },
        relations: ['worksheetDetails', 'worksheetDetails.targetInventory', 'worksheetDetails.domain']
      })

      if (!worksheet) {
        throw new Error('No loading worksheet found for order ' + releaseGoodNo)
      }

      const oiIds = worksheet.worksheetDetails.map((wsd: WorksheetDetail) => wsd.targetInventory.id)
      const orderInventories: OrderInventory[] = await tx.getRepository(OrderInventory).find({
        where: { id: In(oiIds) },
        relations: [
          'inventory',
          'domain',
          'bizplace',
          'releaseGood',
          'product',
          'productDetail',
          'binLocation',
          'orderProduct'
        ]
      })

      const updatedOrderInventories: OrderInventory[] = []
      for (const wsd of updatedWorksheetDetails) {
        // Check if this is combined data with multiple order inventory references
        // (from bin scan flow with combineSameProductsWithAllRefs)
        if (wsd.allRelatedOrderInvs && wsd.allRelatedOrderInvs.length > 0) {
          // Combined mode: distribute loadedQty across multiple order inventories (FIFO)
          let remainingLoadQty = wsd.loadedQty

          for (const ref of wsd.allRelatedOrderInvs) {
            if (remainingLoadQty <= 0) break

            let orderInventory = orderInventories.find(oi => oi.id === ref.id)
            if (!orderInventory) continue

            const foundWorksheetDetail = worksheet.worksheetDetails.find(
              foundWsd => foundWsd.targetInventory.id === orderInventory.id
            )
            if (!foundWorksheetDetail) continue

            // Skip if already loaded or DO generated
            if (orderInventory.status === ORDER_INVENTORY_STATUS.LOADED || orderInventory.deliveryOrderId !== null) {
              continue
            }

            // Calculate how much to load from this order inventory
            const availableQty = orderInventory.pickedQty
            const qtyToLoad = Math.min(remainingLoadQty, availableQty)
            remainingLoadQty -= qtyToLoad

            if (qtyToLoad === orderInventory.releaseQty) {
              // Full load - mark as LOADED
              orderInventory = {
                ...orderInventory,
                status: ORDER_INVENTORY_STATUS.LOADED,
                updater: user,
                updatedAt: new Date()
              }

              await tx.getRepository(WorksheetDetail).update(foundWorksheetDetail.id, {
                status: WORKSHEET_STATUS.DONE,
                updater: user,
                updatedAt: new Date()
              })
            } else if (qtyToLoad < orderInventory.releaseQty && qtyToLoad > 0) {
              // Partial load - split order inventory
              const remainingOiQty = orderInventory.releaseQty - qtyToLoad
              const newOrderInventory = tx.getRepository(OrderInventory).create({
                ...orderInventory,
                id: undefined,
                name: OrderNoGenerator.orderInventory(),
                releaseQty: remainingOiQty,
                releaseUomValue: (orderInventory.releaseUomValue / orderInventory.releaseQty) * remainingOiQty,
                pickedQty: orderInventory.pickedQty - qtyToLoad,
                sortedQty: orderInventory.sortedQty === 0 ? 0 : orderInventory.sortedQty - qtyToLoad,
                status: orderInventory.status,
                updater: user,
                updatedAt: new Date()
              })

              await tx.getRepository(OrderInventory).save(newOrderInventory)

              const newWorksheetDetail = tx.getRepository(WorksheetDetail).create({
                ...foundWorksheetDetail,
                id: undefined,
                name: `LOAD-DETAIL-` + uuidv4(),
                worksheet: worksheet,
                targetInventory: newOrderInventory,
                status: WORKSHEET_STATUS.EXECUTING,
                updater: user,
                updatedAt: new Date()
              })

              await tx.getRepository(WorksheetDetail).save(newWorksheetDetail)

              orderInventory = {
                ...orderInventory,
                status: ORDER_INVENTORY_STATUS.LOADED,
                releaseUomValue: (orderInventory.releaseUomValue / orderInventory.releaseQty) * qtyToLoad,
                releaseQty: qtyToLoad,
                pickedQty: qtyToLoad,
                sortedQty: qtyToLoad,
                updater: user,
                updatedAt: new Date()
              }

              await tx.getRepository(WorksheetDetail).update(foundWorksheetDetail.id, {
                status: WORKSHEET_STATUS.DONE,
                updater: user,
                updatedAt: new Date()
              })
            } else {
              // qtyToLoad is 0, skip this order inventory
              continue
            }

            const inventory = await tx.getRepository(Inventory).findOne({
              where: { id: orderInventory.inventoryId }
            })

            await generateInventoryHistory(inventory, releaseGood, INVENTORY_TRANSACTION_TYPE.LOADING, 0, 0, user, tx)
            updatedOrderInventories.push(orderInventory)
          }

          // Validate that all requested quantity was loaded
          if (remainingLoadQty > 0) {
            console.warn(`Could not fully distribute loadedQty for SKU ${wsd.productSku}. Remaining: ${remainingLoadQty}`)
          }
        } else {
          // Standard mode: single order inventory reference
          // Support both:
          // 1. Match by worksheetDetail name (when using LOADING_WORKSHEET_V2 query)
          // 2. Match by relatedOrderInv.id (when name is null)
          let foundWorksheetDetail: WorksheetDetail
          let orderInventory: OrderInventory

          if (wsd.name) {
            foundWorksheetDetail = worksheet.worksheetDetails.find(foundWsd => foundWsd.name === wsd.name)
            if (foundWorksheetDetail) {
              orderInventory = orderInventories.find(oi => oi.id === foundWorksheetDetail.targetInventory.id)
            }
          }

          if (!foundWorksheetDetail && wsd.relatedOrderInv?.id) {
            orderInventory = orderInventories.find(oi => oi.id === wsd.relatedOrderInv.id)
            if (orderInventory) {
              foundWorksheetDetail = worksheet.worksheetDetails.find(
                foundWsd => foundWsd.targetInventory.id === orderInventory.id
              )
            }
          }

          if (!foundWorksheetDetail || !orderInventory) {
            console.warn(`Could not find worksheet detail or order inventory for item: ${JSON.stringify({ name: wsd.name, relatedOrderInvId: wsd.relatedOrderInv?.id, productSku: wsd.productSku })}`)
            continue
          }

          if (orderInventory.status === ORDER_INVENTORY_STATUS.LOADED || orderInventory.deliveryOrderId !== null) {
            continue
          }

          if (wsd.loadedQty > orderInventory.pickedQty) {
            throw new Error('Load qty is more than picked qty for sku ' + wsd.productSku)
          } else if (wsd.loadedQty == orderInventory.releaseQty) {
            orderInventory = {
              ...orderInventory,
              status: ORDER_INVENTORY_STATUS.LOADED,
              updater: user,
              updatedAt: new Date()
            }

            await tx.getRepository(WorksheetDetail).update(foundWorksheetDetail.id, {
              status: WORKSHEET_STATUS.DONE,
              updater: user,
              updatedAt: new Date()
            })
          } else if (wsd.loadedQty < orderInventory.releaseQty) {
            const remainingQty = orderInventory.releaseQty - wsd.loadedQty
            const newOrderInventory = tx.getRepository(OrderInventory).create({
              ...orderInventory,
              id: undefined,
              name: OrderNoGenerator.orderInventory(),
              releaseQty: remainingQty,
              releaseUomValue: (orderInventory.releaseUomValue / orderInventory.releaseQty) * remainingQty,
              pickedQty: orderInventory.pickedQty - wsd.loadedQty,
              sortedQty: orderInventory.sortedQty === 0 ? 0 : orderInventory.sortedQty - wsd.loadedQty,
              status: orderInventory.status,
              updater: user,
              updatedAt: new Date()
            })

            await tx.getRepository(OrderInventory).save(newOrderInventory)

            const newWorksheetDetail = tx.getRepository(WorksheetDetail).create({
              ...foundWorksheetDetail,
              id: undefined,
              name: `LOAD-DETAIL-` + uuidv4(),
              worksheet: worksheet,
              targetInventory: newOrderInventory,
              status: WORKSHEET_STATUS.EXECUTING,
              updater: user,
              updatedAt: new Date()
            })

            await tx.getRepository(WorksheetDetail).save(newWorksheetDetail)

            orderInventory = {
              ...orderInventory,
              status: ORDER_INVENTORY_STATUS.LOADED,
              releaseUomValue: (orderInventory.releaseUomValue / orderInventory.releaseQty) * wsd.loadedQty,
              releaseQty: wsd.loadedQty,
              pickedQty: wsd.loadedQty,
              sortedQty: wsd.loadedQty,
              updater: user,
              updatedAt: new Date()
            }

            await tx.getRepository(WorksheetDetail).update(foundWorksheetDetail.id, {
              status: WORKSHEET_STATUS.DONE,
              updater: user,
              updatedAt: new Date()
            })
          }

          const inventory = await tx.getRepository(Inventory).findOne({
            where: { id: orderInventory.inventoryId }
          })

          await generateInventoryHistory(inventory, releaseGood, INVENTORY_TRANSACTION_TYPE.LOADING, 0, 0, user, tx)
          updatedOrderInventories.push(orderInventory)
        }
      }

      if (updatedOrderInventories.length < 1) {
        return false
      } else {
        const deliveryOrder: DeliveryOrderPatch = {
          truckNo: worksheetDetailPatch?.vehicleNo,
          ownCollection: worksheetDetailPatch?.ownCollection,
          totalPalletQty: worksheetDetailPatch?.palletQty,
          contactPointRefId: releaseGood.deliverToId || releaseGood.deliverTo?.id || null
        }

        const createdDo = await generateDeliveryOrder(
          deliveryOrder,
          updatedOrderInventories,
          releaseGood.bizplace,
          releaseGood,
          domain,
          user,
          tx
        )
        return true
      }
    } catch (e) {
      throw e
    }
  }
}
