import { EntityManager } from 'typeorm'
import { In } from 'typeorm'

import { Domain } from '@things-factory/shell'
import { ProductDetail } from '@things-factory/product-base'
import { ReleaseGood, ORDER_INVENTORY_STATUS, ORDER_STATUS } from '@things-factory/sales-base'
import { Worksheet, WorksheetDetail, WORKSHEET_TYPE, WORKSHEET_STATUS, computeConversionString } from '@things-factory/worksheet-base'
import { Setting } from '@things-factory/setting-base'

const FILTER_TYPES = {
  QC: 'qc',
  LOADING: 'loading'
} as const

// Types
interface WorksheetInfo {
  worksheet: {
    id: string
    name: string
    type: string
    status: string
  }
  bizplaceName: string
  startedAt: string | null
  releaseGood: {
    id: string
    name: string
    status: string
    attentionCompany?: string
    attentionTo?: string
    deliveryAddress1?: string
    deliveryAddress2?: string
    refNo?: string
    refNo2?: string
    refNo3?: string
    remark?: string
    rtm?: string
  }
}

interface WorksheetDetailInfo {
  id: string
  name: string
  batchId: string
  releaseQty: number
  pickedQty: number
  sortedQty: number
  loadedQty: number
  status: string
  packingType: string
  packingSize: string
  conversion: string
  expirationDate?: string
  binLocation: {
    name?: string
  }
  productName?: string
  productSku?: string
  relatedOrderInv: {
    id?: string
    batchId?: string
    packingType?: string
    packedQty?: number
    releaseQty?: number
    product?: {
      id?: string
      sku?: string
      name?: string
      isInventoryDecimal?: boolean
    }
    productDetail?: {
      id?: string
      productBarcodes?: { gtin: string }[]
    }
    orderProduct?: {
      id?: string
    }
  }
}

interface LoadingWorksheetResponse {
  worksheetInfo: WorksheetInfo
  worksheetDetailInfos: WorksheetDetailInfo[]
}

// Helper functions
function formatDate(date: Date | null): string | null {
  if (!date) return null
  return new Date(date).toISOString().slice(0, 19).replace('T', ' ')
}

function shouldFilterBySortedQty(releaseGoodStatus: string, disableQC: boolean): boolean {
  return releaseGoodStatus === ORDER_STATUS.LOADING && !disableQC
}

function getSortedQty(releaseGoodStatus: string, disableQC: boolean, pickedQty: number, sortedQty: number): number {
  return releaseGoodStatus === ORDER_STATUS.LOADING && disableQC ? pickedQty : sortedQty
}

export const loadingWorksheetv2 = {
  async loadingWorksheetv2(_: any, { orderNo, filter = null }, context: any): Promise<LoadingWorksheetResponse> {
    const { domain, tx }: { domain: Domain; tx: EntityManager } = context.state

    if (process.env.NODE_ENV === 'development') {
      console.time('loadingWorksheetv2')
    }

    // Find release good
    const releaseGood: ReleaseGood = await tx.getRepository(ReleaseGood).findOne({
      where: { domain, name: orderNo }
    })

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

    // Find worksheet
    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: ['bizplace']
    })

    if (!worksheet) throw new Error('No loading worksheet found for order ' + orderNo)

    // Get QC setting
    const disableQcSetting: Setting = await tx.getRepository(Setting).findOne({
      where: {
        domain,
        name: 'disable-QC-in-loading'
      }
    })

    const disableQc = disableQcSetting?.value === 'true'

    // Build worksheet details query
    const qb = tx
      .getRepository(WorksheetDetail)
      .createQueryBuilder('worksheetDetail')
      .leftJoinAndSelect('worksheetDetail.targetInventory', 'targetInventory')
      .leftJoinAndSelect('targetInventory.binLocation', 'binLocation')
      .leftJoinAndSelect('targetInventory.product', 'product')
      .leftJoinAndSelect('targetInventory.productDetail', 'productDetail')
      .leftJoinAndSelect('productDetail.productBarcodes', 'productBarcodes')
      .leftJoinAndSelect('productDetail.childProductDetail', 'childProductDetail')
      .leftJoinAndSelect('productDetail.parentProductDetails', 'parentProductDetails')
      .leftJoin('targetInventory.inventory', 'inventory')
      .addSelect('inventory.expirationDate')
      .where('worksheetDetail.domain_id = :domainId', { domainId: domain.id })
      .andWhere('worksheetDetail.worksheet_id = :worksheetId', { worksheetId: worksheet.id })

    // Check if all items are deactivated
    const allWorksheetDetails: WorksheetDetail[] = await qb.getMany()
    if (allWorksheetDetails.every(wsd => wsd.status === WORKSHEET_STATUS.DEACTIVATED)) {
      throw new Error('Item in this RO is not ready to be loaded')
    }

    // Apply filters
    qb.andWhere('targetInventory.status NOT IN (:...excludeStatuses)', {
      excludeStatuses: [ORDER_INVENTORY_STATUS.TERMINATED, ORDER_INVENTORY_STATUS.RETURNING]
    })

    qb.andWhere('worksheetDetail.status IN (:...wsdStatus)', {
      wsdStatus: [WORKSHEET_STATUS.EXECUTING]
    })

    if (filter === FILTER_TYPES.QC) {
      qb.andWhere('targetInventory.status IN (:...loadingStatus)', {
        loadingStatus: [ORDER_INVENTORY_STATUS.SORTING, ORDER_INVENTORY_STATUS.LOADING]
      })
      qb.andWhere('targetInventory.sortedQty <> targetInventory.pickedQty')
    }

    if (filter === FILTER_TYPES.LOADING) {
      if (shouldFilterBySortedQty(releaseGood.status, disableQc)) {
        qb.andWhere('targetInventory.sortedQty > 0')
        // Exclude LOADED items - they should only appear in LOADED tab
        qb.andWhere('targetInventory.status <> :loadedStatus', {
          loadedStatus: ORDER_INVENTORY_STATUS.LOADED
        })
      } else {
        qb.andWhere('targetInventory.status IN (:...loadingStatus)', {
          loadingStatus: [ORDER_INVENTORY_STATUS.SORTING, ORDER_INVENTORY_STATUS.LOADING]
        })
      }
    }

    qb.orderBy('product.name', 'ASC')

    const worksheetDetails = await qb.getMany()

    if (process.env.NODE_ENV === 'development') {
      console.timeEnd('loadingWorksheetv2')
    }

    return await mapdata(worksheet, worksheetDetails, releaseGood, disableQc)
  }
}

async function mapdata(
  worksheet: Worksheet,
  worksheetDetails: WorksheetDetail[],
  releaseGood: ReleaseGood,
  disableQC: boolean
): Promise<LoadingWorksheetResponse> {
  const worksheetInfo: WorksheetInfo = {
    worksheet: {
      id: worksheet.id,
      name: worksheet.name,
      type: worksheet.type,
      status: worksheet.status
    },
    bizplaceName: worksheet.bizplace?.name,
    startedAt: formatDate(worksheet.startedAt),
    releaseGood: {
      id: releaseGood.id,
      name: releaseGood.name,
      status: releaseGood.status,
      attentionCompany: releaseGood?.attentionCompany,
      attentionTo: releaseGood?.attentionTo,
      deliveryAddress1: releaseGood?.deliveryAddress1,
      deliveryAddress2: releaseGood?.deliveryAddress2,
      refNo: releaseGood?.refNo,
      refNo2: releaseGood?.refNo2,
      refNo3: releaseGood?.refNo3,
      remark: releaseGood?.remark,
      rtm: releaseGood?.rtm
    }
  }

  const worksheetDetailInfos: WorksheetDetailInfo[] = worksheetDetails.map(wsd => ({
    id: wsd.id,
    name: wsd.name,
    batchId: wsd.targetInventory.batchId || '-',
    releaseQty: wsd.targetInventory.releaseQty,
    pickedQty: wsd.targetInventory.pickedQty,
    sortedQty: getSortedQty(
      releaseGood.status,
      disableQC,
      wsd.targetInventory.pickedQty,
      wsd.targetInventory.sortedQty
    ),
    loadedQty: wsd.targetInventory.loadedQty,
    status: wsd.status,
    packingType: wsd.targetInventory.packingType,
    packingSize: wsd.targetInventory.packingSize,
    conversion: computeConversionString(wsd.targetInventory.releaseQty, wsd.targetInventory.productDetail as ProductDetail),
    expirationDate: wsd.targetInventory?.inventory?.expirationDate,
    binLocation: {
      name: wsd.targetInventory.binLocation?.name
    },
    productName: wsd.targetInventory?.product?.name,
    productSku: wsd.targetInventory?.product?.sku,
    relatedOrderInv: {
      id: wsd.targetInventory?.id,
      batchId: wsd.targetInventory?.batchId || '-',
      packingType: wsd.targetInventory?.packingType,
      refWorksheetId: wsd.targetInventory?.refWorksheetId,
      packedQty: wsd.targetInventory?.packedQty || 0,
      releaseQty: wsd.targetInventory?.releaseQty,
      product: {
        id: wsd.targetInventory?.product?.id,
        sku: wsd.targetInventory?.product?.sku,
        name: wsd.targetInventory?.product?.name,
        isInventoryDecimal: wsd.targetInventory?.product?.isInventoryDecimal
      },
      productDetail: {
        id: wsd.targetInventory?.productDetail?.id,
        productBarcodes: (wsd.targetInventory?.productDetail?.productBarcodes || []).map((pb: { gtin: string }) => ({ gtin: pb.gtin }))
      },
      orderProduct: {
        id: wsd.targetInventory?.orderProductId
      }
    }
  }))

  return {
    worksheetInfo,
    worksheetDetailInfos
  }
}
