package com.ffii.fpsms.modules.master.service

import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.master.entity.*
import org.springframework.stereotype.Service
import java.util.*
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.math.ceil

@Service
open class SchedulingService(
    private val jdbcDao: JdbcDao,
    private val uomConversionRepository: UomConversionRepository,
    private val itemService: ItemsService,
): AbstractBaseEntityService<UomConversion, Long, UomConversionRepository>(jdbcDao, uomConversionRepository)  {
    // do mapping with projection

    open fun convertToFinishedGoodList(): ArrayList<FinishedGood> {
        val roughScheduleList = itemService.getRoughScheduleList() // Retrieve the list from your existing method
        val finishedGoodList = ArrayList<FinishedGood>()

        for (item in roughScheduleList) {
            val finishedGood = FinishedGood(
                id = item["itemId"].toString().toLong(), // Cast to Long
                name = item["itemName"] as String?, // Cast to String
                receipeId = 0, // Assuming itemNo is a Long
                openBalance = 0.0, // Set default value or calculate if needed
                lastMonthAvgSalesCount = item["lastMonthAvgSalesCount"].toString().toDouble(), // Cast to Double
                fgProductionLimit = item["fgProductionLimit"].toString().toDouble() // Default production limit
            )
            finishedGoodList.add(finishedGood)
        }

        return finishedGoodList
    }

    open fun generateRoughScheduleByWeek(fgList : ArrayList<FinishedGood>):  HashMap<RoughScheduleObj, Double> {
        val roughScheduleOutput: HashMap<RoughScheduleObj, Double> = HashMap();

        for(fg:FinishedGood in fgList) {
            val roughScheduleRecord: RoughScheduleObj = roughScheduleByItem(fg);
            roughScheduleOutput.put(roughScheduleRecord, roughScheduleRecord.totalDifference);
        }

        //完成初步fg粗排後檢查調整
        for (i in 0 until 12) {
            var accProductionCount = 0L;

            // Sort by totalDifference before processing
            val sortedEntries = roughScheduleOutput.entries.sortedBy { it.value }

            //原排產
            for ((roughScheduleRecord, totalDifference) in sortedEntries) {
                if (accProductionCount + ceil(roughScheduleRecord.productionSchedule[i]).toLong() <= 22000) { //沒有超過每日產量
                    accProductionCount += ceil(roughScheduleRecord.productionSchedule[i]).toLong() //分開日子算

                    roughScheduleRecord.totalDifference += roughScheduleRecord.productionSchedule[i]
                    roughScheduleOutput[roughScheduleRecord] = roughScheduleRecord.totalDifference

                }
                else{//超過每日產量
                    var maxExtraProduction = Math.max((22000L - accProductionCount),0L);
                    roughScheduleRecord.totalForgoneCount = roughScheduleRecord.totalForgoneCount + roughScheduleRecord.productionSchedule[i] - maxExtraProduction;
                    roughScheduleRecord.productionSchedule[i] = maxExtraProduction.toDouble();
                    accProductionCount += maxExtraProduction;

                    //update close balance
                    roughScheduleRecord.closeBalance[i] =  roughScheduleRecord.closeBalance[i] - roughScheduleRecord.productionSchedule[i] + maxExtraProduction.toDouble();
                    for (j in i+1 until 12){
                        roughScheduleRecord.closeBalance[j] = roughScheduleRecord.closeBalance[j] - roughScheduleRecord.productionSchedule[i] + maxExtraProduction.toDouble();
                    }

                    // Update totalDifference in roughScheduleRecord
                    roughScheduleRecord.totalDifference += (roughScheduleRecord.productionSchedule[i] - maxExtraProduction.toDouble());
                    roughScheduleOutput[roughScheduleRecord] = roughScheduleRecord.totalDifference
                }

            }

            //解決拖欠的數量
            for ((roughScheduleRecord, totalDifference) in sortedEntries){
                if (accProductionCount + ceil(roughScheduleRecord.productionSchedule[i]).toLong() <= 22000) { //沒有超過每日產量
                    if(roughScheduleRecord.totalForgoneCount > 0.0){
                        if(accProductionCount + ceil(roughScheduleRecord.totalForgoneCount).toLong() <=22000){
                            //可以全做
                            roughScheduleRecord.productionSchedule[i] = roughScheduleRecord.productionSchedule[i] + ceil(roughScheduleRecord.totalForgoneCount).toLong();

                            accProductionCount += ceil(roughScheduleRecord.totalForgoneCount).toLong();

                            roughScheduleRecord.totalDifference += roughScheduleRecord.totalForgoneCount;
                            roughScheduleOutput[roughScheduleRecord] = roughScheduleRecord.totalDifference

                            //update close balance
                            for (j in i until 12){
                                roughScheduleRecord.closeBalance[j] = roughScheduleRecord.closeBalance[j] + roughScheduleRecord.totalForgoneCount;
                            }
                            roughScheduleRecord.totalForgoneCount = 0.0;
                        }
                        else{
                            //只能做部分
                            var maxExtraProduction = Math.max((22000L - accProductionCount), 0L);
                            roughScheduleRecord.totalForgoneCount = Math.max((roughScheduleRecord.totalForgoneCount - maxExtraProduction), 0.0);

                            roughScheduleRecord.productionSchedule[i] = roughScheduleRecord.productionSchedule[i] + maxExtraProduction;
                            accProductionCount += maxExtraProduction;
                            roughScheduleRecord.totalDifference += maxExtraProduction;

                            //update close balance
                            for (j in i until 12){
                                roughScheduleRecord.closeBalance[j] = roughScheduleRecord.closeBalance[j] + maxExtraProduction;
                            }
                        }
                    }

                }
            }

            //debug use
            for ((roughScheduleRecord, totalDifference) in sortedEntries){
                println("[RUN" + i + "][index:" + totalDifference + "] - " + roughScheduleRecord.fgDetail?.name + " - " + roughScheduleRecord.productionSchedule[i]);
            }
            println("Total Production Count $accProductionCount");
            println("========================================");

        }

        return roughScheduleOutput
    }

    open fun roughScheduleByItem(fg: FinishedGood): RoughScheduleObj{
        val minStockCount: Double = fg.lastMonthAvgSalesCount * 2
        val stockDifference: Double = minStockCount - fg.openBalance
        val productionSchedule: Array<Double> = Array<Double>(12, { 0.0 })

        //首日粗排產量 —＞ 取結果，或是fg本身生產上限,並不可小於0
        productionSchedule[0] = Math.max(
            ceil(Math.min(
                (stockDifference + fg.lastMonthAvgSalesCount),
                fg.fgProductionLimit
            )),
            0.0
        )

        val closeBalance: Array<Double> = Array<Double>(12, { 0.0 })
        closeBalance[0] = /*Math.floor(*/fg.openBalance + productionSchedule[0] - fg.lastMonthAvgSalesCount/*)*/;

        for (i in 1 until 12){
            //最少庫存-closeBalance 有可能小於0，所以必須要確保輸出>=0
            productionSchedule[i] =  Math.max(
                ceil(Math.min(
                    (minStockCount - closeBalance[i-1] + fg.lastMonthAvgSalesCount),
                    fg.fgProductionLimit
                )),
                0.0
            )
            closeBalance[i] = /*Math.floor(*/closeBalance[i-1] + productionSchedule[i] - fg.lastMonthAvgSalesCount/*)*/;
        }

        var totalProductionCount: Double = 0.0

        for (i in 5 until 12) {
            totalProductionCount = totalProductionCount + productionSchedule[i];
        }

        val totalDifference: Double = -(totalProductionCount - fg.openBalance);


        return RoughScheduleObj(
            fg,
            productionSchedule,
            closeBalance,
            totalDifference,
            totalProductionCount,
            0.0
        );
    }

    open class FinishedGood {
        //var fgDetail: FinishedGood? = null
        var id: Long? = null
        var name: String? = null
        var receipeId: Long? = null
        var openBalance: Double = 0.0
        var lastMonthAvgSalesCount: Double = 0.0
        var fgProductionLimit: Double = 22000.0

        constructor(
            id: Long?,
            name: String?,
            receipeId: Long?,
            openBalance: Double,
            lastMonthAvgSalesCount: Double,
            fgProductionLimit: Double
        ) {
            this.id = id
            this.name = name
            this.receipeId = receipeId
            this.openBalance = openBalance
            this.lastMonthAvgSalesCount = lastMonthAvgSalesCount
            this.fgProductionLimit = fgProductionLimit
        }
    }

    class RoughScheduleObj {
        var fgDetail: FinishedGood? = null
        var productionSchedule: Array<Double> = Array<Double>(12, { 0.0 })
        var closeBalance: Array<Double> = Array(12) { 0.0 };
        var totalDifference: Double = 0.0
        var totalProductionCount: Double = 0.0
        var totalForgoneCount: Double = 0.0

        constructor(
            fgDetail: FinishedGood?,
            productionSchedule: Array<Double>,
            closeBalance: Array<Double>,
            totalDifference: Double,
            totalProductionCount: Double,
            totalForgoneCount: Double
        ) {
            this.fgDetail = fgDetail
            this.productionSchedule = productionSchedule
            this.closeBalance = closeBalance
            this.totalDifference = totalDifference
            this.totalProductionCount = totalProductionCount
            this.totalForgoneCount = totalForgoneCount
        }

        override fun toString(): String {
            return "RoughScheduleObj(fgDetail=$fgDetail, productionSchedule=${productionSchedule.contentToString()}, closeBalance=${closeBalance.contentToString()}, totalDifference=$totalDifference, totalProductionCount=$totalProductionCount, totalForgoneCount=$totalForgoneCount)"
        }


    }

}