import { EntityManager, getManager, In } from 'typeorm'
import { Domain } from '@things-factory/shell'
import { ApplicationType, User } from '@things-factory/auth-base'
import { Account } from '@things-factory/integration-accounting'
import {
  DeliveryOrder,
  ReleaseGood,
  OrderInventory,
  OrderProduct,
  OrderTote,
  PowrupController,
  OrderPackage,
  ORDER_STATUS,
  ORDER_INVENTORY_STATUS
} from '@things-factory/sales-base'
import { Worksheet, WorksheetDetail, WORKSHEET_STATUS, WORKSHEET_TYPE, XilnexController } from '@things-factory/worksheet-base'
import { getTmsService } from '@things-factory/integration-lmd'
import { Powrup } from '@things-factory/integration-powrup'
import { Setting } from '@things-factory/setting-base'
import { webhookHandler, WebhookEventsEnum } from '@things-factory/integration-base'

function parseNonNegativeInt(value: any): number {
  const n = parseInt(value ?? '0', 10)
  return Number.isFinite(n) ? Math.max(0, n) : 0
}

export const completeLoad = {
  async completeLoad(_: any, { orderId, worksheetId }, context: any) {
    const { domain, tx, user }: { domain: Domain; tx: EntityManager; user: User } = context.state

    return completeLoading(domain, tx, user, false, orderId, worksheetId)
  },

  async completeLoadPartially(_: any, { orderId, worksheetId }, context: any) {
    const { domain, tx, user }: { domain: Domain; tx: EntityManager; user: User } = context.state

    return completeLoading(domain, tx, user, true, orderId, worksheetId)
  }
}

async function completeLoading(
  domain: Domain,
  tx: EntityManager,
  user: User,
  partial: Boolean,
  orderId: String,
  worksheetId: String
) {
  // Fetch release order - optimized to reduce DB locks (14 relations → 4 relations)
  // - orderProducts re-fetched at line 200 with fresh relations
  // - deliveryOrders re-fetched at line 109
  // - Only load essential relations needed before those re-fetches
  console.time('completeLoading - fetch releaseGood')
  const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
    where: {
      name: orderId,
      domain: domain,
      status: In([ORDER_STATUS.LOADING, ORDER_STATUS.PROCESSING, ORDER_STATUS.PARTIAL_PROCESSING])
    },
    relations: ['bizplace', 'bizplace.domain', 'orderInventories', 'lastMileDelivery']
  })

  console.timeEnd('completeLoading - fetch releaseGood')
  if (!releaseGood) {
    throw new Error('Release good not found or not in a valid status')
  }
  const orderInventories: OrderInventory[] = releaseGood.orderInventories

  const worksheet: Worksheet = await tx.getRepository(Worksheet).findOne({
    where: {
      id: worksheetId,
      domain: domain,
      status: In([WORKSHEET_STATUS.EXECUTING, WORKSHEET_STATUS.PARTIAL_EXECUTING]),
      type: WORKSHEET_TYPE.LOADING
    },
    relations: ['worksheetDetails']
  })
  if (!worksheet) {
    throw new Error('Loading worksheet not found or not in a valid status')
  }

  const currentTime = new Date()

  //complete load
  if (!partial) {
    //validate all order inventories are loaded
    if (
      orderInventories.some(
        oi =>
          oi.status !== ORDER_INVENTORY_STATUS.LOADED &&
          oi.status !== ORDER_INVENTORY_STATUS.RETURNING &&
          oi.status !== ORDER_INVENTORY_STATUS.TERMINATED &&
          oi.status !== ORDER_INVENTORY_STATUS.MISSING
      )
    ) {
      throw new Error('Item is not completely loaded')
    }

    //update ro transport option depending on do types
    let deliveryOrders: DeliveryOrder[] = await tx.getRepository(DeliveryOrder).find({
      where: { releaseGood: releaseGood, domain: domain },
      relations: ['releaseGood']
    })

    if (deliveryOrders.length > 0) {
      let ownTransport = 0
      let warehouseTransport = 0
      let ownTransportFlag: Boolean

      for (let i = 0; i < deliveryOrders.length; i++) {
        let deliveryOrder = deliveryOrders[i]

        deliveryOrder.ownCollection ? ownTransport++ : warehouseTransport++
      }
      ownTransportFlag = ownTransport > warehouseTransport
      releaseGood.ownTransport = ownTransportFlag
    }

    // If minimum seal is 0 (or invalid/negative), auto-close any open totes for this order.
    // This supports the "0 seal required" scenario without blocking tote usage during QC/loading.
    try {
      let sealNoSetting: Setting = await tx.getRepository(Setting).findOne({
        where: {
          domain: (releaseGood as any).bizplace?.domain,
          name: 'minimum-seal-number'
        }
      })

      if (!sealNoSetting) {
        sealNoSetting = await tx.getRepository(Setting).findOne({
          where: {
            domain,
            name: 'minimum-seal-number'
          }
        })
      }

      const minimumSealRequired = parseNonNegativeInt(sealNoSetting?.value)
      if (minimumSealRequired <= 0) {
        await tx
          .getRepository(OrderTote)
          .createQueryBuilder()
          .update(OrderTote)
          .set({ closedDate: currentTime, updater: user })
          .where('release_good_id = :releaseGoodId', { releaseGoodId: releaseGood.id })
          .andWhere('closed_date IS NULL')
          .execute()
      }
    } catch (e) {
      // do not fail completion if tote auto-close fails
      console.error('Failed to auto-close order totes:', e)
    }

    await updateOrderInventories(tx, orderInventories, ORDER_INVENTORY_STATUS.TERMINATED, user, currentTime)
    await updateWorksheet(tx, worksheet, WORKSHEET_STATUS.DONE, user, currentTime)
    webhookHandler(releaseGood, releaseGood.bizplace, WebhookEventsEnum.LoadingCompleted)

    await updateReleaseGood(tx, releaseGood, ORDER_STATUS.DONE, user, currentTime)

    if (
      releaseGood?.lastMileDelivery?.isTms &&
      deliveryOrders.length > 1 &&
      releaseGood?.lastMileDelivery?.status === 'active'
    ) {
      const tmsService = getTmsService(releaseGood.lastMileDelivery.platform)

      tmsService.createAndUpdateShipment(releaseGood, deliveryOrders)
    }

    const orderSource: string = releaseGood.source
    switch (orderSource) {
      case ApplicationType.XILNEX:
        const xilnex: Account = await tx.getRepository(Account).findOne({
          where: { domain: releaseGood.bizplace.domain, status: 'active', platform: 'xilnex' },
          relations: ['domain']
        })

        if (xilnex) {
          const xilnexCtrl: XilnexController = new XilnexController(tx, domain, user)
          await xilnexCtrl.deliveryTransferNote(tx, xilnex, releaseGood.id)
        }
        break

      case ApplicationType.POWRUP:
        const powrup: Powrup = await tx.getRepository(Powrup).findOne({ active: true, platform: 'POWRUP' })
        const rtsTriggerLevel: Setting = await tx
          .getRepository(Setting)
          .findOne({
            where: { domain, category: 'id-rule', name: 'rts-trigger-level' }
          })
          .then((setting: Setting) => {
            return parseInt(setting?.value || 0)
          })

        if (powrup && rtsTriggerLevel === 3) {
          getManager().transaction(async txMgr => {
            const powrupController: PowrupController = new PowrupController()

            const hasPackage =
              (await txMgr.getRepository(OrderPackage).count({ where: { releaseGood: { id: releaseGood.id } } })) > 0

            if (!hasPackage && !releaseGood.marketPackCallSuccess) {
              const orderProducts = await txMgr.getRepository(OrderProduct).find({
                where: { releaseGood: { id: releaseGood.id } },
                relations: ['product', 'productDetail']
              })

              await powrupController.packOrder(powrup, { ...releaseGood, orderProducts }, domain, user, txMgr)
            }

            await powrupController.initiateShipment(powrup, releaseGood, domain, user, txMgr)
          })
        }
        break

      default:
        break
    }
  } else {
    await updateOrderInventories(tx, orderInventories, ORDER_INVENTORY_STATUS.TERMINATED, user, currentTime)
    await updateWorksheet(tx, worksheet, WORKSHEET_STATUS.PARTIAL_EXECUTING, user, currentTime)
    await updateReleaseGood(tx, releaseGood, ORDER_STATUS.PARTIAL_PROCESSING, user, currentTime)
  }
}

async function updateOrderInventories(tx, orderInventories, status, user, currentTime) {
  for (const oi of orderInventories) {
    if (oi.status === ORDER_INVENTORY_STATUS.LOADED) {
      await tx.getRepository(OrderInventory).update(oi.id, {
        status,
        updater: user,
        updatedAt: currentTime
      })
    }
  }
}

async function updateWorksheet(tx, worksheet, status, user, currentTime) {
  await tx.getRepository(Worksheet).update(worksheet.id, {
    status,
    updater: user,
    updatedAt: new Date(),
    endedAt: status === WORKSHEET_STATUS.DONE ? new Date() : null
  })
}

async function updateReleaseGood(tx, releaseGood, status, user, currentTime) {
  await tx.getRepository(ReleaseGood).update(releaseGood.id, {
    status,
    ownTransport: releaseGood.ownTransport,
    updater: user,
    updatedAt: new Date()
  })
  if (status === ORDER_STATUS.DONE) {
    webhookHandler(releaseGood, releaseGood.bizplace, WebhookEventsEnum.ReleaseOrderCompleted)
  }
}
