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

import { User } from '@things-factory/auth-base'
import { Domain } from '@things-factory/shell'
import { ReleaseGood, OrderInventory, ORDER_INVENTORY_STATUS, ORDER_STATUS } from '@things-factory/sales-base'
import { InventoryChange } from '@things-factory/warehouse-base'
import { Worksheet, WorksheetDetail, WORKSHEET_STATUS, WORKSHEET_TYPE } from '@things-factory/worksheet-base'
import { Setting } from '@things-factory/setting-base'
import { zonePickingWorksheet } from './zone-worksheet'

export const getZonePickingTask = {
  async getZonePickingTask(_: any, {}, context: any): Promise<any> {
    try {
      let worksheet: Worksheet
      let locationSortingRules: Setting
      await getConnection().transaction(async (tx: EntityManager) => {
        const { domain, user }: { domain: Domain; user: User } = context.state
        const assignee: User = await tx.getRepository(User).findOne(user.id, { relations: ['domains'] })
        if ((assignee?.domains || []).every((userDomain: Domain) => userDomain.id !== domain.id))
          throw new Error('Target user is not belong to current domain')

        if (!assignee) throw new Error('failed to find target user')

        let worksheetQb = await tx
          .getRepository(Worksheet)
          .createQueryBuilder('ws')
          .select('ws.id')
          .addSelect('ws.name')
          .addSelect('ws.status')
          .where(
            '(ws.domain_id = :domain_id AND ws.type = :type AND ws.assignee_id = :assignee AND (ws.status = :status1 OR ws.status = :status2))',
            {
              domain_id: domain.id,
              type: WORKSHEET_TYPE.ZONE_PICKING,
              assignee: assignee.id,
              status1: WORKSHEET_STATUS.EXECUTING,
              status2: WORKSHEET_STATUS.DEACTIVATED
            }
          )
        worksheet = await worksheetQb.getOne()

        if (!worksheet) {
          //find a worksheet and update assignee to user
          let updatedWorksheets = await getConnection().transaction(async (tx2: EntityManager) => {
            let x = await tx2.getRepository(Worksheet).query(
              `
              UPDATE worksheets ws SET 
                assignee_id = $2,
                updated_at = NOW(),
                updater_id = $2
              FROM (
                SELECT ws.id, ws.name, ws.status, ws.release_good_id FROM worksheets ws
                WHERE ws.domain_id = $1
                AND ws.type = 'ZONE_PICKING'
                AND ws.status IN ('EXECUTING', 'DEACTIVATED')
                AND ws.assignee_id IS NULL
                ORDER BY ws.created_at
              ) src where src.id = ws.id
              RETURNING ws.id;
            `,
              [domain.id, assignee.id]
            )

            return x[0]
          })
          if (updatedWorksheets.length == 0) throw new Error('no suitable worksheet found')

          //fetch updated ws
          worksheet = await tx
            .getRepository(Worksheet)
            .findOne({ where: { id: updatedWorksheets[0].id, assignee: assignee } })
        }

        //validate worksheet is assigned to user
        if (!worksheet) throw new Error('Please try again by clicking the "Get Task" button')

        if (worksheet.status == WORKSHEET_STATUS.DEACTIVATED) {
          await tx.getRepository(Worksheet).update(worksheet.id, {
            status: WORKSHEET_STATUS.EXECUTING,
            startedAt: new Date()
          })
          const worksheetDetails: WorksheetDetail[] = await tx.getRepository(WorksheetDetail).find({
            where: { worksheet: worksheet },
            relations: ['targetInventory']
          })

          await tx
            .getRepository(WorksheetDetail)
            .update(
              { worksheet: worksheet, status: WORKSHEET_STATUS.DEACTIVATED },
              { status: WORKSHEET_STATUS.EXECUTING }
            )

          const orderInventoryIds = worksheetDetails
            .map(detail => detail.targetInventory)
            .filter(oi => oi.status === ORDER_INVENTORY_STATUS.READY_TO_PICK)
            .map(oi => oi.id)

          if (orderInventoryIds.length > 0) {
            await tx
              .getRepository(OrderInventory)
              .update({ id: In(orderInventoryIds) }, { status: ORDER_INVENTORY_STATUS.PROCESSING })

            const releaseGoodIds = worksheetDetails
              .map(detail => detail.targetInventory.releaseGoodId)
              .filter((value, index, self) => self.indexOf(value) === index)

            if (releaseGoodIds.length > 0) {
              await tx
              .getRepository(ReleaseGood)
              .update({ id: In(releaseGoodIds), status: ORDER_STATUS.READY_TO_PICK }, { status: ORDER_STATUS.PROCESSING })
            }
          }
        }

        locationSortingRules = await tx.getRepository(Setting).findOne({
          where: { domain, name: 'rule-for-picking-product' }
        })
      })
      //call zonePickingWorksheet
      return zonePickingWorksheet.zonePickingWorksheet(
        _,
        { name: worksheet.name, locationSortingRules, filter: 'PICKING' },
        context
      )
    } catch (e) {
      throw e
    }
  }
}
