package com.ffii.fpsms.modules.master.web

import com.ffii.core.response.RecordsRes
import com.ffii.core.utils.CriteriaArgsBuilder
import com.ffii.fpsms.modules.common.scheduler.service.SchedulerService
import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo
import com.ffii.fpsms.modules.master.entity.projections.RoughProdScheduleWithLine
import com.ffii.fpsms.modules.master.service.ProductionScheduleService
import com.ffii.fpsms.modules.master.service.ProductionScheduleService.FinishedGood
import com.ffii.fpsms.modules.master.service.ProductionScheduleService.RoughScheduleObj
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleLineRequest
import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest
import com.ffii.fpsms.modules.master.web.models.ReleaseProdScheduleRequest
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.*
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.HashMap
import kotlin.collections.component1
import kotlin.collections.component2
import org.springframework.http.ResponseEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import java.time.format.DateTimeParseException

@RestController
@RequestMapping("/productionSchedule")
class ProductionScheduleController(
    private val productionScheduleService: ProductionScheduleService,
    private val productionScheduleRepository: ProductionScheduleRepository,
    private val schedulerService: SchedulerService,
) {
//    @GetMapping
//    fun allItems(): List<Items> {
//        return uomConversionService.allItems()
//    }

    val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    @GetMapping("/test")
    fun test(request: HttpServletRequest): Any {
        val criteriaArgs = CriteriaArgsBuilder.withRequest(request)
            .addStringLike("type")
            .build()
        if (!criteriaArgs.containsKey("type")) {
            criteriaArgs["type"] = "rough"
        }
        return productionScheduleService.getLatestScheduleAt("rough")
    }

    @GetMapping("/test1")
    fun test1(): Any {
        return schedulerService.getRoughProdSchedule();
    }

    @GetMapping("/test2")
    fun test2(): Any {
        return schedulerService.getDetailedProdSchedule();
    }

    @GetMapping("/detail/rough/{id}")
    fun getScheduleDetail(@PathVariable id: Long): RoughProdScheduleWithLine {
        return productionScheduleService.roughProdScheduleDetail(id)
    }

    @GetMapping("/detail/detailed/{id}")
    fun getDetailedProdScheduleDetail(@PathVariable id: Long): DetailedProdScheduleWithLine {
        print("getDetailedProdScheduleDetail################")
        return productionScheduleService.detailedProdScheduleDetail(id)
    }

    @PostMapping("/detail/detailed/save")
    fun saveDetailedProdScheduleDetail(@Valid @RequestBody request: ReleaseProdScheduleLineRequest): MessageResponse {
        return productionScheduleService.saveProdScheduleLine(request)
    }

    @PostMapping("/detail/detailed/releaseLine")
    fun releaseProdScheduleLine(@Valid @RequestBody request: ReleaseProdScheduleLineRequest): MessageResponse {
        return productionScheduleService.releaseProdScheduleLine(request)
    }

    @PostMapping("/detail/detailed/release")
    fun releaseProdSchedule(@Valid @RequestBody request: ReleaseProdScheduleRequest): MessageResponse {
        return productionScheduleService.releaseProdSchedule(request)
    }

    @GetMapping("/getRecordByPage")
    fun allProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> {
        print("allProdSchedulesByPage############")
        return productionScheduleService.allProdSchedulesByPage(request);
    }

    @GetMapping("/getRecordByPage/rough")
    fun allRoughProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> {
        return productionScheduleService.allRoughProdSchedulesByPage(request);
    }

    @GetMapping("/getRecordByPage/detailed")
    fun allDetailedProdSchedulesByPage(@ModelAttribute request: SearchProdScheduleRequest): RecordsRes<ProdScheduleInfo> {
        return productionScheduleService.allDetailedProdSchedulesByPage(request);
    }

    @RequestMapping(value = ["/testDetailedSchedule"], method = [RequestMethod.GET])
    fun generateDetailSchedule(
        @RequestParam(name = "startDate", required = false) startDateParam: String?,
        @RequestParam(name = "days", required = false, defaultValue = "7") days: Int,
        request: HttpServletRequest?
    ): Int {
        try {
            // ── Determine the generation base date ──
            val genDate = startDateParam
                ?.let { param ->
                    try {
                        LocalDate.parse(param).atStartOfDay()
                    } catch (e: Exception) {
                        // Invalid format → fallback to today
                        println("Invalid startDate format: $param → using today")
                        LocalDateTime.now()
                    }
                }
                ?: LocalDateTime.now()   // default: today

            val today = LocalDateTime.now()

            // ── Your existing assignDate logic ──
            // (assuming schedule period monday=1 ... sunday=7)
            val assignDate = genDate.dayOfWeek.value

            // If you want to allow offset / custom day assignment later, you can keep/extend this:
            // val day = 1
            // val assignDate = ((genDate.dayOfWeek.value + day) % 7) + 1

            println("Starting detailed schedule generation")
            println("  → genDate     : $genDate")
            println("  → today       : $today")
            println("  → assignDate  : $assignDate")

            // Core generation call – using today's timestamp as "reference time"
            // (you can change the last parameter to genDate if business logic requires it)
            productionScheduleService.generateDetailedScheduleByDay(
                assignDate,
                today,           // reference time / as-of time
                genDate,
                days// target / base date for planning
            )

            return 200
        } catch (e: Exception) {
            println("Error generating detailed schedule: ${e.message}")
            e.printStackTrace()
            throw RuntimeException("Error generate schedule: ${e.message}", e)
        }
    }

    @RequestMapping(value = ["/testRoughSchedule"], method = [RequestMethod.GET])
    fun generateRoughSchedule(request: HttpServletRequest?): List<HashMap<RoughScheduleObj, Double>> {
        try {
            val demoFGList_old = arrayListOf<FinishedGood>(
                FinishedGood(
                1,
                "豆腐花 - Tofu pudding",
                3,
                91.0,
                31.0,
                3000.0
                ),
                FinishedGood(
                    2,
                    "中湯包 - Chinese Soup",
                    4,
                    100.0,
                    72.5,
                    3000.0
                ),
                FinishedGood(
                    3,
                    "苦瓜炒蛋 - Fried Egg",
                    3,
                    700.0,
                    300.5,
                    22000.0
                ),
                FinishedGood(
                    4,
                    "酸菜魚湯包 - Pickled fish soup",
                    4,
                    3000.0,
                    600.5,
                    22000.0
                ),
                FinishedGood(
                    5,
                    "咖哩雞球 - Curry chicken balls",
                    5,
                    3000.0,
                    2150.5,
                    22000.0
                ),
                FinishedGood(
                    6,
                    "水煮牛 - Spicy Beef",
                    6,
                    1000.0,
                    2000.5,
                    22000.0
                ),
                FinishedGood(
                    6,
                    "海鮮濃湯 - Sea Food Soup",
                    6,
                    0.0,
                    3500.5,
                    22000.0
                ),
                FinishedGood(
                    7,
                    "薯條 - Fries",
                    7,
                    0.0,
                    3000.5,
                    22000.0
                ),
            );

            val demoFGList = productionScheduleService.convertToFinishedGoodList();

            val result: HashMap<RoughScheduleObj, Double> = productionScheduleService.generateRoughScheduleByWeek(demoFGList)
            val sortedEntries = result.entries.sortedBy { it.value }


            var accProdCount = 0.0;
            var fgCount = 0L;
            for ((roughScheduleRecord, totalDifference) in sortedEntries) {
                accProdCount += roughScheduleRecord.totalProductionCount;
                fgCount ++;
                println("[totalDifference:" + totalDifference + "] - " + roughScheduleRecord.toString())
            }

            // Convert to List<HashMap<RoughScheduleObj, Double>>
            productionScheduleService.saveRoughScheduleOutput(sortedEntries, accProdCount, fgCount);
            return sortedEntries.map { entry ->
                hashMapOf(entry.key to entry.value)
            }
        } catch (e: Exception) {
            throw RuntimeException("Error generate schedule: ${e.message}", e)
        }
    }

    @GetMapping(
        value = ["/export-prod-schedule"],
        produces = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
    )
    fun exportProdSchedule(
        @RequestParam(name = "fromDate", required = false) fromDateParam: String?
    ): ResponseEntity<ByteArray> {
        // Parse fromDate or fall back to today
        val fromDate: LocalDate = fromDateParam
            ?.let {
                try {
                    LocalDate.parse(it)
                } catch (e: DateTimeParseException) {
                    LocalDate.now()
                }
            }
            ?: LocalDate.now()

        val data = productionScheduleService.searchExportProdSchedule(fromDate)
        val dataMat = productionScheduleService.searchExportProdScheduleMaterial(fromDate)
        val dataDaily = productionScheduleService.searchExportDailyMaterial(fromDate)
        val excelContent = productionScheduleService.exportProdScheduleToExcel(fromDate, data, dataMat, dataDaily)
        
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=production_schedule.xlsx")
            .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
            .body(excelContent)
    }
}