package com.ffii.fpsms.modules.jobOrder.web

import com.ffii.core.response.RecordsRes
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail

import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService
import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService
import com.ffii.fpsms.modules.jobOrder.service.JobOrderService
import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest
import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderCommonActionRequest
import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobTypeResponse
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import com.ffii.fpsms.modules.jobOrder.service.JoPickOrderService
import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchMainService
import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchReleaseService
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus

import org.springframework.format.annotation.DateTimeFormat
import com.ffii.fpsms.modules.productProcess.service.ProductProcessService
import com.ffii.fpsms.modules.jobOrder.web.model.*
import com.ffii.fpsms.modules.jobOrder.web.model.ExportPickRecordRequest
import com.ffii.fpsms.modules.jobOrder.web.model.PrintPickRecordRequest
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanSubmitRequest
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanIssueRequest
import jakarta.servlet.http.HttpServletResponse
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperPrint
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException
import org.springframework.context.NoSuchMessageException
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.text.ParseException
import org.springframework.web.bind.annotation.*  
import org.springframework.web.bind.annotation.RequestParam 
import com.ffii.fpsms.modules.jobOrder.web.model.UpdateJoPickOrderHandledByRequest
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName
import com.ffii.fpsms.modules.jobOrder.web.model.ExportFGStockInLabelRequest
import com.ffii.fpsms.modules.jobOrder.web.model.MaterialPickStatusItem
import java.time.LocalDate

@RestController
@RequestMapping("/jo")
class JobOrderController(
    private val jobOrderService: JobOrderService,
    private val jobOrderBomMaterialService: JobOrderBomMaterialService,
    private val jobOrderProcessService: JobOrderProcessService,
    private val joPickOrderService: JoPickOrderService,
    private val joWorkbenchMainService: JoWorkbenchMainService,
    private val joWorkbenchReleaseService: JoWorkbenchReleaseService,
    private val productProcessService: ProductProcessService,
    private val jobOrderCreationService: com.ffii.fpsms.modules.jobOrder.service.JobOrderCreationService
) {

    @GetMapping("/getRecordByPage")
    fun allJobOrdersByPage(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfoWithTypeName> {
        println("getRecordByPage")
        println(request)
        //return jobOrderService.allJobOrdersByPage(request);
        return jobOrderService.allJobOrdersByPageWithTypeName(request);
    }

    @GetMapping("/detail/{id}")
    fun jobOrderDetail(@PathVariable id: Long): JobOrderDetail {
        return jobOrderService.jobOrderDetail(id);
    }

    @GetMapping("/detailByCode/{code}")
    fun jobOrderDetailByCode(@PathVariable code: String): JobOrderDetail {
        return jobOrderService.jobOrderDetailByCode(code);
    }

    @PostMapping("/update")
    fun updateJobOrder(@Valid @RequestBody request: JobOrderUpdateRequest): MessageResponse {
        return jobOrderService.updateJobOrder(request)
    }

    @PostMapping("/release")
    fun releaseJobOrder(@Valid @RequestBody request: JobOrderCommonActionRequest): MessageResponse {
        return jobOrderService.releaseJobOrder(request)
    }

    /** Workbench: same search as [allJobOrdersByPage] but dedicated route for future divergence. */
    @GetMapping("/workbench/getRecordByPage")
    fun allJobOrdersByPageForWorkbench(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfoWithTypeName> {
        return jobOrderService.allJobOrdersByPageWithTypeName(request)
    }

    /** Workbench: release without stock out / SPL / SOL until first pick-order assign. */
    
    @PostMapping("/workbench/release")
    fun releaseJobOrderForWorkbench(@Valid @RequestBody request: JobOrderCommonActionRequest): MessageResponse {
        return joWorkbenchReleaseService.releaseJobOrderForWorkbench(request)
    }
    
    @PostMapping("/set-hidden")
    fun setJobOrderHidden(@Valid @RequestBody request: SetJobOrderHiddenRequest): MessageResponse {
        return jobOrderService.setJobOrderHidden(request)
    }

    @PostMapping("/start")
    fun startJobOrder(@Valid @RequestBody request: JobOrderCommonActionRequest): MessageResponse {
        val jo = jobOrderService.startJobOrder(request)
        if (jo.id == null) {
            throw NoSuchElementException()
        }
        jobOrderBomMaterialService.completeJobOrderBomMaterialsByJoId(jo.id)

        return jo
    }

    @PostMapping("/manualCreate")
    fun manualCreateJobOrder(@Valid @RequestBody request: CreateJobOrderRequest): MessageResponse {
        return jobOrderCreationService.createJobOrderWithFullFlow(request, request.productionPriority)
    }
    @GetMapping("/all-lots-hierarchical/{userId}")
    fun getAllJobOrderLotsHierarchical(@PathVariable userId: Long): JobOrderLotsHierarchicalResponse {
        return joPickOrderService.getAllJobOrderLotsWithDetailsHierarchical(userId)
    }

    @GetMapping("/completed-job-order-pick-orders/{userId}")
    fun getCompletedJobOrderLotsHierarchical(@PathVariable userId: Long): Map<String, Any?> {
        return joPickOrderService.getCompletedJobOrderLotsHierarchical(userId)
    }

    @GetMapping("/completed-job-order-pick-order-records/{userId}")
    fun getCompletedJobOrderPickOrderRecords(@PathVariable userId: Long): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrderRecords(userId)
    }
    @GetMapping("/unassigned-job-order-pick-orders")
    fun getUnassignedJobOrderPickOrders(): List<Map<String, Any?>> {
        return joPickOrderService.getUnassignedJobOrderPickOrders()
    }

    @PostMapping("/assign-job-order-pick-order/{pickOrderId}/{userId}")
    fun assignJobOrderPickOrderToUser(
        @PathVariable pickOrderId: Long,
        @PathVariable userId: Long
    ): MessageResponse {
        return joPickOrderService.assignJobOrderPickOrderToUser(pickOrderId, userId)
    }

    /** Workbench: assign + prime SPL/SOL when release was deferred (see [JoWorkbenchMainService]). */
    
    @PostMapping("/workbench/assign-job-order-pick-order/{pickOrderId}/{userId}")
    fun assignJobOrderPickOrderToUserForWorkbench(
        @PathVariable pickOrderId: Long,
        @PathVariable userId: Long
    ): MessageResponse {
        return joWorkbenchMainService.assignJobOrderPickOrderToUserForWorkbench(pickOrderId, userId)
    }
    
    @PostMapping("/unassign-job-order-pick-order/{pickOrderId}")
    fun unAssignJobOrderPickOrderToUser(
        @PathVariable pickOrderId: Long
    ): MessageResponse {
        return joPickOrderService.unAssignJobOrderPickOrderToUser(pickOrderId)
    }
    @PostMapping("/update-match-status")
    fun updateMatchStatus(@RequestBody request: UpdateMatchStatusRequest): MessageResponse {
        return joPickOrderService.updateMatchStatus(
            request.pickOrderId, 
            request.itemId, 
            request.userId, 
            request.qty
        )
    }

    @PostMapping("/second-scan-submit/{pickOrderId}/{itemId}")
    fun submitSecondScanQuantity(
        @PathVariable pickOrderId: Long,
        @PathVariable itemId: Long,
        @RequestBody data: Map<String, Any>
    ): MessageResponse {
        val request = SecondScanSubmitRequest(
            pickOrderId = pickOrderId, //  Use path variable
            itemId = itemId, //  Use path variable
            qty = (data["qty"] as Number).toDouble(),
            isMissing = data["isMissing"] as? Boolean ?: false,
            isBad = data["isBad"] as? Boolean ?: false,
            userId = (data["userId"] as Number).toLong()
        )
        return joPickOrderService.submitSecondScanQty(request)
    }
    
    @PostMapping("/second-scan-issue/{pickOrderId}/{itemId}")
fun recordSecondScanIssue(
    @PathVariable pickOrderId: Long,
    @PathVariable itemId: Long,
    @RequestBody data: Map<String, Any>
): MessageResponse {
    val request = SecondScanIssueRequest(
        pickOrderId = pickOrderId, //  path 变量
        itemId = itemId,           //  path 变量
        qty = (data["qty"] as Number).toDouble(),
        //  新增：安全读取 missQty/badItemQty/type，默认 0/"match"
        missQty = (data["missQty"] as? Number)?.toDouble() ?: 0.0,
        badItemQty = (data["badItemQty"] as? Number)?.toDouble() ?: 0.0,
        isMissing = data["isMissing"] as? Boolean ?: false,
        isBad = data["isBad"] as? Boolean ?: false,
        reason = data["reason"] as? String ?: "",
        createdBy = (data["createdBy"] as Number).toLong(),
        type = (data["type"] as? String) ?: "match"
    )
    return joPickOrderService.recordSecondScanIssue(request)
}
/*
    @GetMapping("/completed-job-order-pick-orders-with-completed-second-scan")
    fun getCompletedJobOrderPickOrdersWithCompletedSecondScan(): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrdersWithCompletedSecondScan()
    }
    */
    @GetMapping("/completed-job-order-pick-order-lot-details/{pickOrderId}")
    fun getCompletedJobOrderPickOrderLotDetails(@PathVariable pickOrderId: Long): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrderLotDetails(pickOrderId)
    }

    @PostMapping("/PickRecord")
    @Throws(UnsupportedEncodingException::class, NoSuchMessageException::class, ParseException::class, Exception::class)
    fun printPickRecord(@Valid @RequestBody request: ExportPickRecordRequest, response: HttpServletResponse){
        response.characterEncoding = "utf-8"
        response.contentType = "application/pdf"
        val out: OutputStream = response.outputStream
        val pdf = jobOrderService.exportPickRecord(request)
        val jasperPrint = pdf["report"] as JasperPrint
        response.addHeader("filename", "${pdf["filename"]}.pdf")
        out.write(JasperExportManager.exportReportToPdf(jasperPrint))
    }

    @GetMapping("/print-PickRecord")
    fun printPickRecord(@ModelAttribute request: PrintPickRecordRequest){
        jobOrderService.printPickRecord(request)
    }

    @PostMapping("/FGStockInLabel")
    @Throws(UnsupportedEncodingException::class, NoSuchMessageException::class, ParseException::class, Exception::class)
    fun exportFGStockInLabel(@Valid @RequestBody request: ExportFGStockInLabelRequest, response: HttpServletResponse){
        response.characterEncoding = "utf-8"
        response.contentType = "application/pdf"
        val out: OutputStream = response.outputStream
        val pdf = jobOrderService.exportFGStockInLabel(request)
        val jasperPrint = pdf["report"] as JasperPrint
        response.addHeader("filename", "${pdf["filename"]}.pdf")
        out.write(JasperExportManager.exportReportToPdf(jasperPrint))
    }

    @GetMapping("/print-FGStockInLabel")
    fun printStockInLabel(@ModelAttribute request: PrintFGStockInLabelRequest){
        jobOrderService.printFGStockInLabel(request)
    }
/*

    @GetMapping("/completed-job-order-pick-orders-only")
    fun getCompletedJobOrderPickOrders(): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrders()
    }
*/
    @GetMapping("/completed-job-order-pick-orders-only")
    fun getCompletedJobOrderPickOrders(
        @RequestParam(name = "date", required = false)
        @org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE)
        completedDate: LocalDate?,
    ): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrders(completedDate)
    }
    @GetMapping("/job-order-pick-orders")
fun getJobOrderPickOrders(
    @RequestParam(name = "date", required = false)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    date: LocalDate?,
    @RequestParam(name = "status", required = false)
    status: PickOrderStatus?,
): List<Map<String, Any?>> {
    return joPickOrderService.getJobOrderPickOrders(date, status)
}

@GetMapping("/job-order-pick-order-lot-details/{pickOrderId}")
fun getJobOrderPickOrderLotDetails(
    @PathVariable pickOrderId: Long,
    @RequestParam(name = "status", required = false) status: String?,
): List<Map<String, Any?>> {
    return joPickOrderService.getJobOrderPickOrderLotDetails(pickOrderId, status)
}
    @GetMapping("/joForPrintQrCode/{date}")
    fun getJoForPrintQrCode(@PathVariable date: String): List<JobOrderListForPrintQrCodeResponse> {
        return joPickOrderService.getJobOrderListForPrintQrCode(LocalDate.parse(date))
    }

    @GetMapping("/completed-job-order-pick-order-lot-details-completed-pick/{pickOrderId}")
    fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(@PathVariable pickOrderId: Long): List<Map<String, Any?>> {
        return joPickOrderService.getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId)
    }
    @GetMapping("/AllJoPickOrder")
    fun getAllJoPickOrder(
        @RequestParam(name = "type", required = false) bomType: String?,
        // Single floor, e.g. "2F"/"3F"/"4F". When provided, backend returns job pick orders
        // that still have unpicked lines on that floor OR any "no lot" remaining lines.
        @RequestParam(required = false) floor: String?,
        @RequestParam(name = "jobOrderCode", required = false) jobOrderCode: String?,
        @RequestParam(name = "pickOrderCode", required = false) pickOrderCode: String?,
        @RequestParam(name = "itemName", required = false) itemName: String?,
        @RequestParam(name = "bomDescription", required = false) bomDescription: String?,
        @RequestParam(name = "planStart", required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
        planStart: LocalDate?,
    ): List<AllJoPickOrderResponse> {
        return joPickOrderService.getAllJoPickOrders(
            bomType = bomType,
            floor = floor,
            jobOrderCode = jobOrderCode,
            pickOrderCode = pickOrderCode,
            itemName = itemName,
            bomDescription = bomDescription,
            planStart = planStart,
        )
    }

    @GetMapping("/all-lots-hierarchical-by-pick-order/{pickOrderId}")
    fun getJobOrderLotsHierarchicalByPickOrderId(@PathVariable pickOrderId: Long): JobOrderLotsHierarchicalResponse {
        return joPickOrderService.getJobOrderLotsHierarchicalByPickOrderId(pickOrderId)
    }

    /** Workbench: available qty uses in−out (matches scan-pick); stockouts include suggested SPL qty when matched. */
    
    @GetMapping("/all-lots-hierarchical-by-pick-order-workbench/{pickOrderId}")
    fun getJobOrderLotsHierarchicalByPickOrderIdWorkbench(@PathVariable pickOrderId: Long): JobOrderLotsHierarchicalWorkbenchResponse {
        return joWorkbenchMainService.getJobOrderLotsHierarchicalByPickOrderIdWorkbench(pickOrderId)
    }
    
    @PostMapping("/update-jo-pick-order-handled-by")
fun updateJoPickOrderHandledBy(@Valid @RequestBody request: UpdateJoPickOrderHandledByRequest): MessageResponse {
    try {
        val joPickOrder = joPickOrderService.updateHandledByForItem(
            request.pickOrderId, 
            request.itemId, 
            request.userId
        )
        
        val joPickOrderRecord = joPickOrderService.updateRecordHandledByForItem(
            request.pickOrderId, 
            request.itemId, 
            request.userId
        )
        
        if (joPickOrder == null) {
            return MessageResponse(
                id = null,
                code = "NOT_FOUND",
                name = null,
                type = null,
                message = "JoPickOrder not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}",
                errorPosition = null
            )
        }
        
        return MessageResponse(
            id = joPickOrder.id,
            code = "SUCCESS",
            name = null,
            type = null,
            message = "JoPickOrder handledBy updated successfully for item ${request.itemId}",
            errorPosition = null
        )
    } catch (e: Exception) {
        println("Error updating JoPickOrder handledBy: ${e.message}")
        e.printStackTrace()
        return MessageResponse(
            id = null,
            code = "ERROR",
            name = null,
            type = null,
            message = "Failed to update JoPickOrder handledBy: ${e.message}",
            errorPosition = null
        )
    }
}
@GetMapping("/jobTypes")
    fun getAllJobTypes(): List<JobTypeResponse> {
        return jobOrderService.getAllJobTypes()
    }


@PostMapping("/demo/deleteJobOrder/{jobOrderId}")
fun deleteJoPickOrderJobOrderProductProcessPickOrder(@PathVariable jobOrderId: Long): MessageResponse {
    return joPickOrderService.deleteJoPickOrderJobOrderProductProcessPickOrder(jobOrderId)
}
@PostMapping("/update-jo-plan-start")
fun updateJoPlanStart(@Valid @RequestBody request: UpdateJoPlanStartRequest): MessageResponse {
    return jobOrderService.updateJoPlanStart(request)
}
    /*
@PostMapping("/checkJobOrderCreated")
fun checkJobOrderCreated(@Valid @RequestBody request: CheckJobOrderCreatedRequest): Boolean {
    return jobOrderService.checkJobOrderCreated(request)
}
    */
    @PostMapping("/updateReqQty")
    fun updateJoReqQty(@Valid @RequestBody request: UpdateJoReqQtyRequest): MessageResponse {
        return jobOrderService.updateJoReqQty(request)
    }
    @GetMapping("/material-pick-status")
    fun getMaterialPickStatus(@RequestParam(required = false) date: String?): List<MaterialPickStatusItem> {
        return joPickOrderService.getMaterialPickStatus(date)
    }
}