package com.ffii.fpsms.modules.stock.service

import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.stock.entity.*
import com.ffii.fpsms.modules.stock.web.model.SaveStockOutRequest
import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus
import com.ffii.fpsms.modules.stock.web.model.StockOutStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.io.IOException
import java.time.LocalDate
import java.time.LocalDateTime

@Service
open class StockOutService(
    private val jdbcDao: JdbcDao,
    private val stockOutRepository: StockOutRepository,
    private val stockOutLIneRepository: StockOutLIneRepository,
    private val stockOutLineService: StockOutLineService,
    private val stockLedgerRepository: StockLedgerRepository,
    ): AbstractBaseEntityService<StockOut, Long, StockOutRepository>(jdbcDao, stockOutRepository)  {
    @Throws(IOException::class)
    @Transactional
    // update record data
    open fun saveStockOut(request: SaveStockOutRequest): MessageResponse {
        val stockOut = if (request.id !== null) stockOutRepository.findById(request.id).orElseThrow() else StockOut()
        stockOut.apply {
            type = request.type
            deliveryOrderCode = request.deliveryOrderCode
            pickOrderCode = request.pickOrderCode
            consoCode = request.consoCode
            completeDate = request.completeDate
            handler = request.handler
            targetOutletId = request.targetOutletId
            remarks = request.remarks
        }
        val savedStockOut = stockOutRepository.saveAndFlush(stockOut)
        // only create stock out line, no update
        for (outLine in request.stockOutLine) {
            stockOutLineService.create(outLine)
        }
        return MessageResponse(
            id = savedStockOut.id,
            code = savedStockOut.pickOrderCode ?: savedStockOut.deliveryOrderCode ,
            name = savedStockOut.consoCode,
            type = savedStockOut.type,
            message = "save success",
            errorPosition = null
        )
    }
    @Throws(IOException::class)
    @Transactional
    open fun complete(request: SaveStockOutRequest): MessageResponse {
        if (request.id === null) {
            return MessageResponse(
                id = null,
                code = null,
                name = null,
                type = null,
                message = "id cannot be null",
                errorPosition = "id"
            )
        }
        /// checking pick quantity
        val stockOut = stockOutRepository.findById(request.id).orElseThrow()
        val allLines = stockOutLineService.findAllByStockOutId(request.id)
        val isBalanced = allLines.all{
            val status = it.status
            var sum = 0.0
            when (status) {
                StockOutLineStatus.PENDING.status -> sum += it.qty ?: 0.0;
                StockOutLineStatus.PICKED.status -> sum -= it.qty ?: 0.0;
            }
            sum == 0.0
        }
        if (!isBalanced) {
            return MessageResponse(
                id = request.id,
                code = stockOut.consoCode,
                name = stockOut.pickOrderCode ?: stockOut.deliveryOrderCode,
                type = stockOut.type,
                message = "there are items not picked",
                errorPosition = null
            )
        }
        // remove base record
        val baseLines = allLines.filter { stockOutLine -> stockOutLine.status === StockOutLineStatus.PENDING.status}
        // update pick record to complete
        val pickLines = allLines
            .filter { stockOutLine -> stockOutLine.status === StockOutLineStatus.PICKED.status}
            .map { stockOutLine ->
                stockOutLine.apply {
                    status = StockOutLineStatus.COMPLETE.status
                }
            }
        // write ledger
        val ledgers = pickLines.map {stockOutLine ->
            StockLedger().apply {
                this.stockOutLine = stockOutLine
                this.inventory = stockOutLine.inventory
                outQty = stockOutLine.qty
            }
        }
        stockLedgerRepository.saveAll(ledgers)
        stockOutLIneRepository.deleteAll(baseLines)
        stockOutLIneRepository.saveAll(pickLines)
        stockOut.apply {
            completeDate = LocalDateTime.now()
            status = StockOutStatus.COMPLETE.status
        }
        val savedStockOut = stockOutRepository.saveAndFlush(stockOut)
        return MessageResponse(
            id = savedStockOut.id,
            code = savedStockOut.consoCode,
            name = savedStockOut.pickOrderCode ?: savedStockOut.deliveryOrderCode,
            type = savedStockOut.type,
            message = "stock out completed",
            errorPosition = null
        )
    }
}