package com.ffii.fpsms.modules.stock.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.stock.entity.projection.QrCodeInfo
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo
import com.ffii.fpsms.modules.stock.web.model.PurchaseStockInAlertRow
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.util.Optional
import java.time.LocalDate
import java.time.LocalDateTime
import org.springframework.data.repository.query.Param

interface JobOrderFirstLotNoProjection {
    val jobOrderId: Long
    val lotNo: String?
}


@Repository
interface StockInLineRepository : AbstractRepository<StockInLine, Long> {
    fun findAllStockInLineInfoByStockInIdAndDeletedFalse(stockInId: Long): List<StockInLineInfo>
    fun findStockInLineInfoByIdAndDeletedFalse(id: Long): StockInLineInfo
    fun findAllByStockInIdAndDeletedFalse(stockInId: Long): List<StockInLine>
    fun findStockInLineInfoByIdInAndDeletedFalse(id: List<Long>): List<StockInLineInfo>
    fun findStockInLineInfoByPurchaseOrderLineIdAndStatusNotAndDeletedFalse(purchaseOrderLineId: Long, status: String): List<StockInLineInfo>
    fun findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId: Long): List<StockInLineInfo>
    fun findStockInLineInfoByIdAndStatusAndDeletedFalse(id: Long, status: String): Optional<StockInLineInfo>
    fun findAllStockInLineInfoByPurchaseOrderIdAndStatusStartsWithAndDeletedFalse(purchaseOrderId: Long, status: String): List<Optional<StockInLineInfo>>
    fun findAllByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: Long): Optional<List<StockInLine>>
    fun findStockInLineInfoByInventoryLotLineId(inventoryLotLineId: Long): Optional<StockInLineInfo>
    fun findStockInLineInfoById(id: Long): Optional<StockInLineInfo>

    @Query("SELECT sil FROM StockInLine sil WHERE sil.item.id = :itemId AND sil.deleted = false")
    fun findAllByItemIdAndDeletedFalse(itemId: Long): List<StockInLine>

    @Query("""
    SELECT sil FROM StockInLine sil 
    WHERE sil.receiptDate IS NOT NULL 
    AND DATE(sil.receiptDate) = :date 
    AND sil.deleted = false
""")
fun findByReceiptDateAndDeletedFalse(date: LocalDate): List<StockInLine>
@Query("""
    SELECT sil FROM StockInLine sil 
    WHERE sil.deleted = false
    AND (:type IS NULL OR sil.type IS NOT NULL)
    AND (:startDate IS NULL OR DATE(sil.created) >= :startDate)
    AND (:endDate IS NULL OR DATE(sil.created) <= :endDate)
    AND (:itemCode IS NULL OR sil.item.code LIKE CONCAT('%', :itemCode, '%'))
    AND (:itemName IS NULL OR sil.item.name LIKE CONCAT('%', :itemName, '%'))
    AND (:type IS NULL OR sil.type = :type)
    ORDER BY sil.created DESC
""")
fun searchStockInLines(
    @Param("startDate") startDate: LocalDate?,
    @Param("endDate") endDate: LocalDate?,
    @Param("itemCode") itemCode: String?,
    @Param("itemName") itemName: String?,
    @Param("type") type: String?
): List<StockInLine>
//AND sil.type IS NOT NULL
@Query("SELECT sil FROM StockInLine sil WHERE sil.item.id IN :itemIds AND sil.deleted = false")
fun findAllByItemIdInAndDeletedFalse(itemIds: List<Long>): List<StockInLine>
fun findFirstByJobOrder_IdAndDeletedFalse(jobOrderId: Long): StockInLine?

    /** Loads stock-in lines with item and purchaseOrderLine.item so bDesc/bDesc_en (item name) are available for M18 GRN. */
    @Query("""
        SELECT DISTINCT sil FROM StockInLine sil
        LEFT JOIN FETCH sil.item
        LEFT JOIN FETCH sil.purchaseOrderLine pol
        LEFT JOIN FETCH pol.item
        WHERE sil.purchaseOrder.id = :purchaseOrderId AND sil.deleted = false
        ORDER BY sil.id
    """)
    fun findAllByPurchaseOrderIdAndDeletedFalseWithItemNames(@Param("purchaseOrderId") purchaseOrderId: Long): List<StockInLine>

    @Query("""
        SELECT DISTINCT sil FROM StockInLine sil
        LEFT JOIN FETCH sil.item
        LEFT JOIN FETCH sil.purchaseOrderLine pol
        LEFT JOIN FETCH pol.item
        WHERE sil.purchaseOrder.id = :purchaseOrderId
          AND sil.deleted = false
          AND sil.status = 'completed'
        ORDER BY sil.id
    """)
    fun findCompletedByPurchaseOrderIdAndDeletedFalseWithItemNames(@Param("purchaseOrderId") purchaseOrderId: Long): List<StockInLine>

    /** Completed stock-in lines for this PO whose calendar receipt day matches (one M18 GRN per delivery date / DN batch). */
    @Query(
        """
        SELECT DISTINCT sil FROM StockInLine sil
        LEFT JOIN FETCH sil.item
        LEFT JOIN FETCH sil.purchaseOrderLine pol
        LEFT JOIN FETCH pol.item
        WHERE sil.purchaseOrder.id = :purchaseOrderId
          AND sil.deleted = false
          AND sil.status = 'completed'
          AND sil.receiptDate IS NOT NULL
          AND DATE(sil.receiptDate) = :receiptDate
        ORDER BY sil.id
        """
    )
    fun findCompletedByPurchaseOrderIdAndReceiptDateAndDeletedFalseWithItemNames(
        @Param("purchaseOrderId") purchaseOrderId: Long,
        @Param("receiptDate") receiptDate: LocalDate,
    ): List<StockInLine>

    @Query("""
        SELECT sil FROM StockInLine sil
        WHERE sil.receiptDate IS NOT NULL
        AND DATE(sil.receiptDate) = :receiptDate
        AND sil.status = 'completed'
        AND sil.purchaseOrder IS NOT NULL
        AND sil.deleted = false
        ORDER BY sil.purchaseOrder.id
    """)
    fun findCompletedDnByReceiptDate(@Param("receiptDate") receiptDate: LocalDate): List<StockInLine>

    @Query("""
    select max(sil.lotNo)
    from StockInLine sil
    where sil.deleted = false
      and sil.lotNo like concat(:prefix, '%')
""")
fun findLatestLotNoByPrefix(@Param("prefix") prefix: String): String?

    /**
     * PO stock-in lines in pending / receiving, created on or after [since] (recent backlog).
     * ([StockInLineService] filters out lines whose POL already has put-away >= order qty in stock units.)
     */
    @Query(
        """
        SELECT new com.ffii.fpsms.modules.stock.web.model.PurchaseStockInAlertRow(
            sil.id,
            po.id,
            pol.id,
            po.code,
            sil.itemNo,
            item.name,
            sil.status,
            sil.created,
            sil.receiptDate,
            sil.lotNo
        )
        FROM StockInLine sil
        JOIN sil.purchaseOrderLine pol
        JOIN sil.purchaseOrder po
        JOIN sil.item item
        WHERE sil.deleted = false
          AND LOWER(COALESCE(sil.status, '')) IN ('pending', 'receiving')
          AND sil.created >= :since
        ORDER BY sil.created DESC
        """
    )
    fun listPurchaseStockInAlertsSince(
        @Param("since") since: LocalDateTime,
        pageable: Pageable,
    ): List<PurchaseStockInAlertRow>


    @Query(
        """
        SELECT sil FROM StockInLine sil
        WHERE sil.jobOrder.id IN :jobOrderIds AND sil.deleted = false
        ORDER BY sil.id ASC
        """
    )
    fun findAllByJobOrder_IdInAndDeletedFalse(@Param("jobOrderIds") jobOrderIds: List<Long>): List<StockInLine>

    @Query(
        """
        SELECT sil.jobOrderId AS jobOrderId, sil.lotNo AS lotNo
        FROM fpsmsdb.stock_in_line sil
        WHERE sil.deleted = 0
          AND sil.jobOrderId IN (:jobOrderIds)
          AND sil.id IN (
              SELECT MIN(s2.id)
              FROM fpsmsdb.stock_in_line s2
              WHERE s2.deleted = 0
                AND s2.jobOrderId IN (:jobOrderIds)
              GROUP BY s2.jobOrderId
          )
        """,
        nativeQuery = true
    )
    fun findFirstLotNoByJobOrderIds(@Param("jobOrderIds") jobOrderIds: List<Long>): List<JobOrderFirstLotNoProjection>
}