package com.ffii.fpsms.modules.jobOrder.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetailWithJsonString
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo
import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import java.time.LocalDateTime
import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus.*
@Repository
interface JobOrderRepository : AbstractRepository<JobOrder, Long> {
    @Query("""
        select jo.code from JobOrder jo where jo.code like :prefix% order by jo.code desc limit 1
    """)
    fun findLatestCodeByPrefix(prefix: String): String?

    fun findJobOrderInfoByCodeContainsAndBomNameContainsAndDeletedIsFalseOrderByIdDesc(code: String, bomName: String, pageable: Pageable): Page<JobOrderInfo>
    fun findByBom_Id(bomId: Long): List<JobOrder>
    @Query(
        nativeQuery = true,
        value = """
            with picked_lot_no as (
                select
                    pol.itemId,
                    po.joId,
                    json_arrayagg(
                        json_object(
                            'lotNo', il.lotNo,
                            'qty', sol.qty
        	            )
                    ) as pickedLotNo
                from pick_order po
                left join pick_order_line pol on pol.poId = po.id
                left join stock_out_line sol on sol.pickOrderLineId = pol.id and sol.status = 'completed'
                left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId
                left join inventory_lot il on il.id = ill.inventoryLotId
                where po.joId = :id and il.lotNo is not null
                group by pol.itemId, po.joId
            )
            select
                jo.id,
                jo.code,
                b.code as itemCode,
                b.name,
                jo.reqQty,
                jo.bomId,
                b.outputQtyUom as unit,
                uc2.udfudesc as uom,
                COALESCE(uc2.udfShortDesc, uc2.udfudesc) as shortUom,
                json_arrayagg(
                    json_object(
                        'id', jobm.id,
                        'code', i.code,
                        'name', i.name,
                        'pickedLotNo', pln.pickedLotNo,
                        'reqQty', jobm.reqQty,
                        'uom', uc.udfudesc,
                        'shortUom', uc.udfShortdesc,
                        'status', jobm.status
                    )
                ) as pickLines,
                jo.status
            from job_order jo
            left join bom b on b.id = jo.bomId
            left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true
            left join items bi on bi.id = b.itemId
            left join uom_conversion uc2 on uc2.id = iu.uomId
            left join job_order_bom_material jobm on jo.id = jobm.jobOrderId
            left join items i on i.id = jobm.itemId
            left join uom_conversion uc on uc.id = jobm.uomId
            left join picked_lot_no pln on pln.itemId = jobm.itemId and pln.joId = jo.id
            where jo.id = :id
            group by jo.id, uc2.udfudesc, uc2.udfShortDesc
            limit 1
        """
    )
    fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?;

    @Query(
        nativeQuery = true,
        value = """
            select
                jo.id,
                jo.code,
                b.name,
                jo.reqQty,
                    b.outputQtyUom as unit, 
                uc2.udfudesc as uom,
                json_arrayagg(
                    json_object(
                        'id', jobm.id,
                        'code', i.code,
                        'name', i.name,
                        'lotNo', il.lotNo,
                        'reqQty', jobm.reqQty,
                        'uom', uc.udfudesc,
                        'status', jobm.status
                    )
                ) as pickLines,
                jo.status
            from job_order jo
            left join bom b on b.id = jo.bomId
            left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true
            left join uom_conversion uc2 on uc2.id = iu.uomId
            left join job_order_bom_material jobm on jo.id = jobm.jobOrderId
            left join items i on i.id = jobm.itemId
            left join uom_conversion uc on uc.id = jobm.uomId
            left join stock_out_line sol on sol.id = jobm.stockOutLineId
            left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId
            left join inventory_lot il on il.id = ill.inventoryLotId
            where jo.code = :code
            group by jo.id, uc2.udfudesc
            limit 1
        """
    )
    fun findJobOrderDetailByCode(code: String): JobOrderDetailWithJsonString?;
    fun findAllByDeletedFalse(): List<JobOrder>

    fun findByDeletedFalseAndPlanStartBetweenOrderByIdAsc(planStartStart: LocalDateTime, planStartEnd: LocalDateTime): List<JobOrder>

    @Query(
        """
        SELECT jo FROM JobOrder jo
        WHERE jo.deleted = false
        AND jo.planStart >= :planStartFrom
        AND jo.planStart < :planStartToExclusive
        ORDER BY jo.id ASC
        """
    )
    fun findByDeletedFalseAndPlanStartFromBeforeExclusiveOrderByIdAsc(
        planStartFrom: LocalDateTime,
        planStartToExclusive: LocalDateTime,
    ): List<JobOrder>

    @Query(
        """
        SELECT DISTINCT jo FROM JobOrder jo
        JOIN jo.bom b
        JOIN b.bomProcesses bp
        JOIN bp.process p
        WHERE jo.deleted = false
          AND jo.planStart >= :planStartFrom
          AND jo.planStart < :planStartToExclusive
          AND p.name = :processName
        ORDER BY jo.id ASC
        """
    )
    fun findByDeletedFalseAndPlanStartBetweenAndBomProcessNameOrderByIdAsc(
        planStartFrom: LocalDateTime,
        planStartToExclusive: LocalDateTime,
        processName: String,
    ): List<JobOrder>

    @Query("""
    SELECT jo FROM JobOrder jo 
    WHERE jo.deleted = false 
    AND (:code IS NULL OR jo.code LIKE CONCAT('%', :code, '%'))
    AND (
        :bomName IS NULL
        OR jo.bom.name LIKE CONCAT('%', :bomName, '%')
        OR jo.bom.item.code LIKE CONCAT('%', :bomName, '%')
        OR jo.bom.item.name LIKE CONCAT('%', :bomName, '%')
    )
    AND (:planStartFrom IS NULL OR jo.planStart >= :planStartFrom)
    AND (:planStartTo IS NULL OR jo.planStart <= :planStartTo)
    AND (
        (:joSearchStatus = 'cancel' AND jo.isHidden = true)
        OR
        (:joSearchStatus = 'putawayed'
            AND (jo.isHidden = false OR jo.isHidden IS NULL)
            AND EXISTS (
                SELECT 1 FROM StockInLine sil
                WHERE sil.jobOrder = jo
                AND sil.deleted = false
                AND LOWER(sil.status) = 'completed'
            )
        )
        OR
        ((:joSearchStatus = 'all' OR :joSearchStatus = '')
            AND (jo.isHidden = false OR jo.isHidden IS NULL)
            AND NOT EXISTS (
                SELECT 1 FROM StockInLine sil
                WHERE sil.jobOrder = jo
                AND sil.deleted = false
                AND LOWER(sil.status) = 'completed'
            )
        )
        OR
        (:joSearchStatus NOT IN ('all', '', 'cancel', 'putawayed')
            AND (jo.isHidden = false OR jo.isHidden IS NULL)
            AND (
                (
                    :joSearchStatus = 'processing'
                    AND (
                        LOWER(jo.status) = 'processing'
                        OR EXISTS (
                            SELECT 1 FROM ProductProcess pp
                            WHERE pp.jobOrder = jo
                            AND pp.deleted = false
                            AND (LOWER(pp.status) = 'in_progress' OR LOWER(pp.status) = 'processing')
                        )
                    )
                )
                OR
                (
                    :joSearchStatus = 'pending'
                    AND LOWER(jo.status) = 'pending'
                    AND NOT EXISTS (
                        SELECT 1 FROM ProductProcess pp
                        WHERE pp.jobOrder = jo
                        AND pp.deleted = false
                        AND (LOWER(pp.status) = 'in_progress' OR LOWER(pp.status) = 'processing')
                    )
                )
                OR
                (
                    :joSearchStatus = 'packaging'
                    AND EXISTS (
                        SELECT 1 FROM PickOrder po
                        JOIN po.pickOrderLines pol
                        JOIN pol.stockOutLines sol
                        WHERE po.jobOrder = jo
                        AND po.deleted = false
                        AND pol.deleted = false
                        AND sol.deleted = false
                        AND LOWER(sol.status) IN ('completed', 'partially_completed', 'partly_completed')
                    )
                )
                OR
                (
                    :joSearchStatus NOT IN ('processing', 'pending')
                    AND LOWER(jo.status) = :joSearchStatus
                )
            )
            AND NOT EXISTS (
                SELECT 1 FROM StockInLine sil
                WHERE sil.jobOrder = jo
                AND sil.deleted = false
                AND LOWER(sil.status) = 'completed'
            )
        )
    )
    ORDER BY jo.id DESC
""")
    fun findJobOrderInfoWithDateFilter(
        @Param("code") code: String?,
        @Param("bomName") bomName: String?,
        @Param("planStartFrom") planStartFrom: LocalDateTime?,
        @Param("planStartTo") planStartTo: LocalDateTime?,
        @Param("joSearchStatus") joSearchStatus: String,
        pageable: Pageable
    ): Page<JobOrderInfo>


    fun findAllByStatusIn(statuses: List<JobOrderStatus>): List<JobOrder>

    @Query(
        nativeQuery = true,
        value = """
            select
                jo.id,
                jo.code,
                b.name,
                jo.reqQty,
                    b.outputQtyUom as unit, 
                uc2.udfudesc as uom,
                json_arrayagg(
                    json_object(
                        'id', jobm.id,
                        'code', i.code,
                        'name', i.name,
                        'lotNo', il.lotNo,
                        'reqQty', jobm.reqQty,
                        'uom', uc.udfudesc,
                        'status', jobm.status
                    )
                ) as pickLines,
                jo.status
            from job_order jo
            left join bom b on b.id = jo.bomId
            left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true
            left join uom_conversion uc2 on uc2.id = iu.uomId
            left join job_order_bom_material jobm on jo.id = jobm.jobOrderId
            left join items i on i.id = jobm.itemId
            left join uom_conversion uc on uc.id = jobm.uomId
            left join stock_out_line sol on sol.id = jobm.stockOutLineId
            left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId
            left join inventory_lot il on il.id = ill.inventoryLotId
            where jo.prodScheduleLineId = :prodScheduleLineId
            and jo.actualEnd is null
            group by jo.id, uc2.udfudesc
            limit 1
        """
    )
    fun findJobOrderByProdScheduleLineId(prodScheduleLineId: Long): JobOrderDetailWithJsonString?;

    @Query(
        nativeQuery = true,
        value = """
            select
                jo.id,
                jo.code,
                b.name,
                jo.reqQty,
                    b.outputQtyUom as unit, 
                uc2.udfudesc as uom,
                json_arrayagg(
                    json_object(
                        'id', jobm.id,
                        'code', i.code,
                        'name', i.name,
                        'lotNo', il.lotNo,
                        'reqQty', jobm.reqQty,
                        'uom', uc.udfudesc,
                        'status', jobm.status
                    )
                ) as pickLines,
                jo.status
            from job_order jo
            left join bom b on b.id = jo.bomId
            left join item_uom iu on b.itemId = iu.itemId and iu.salesUnit = true
            left join uom_conversion uc2 on uc2.id = iu.uomId
            left join job_order_bom_material jobm on jo.id = jobm.jobOrderId
            left join items i on i.id = jobm.itemId
            left join uom_conversion uc on uc.id = jobm.uomId
            left join stock_out_line sol on sol.id = jobm.stockOutLineId
            left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId
            left join inventory_lot il on il.id = ill.inventoryLotId
            where b.itemId = :itemId
            and jo.actualEnd is null
            group by jo.id, uc2.udfudesc
            limit 1
        """
    )
    fun findJobOrderByItemId(itemId: Long): JobOrderDetailWithJsonString?;

}