package com.ffii.fpsms.modules.deliveryOrder.web

import com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchMainService
import com.ffii.fpsms.modules.deliveryOrder.web.models.WorkbenchScanPickRequest
import com.ffii.fpsms.modules.deliveryOrder.web.models.WorkbenchBatchScanPickRequest
import com.ffii.fpsms.modules.master.web.models.MessageResponse
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 org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.PathVariable
import java.time.LocalDate
import org.springframework.format.annotation.DateTimeFormat
import com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchReleaseService
import com.ffii.fpsms.modules.deliveryOrder.web.models.*
import org.springframework.web.bind.annotation.ModelAttribute
import com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchDopoAssignmentService
import com.ffii.fpsms.modules.deliveryOrder.service.WorkbenchTruckRoutingSummaryService
import com.ffii.fpsms.modules.report.service.ReportService
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import java.time.format.DateTimeFormatter
@RestController
@RequestMapping("/doPickOrder/workbench")
class DoWorkbenchController(
    private val doWorkbenchMainService: DoWorkbenchMainService,
    private val doWorkbenchReleaseService: DoWorkbenchReleaseService,
    private val doWorkbenchDopoAssignmentService: DoWorkbenchDopoAssignmentService,
    private val workbenchTruckRoutingSummaryService: WorkbenchTruckRoutingSummaryService,
    private val reportService: ReportService,
) {
    /**
     * Workbench scan-pick: implemented in [com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchMainService].
     * Posts outbound on the scanned inventory lot line first; on lot exhaustion before the line chunk is filled
     * (without an explicit short [WorkbenchScanPickRequest.qty]) performs split + `pick_order_line.partially_completed`.
     * Explicit short qty completes SOL and POL without `partially_completed`.
     * Explicit qty may exceed this SOL’s remaining chunk (overscan); actual post is capped by lot availability, then
     * rebuild + ensure SOL reconcile the line.
     */
    @PostMapping("/scan-pick")
    fun scanPick(@RequestBody request: WorkbenchScanPickRequest): MessageResponse {
        return doWorkbenchMainService.scanPick(request)
    }

    /**
     * Batch scan-pick for workbench submit flows (e.g. close noLot/expired/unavailable with qty=0).
     * Returns SUCCESS when at least one line is processed; each line response is returned in entity.lines.
     */
    @PostMapping("/scan-pick/batch")
    fun scanPickBatch(@RequestBody request: WorkbenchBatchScanPickRequest): MessageResponse {
        val lines = request.lines
        if (lines.isEmpty()) {
            return MessageResponse(
                id = null,
                name = "scan_pick_batch",
                code = "EMPTY",
                type = "workbench_scan_pick_batch",
                message = "No lines",
                errorPosition = null,
                entity = mapOf("lines" to emptyList<Any>()),
            )
        }
        val results = lines.map { doWorkbenchMainService.scanPick(it) }
        val anySuccess = results.any { (it.code ?: "").equals("SUCCESS", ignoreCase = true) }
        return MessageResponse(
            id = null,
            name = "scan_pick_batch",
            code = if (anySuccess) "SUCCESS" else "FAILED",
            type = "workbench_scan_pick_batch",
            message = if (anySuccess) "Batch scan-pick processed" else "Batch scan-pick failed",
            errorPosition = null,
            entity = mapOf(
                "lines" to results,
                "total" to results.size,
                "success" to results.count { (it.code ?: "").equals("SUCCESS", ignoreCase = true) },
                "failed" to results.count { !(it.code ?: "").equals("SUCCESS", ignoreCase = true) },
            ),
        )
    }
    
    /** Lane grid from `delivery_order_pick_order` + linked `pick_order` (workbench FG assign view). */
    @GetMapping("/summary-by-store")
    fun getWorkbenchSummaryByStore(
        @RequestParam storeId: String,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) requiredDate: LocalDate?,
        @RequestParam releaseType: String?
    ): StoreLaneSummary {
        return doWorkbenchMainService.getDeliveryOrderPickOrderSummaryByStore(
            storeId,
            requiredDate,
            releaseType ?: "all"
        )
    }

    /** All Etra workbench tickets for a day, grouped by shop → truck (see [DoWorkbenchMainService.getWorkbenchEtraLaneSummary]). */
    @GetMapping("/summary-is-etra")
    fun getWorkbenchEtraSummary(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) requiredDate: LocalDate?,
    ): List<WorkbenchEtraShopLaneGroup> =
        doWorkbenchMainService.getWorkbenchEtraLaneSummary(requiredDate)

    /** Past-date backlog tickets from `delivery_order_pick_order` (not `do_pick_order`). */
    @GetMapping("/released")
    fun getWorkbenchReleasedDoPickOrders(
        @RequestParam(required = false) shopName: String?,
        @RequestParam(required = false) storeId: String?,
        @RequestParam(required = false) truck: String?,
        @RequestParam(required = false) releaseType: String?,
    ): List<ReleasedDoPickOrderListItem> {
        return doWorkbenchMainService.findWorkbenchReleasedDeliveryOrderPickOrdersForSelection(
            shopName,
            storeId,
            truck,
            releaseTypeFilter = releaseType,
        )
    }

    @GetMapping("/released-today")
    fun getWorkbenchReleasedDoPickOrdersToday(
        @RequestParam(required = false) shopName: String?,
        @RequestParam(required = false) storeId: String?,
        @RequestParam(required = false) truck: String?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) requiredDate: LocalDate?,
        @RequestParam(required = false) releaseType: String?,
    ): List<ReleasedDoPickOrderListItem> {
        return doWorkbenchMainService.findWorkbenchReleasedDeliveryOrderPickOrdersForSelectionToday(
            shopName,
            storeId,
            truck,
            requiredDeliveryDate = requiredDate,
            releaseTypeFilter = releaseType,
        )
    }

    @PostMapping("/assign-by-delivery-order-pick-order-id")
    fun assignByDeliveryOrderPickOrderId(@RequestBody request: AssignByDeliveryOrderPickOrderIdRequest): MessageResponse {
        return doWorkbenchDopoAssignmentService.assignByDeliveryOrderPickOrderId(request)
    }

    /** Assign V1 (legacy): old FG-style, no atomic conflict guard. */
    @PostMapping("/assign-by-delivery-order-pick-order-id-v1")
    fun assignByDeliveryOrderPickOrderIdV1(@RequestBody request: AssignByDeliveryOrderPickOrderIdRequest): MessageResponse {
        return doWorkbenchDopoAssignmentService.assignByDeliveryOrderPickOrderIdV1(request)
    }

    /** Lane assign for workbench FG (uses `delivery_order_pick_order`, not `do_pick_order`). */
    @PostMapping("/assign-by-lane")
    fun assignByLaneWorkbench(@RequestBody request: AssignByLaneRequest): MessageResponse {
        return doWorkbenchDopoAssignmentService.assignByLaneForWorkbench(request)
    }

    /** Lane assign V1 (legacy): old FG-style, no atomic conflict guard. */
    @PostMapping("/assign-by-lane-v1")
    fun assignByLaneWorkbenchV1(@RequestBody request: AssignByLaneRequest): MessageResponse {
        return doWorkbenchDopoAssignmentService.assignByLaneForWorkbenchV1(request)
    }
    
    @PostMapping("/batch-release/async")
    fun startWorkbenchBatchReleaseAsync(
        @RequestBody ids: List<Long>,
        @RequestParam(defaultValue = "1") userId: Long
    ): MessageResponse {
        return doWorkbenchReleaseService.startBatchReleaseAsync(ids, userId)
    }

    /**
     * V2: [DeliveryOrderService.releaseDeliveryOrderWithoutTicketNoHoldV2] — no stock out header at release;
     * suggested pick + stock out + lines run when assigning the workbench ticket ([DoWorkbenchDopoAssignmentService]).
     */
    @PostMapping("/batch-release/async-v2")
    fun startWorkbenchBatchReleaseAsyncV2(
        @RequestBody ids: List<Long>,
        @RequestParam(defaultValue = "1") userId: Long
    ): MessageResponse {
        return doWorkbenchReleaseService.startBatchReleaseAsyncV2(ids, userId)
    }

    /**
     * One delivery order, same release pipeline as [startWorkbenchBatchReleaseAsyncV2], but
     * [delivery_order_pick_order.releaseType] = `single` and ticket prefix `TI-S-` (not batch / `TI-B-`).
     * Body: JSON number (mirrors [DoPickOrderController.startBatchReleaseAsyncSingle]).
     */
    @PostMapping("/batch-release/async-single-v2")
    fun startWorkbenchBatchReleaseAsyncSingleV2(
        @RequestBody doId: Long,
        @RequestParam(defaultValue = "1") userId: Long
    ): MessageResponse {
        return doWorkbenchReleaseService.startBatchReleaseAsyncSingleV2(listOf(doId), userId)
    }

    /** Synchronous batch release V2 (same semantics as async-v2; for tools / tests). */
    @PostMapping("/batch-release/sync-v2")
    fun workbenchBatchReleaseSyncV2(
        @RequestBody ids: List<Long>,
        @RequestParam(defaultValue = "1") userId: Long
    ): MessageResponse {
        return doWorkbenchReleaseService.releaseBatchV2(ids, userId)
    }
    
    @GetMapping("/batch-release/progress/{jobId}")
    fun getWorkbenchBatchReleaseProgress(@PathVariable jobId: String): MessageResponse {
        return doWorkbenchReleaseService.getBatchReleaseProgress(jobId)
    }

    @GetMapping("/ticket-release-table/{startDate}&{endDate}")
    fun getWorkbenchTicketReleaseTable(
        @PathVariable startDate: LocalDate,
        @PathVariable endDate: LocalDate,
    ): List<WorkbenchTicketReleaseTableResponse> {
        return doWorkbenchMainService.getWorkbenchTicketReleaseTable(startDate, endDate)
    }

    /** Workbench-only force complete by delivery_order_pick_order.id (does not touch do_pick_order headers). */
    @PostMapping("/force-complete/{deliveryOrderPickOrderId}")
    fun forceCompleteWorkbenchTicket(@PathVariable deliveryOrderPickOrderId: Long): MessageResponse {
        return doWorkbenchMainService.forceCompleteWorkbenchTicket(deliveryOrderPickOrderId)
    }

    /** Workbench-only revert assignment by delivery_order_pick_order.id. */
    @PostMapping("/revert-assignment/{deliveryOrderPickOrderId}")
    fun revertWorkbenchTicketAssignment(@PathVariable deliveryOrderPickOrderId: Long): MessageResponse {
        return doWorkbenchMainService.revertWorkbenchTicketAssignment(deliveryOrderPickOrderId)
    }

    @GetMapping("/completed-lot-details/{deliveryOrderPickOrderId}")
    fun getCompletedLotDetails(
        @PathVariable deliveryOrderPickOrderId: Long,
    ): Map<String, Any?> {
        return doWorkbenchMainService.getCompletedLotDetailsByDeliveryOrderPickOrderId(deliveryOrderPickOrderId)
    }
    @GetMapping("/print-DN")
    fun printWorkbenchDN(@ModelAttribute request: PrintDeliveryNoteRequest) {
        doWorkbenchMainService.printDeliveryNoteWorkbench(request)
    }

    @GetMapping("/print-DNLabels")
    fun printWorkbenchDNLabels(@ModelAttribute request: PrintDNLabelsRequest) {
        doWorkbenchMainService.printDNLabelsWorkbench(request)
    }
    @GetMapping("/print-DNLabels-reprint")
    fun printWorkbenchDNLabelsReprint(@ModelAttribute request: PrintDNLabelsReprintRequest) {
        doWorkbenchMainService.printDNLabelsReprint(request)
    }
    @GetMapping("/truck-routing-summary/store-options")
    fun getWorkbenchTruckRoutingStoreOptions(): List<Map<String, String>> =
        workbenchTruckRoutingSummaryService.getStoreOptions()

    @GetMapping("/truck-routing-summary/lane-options")
    fun getWorkbenchTruckRoutingLaneOptions(
        @RequestParam(required = false) storeId: String?,
    ): List<Map<String, String>> =
        workbenchTruckRoutingSummaryService.getLaneOptions(storeId)

    @GetMapping("/truck-routing-summary/precheck")
    fun precheckWorkbenchTruckRoutingSummary(
        @RequestParam(required = false) storeId: String?,
        @RequestParam(required = false) truckLanceCode: String?,
        @RequestParam(required = false) date: String?,
    ): Map<String, Any> {
        val reportDate = if (date.isNullOrBlank()) {
            LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        } else {
            date
        }
        val unpickedCount = workbenchTruckRoutingSummaryService.getUnpickedOrderCount(storeId, truckLanceCode, reportDate)
        return mapOf(
            "unpickedOrderCount" to unpickedCount,
            "hasUnpickedOrders" to (unpickedCount > 0),
        )
    }

    @GetMapping("/truck-routing-summary/print")
    fun printWorkbenchTruckRoutingSummary(
        @RequestParam(required = false) storeId: String?,
        @RequestParam(required = false) truckLanceCode: String?,
        @RequestParam(required = false) date: String?,
    ): ResponseEntity<ByteArray> {
        val reportDate = if (date.isNullOrBlank()) {
            LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
        } else {
            date
        }
        val departureLabel = workbenchTruckRoutingSummaryService.getDepartureTimeLabel(storeId, truckLanceCode)
        val lane = (truckLanceCode ?: "").trim()
        val params = mutableMapOf<String, Any>(
            "FLOOR_LABEL" to workbenchTruckRoutingSummaryService.formatStoreIdForDisplay(storeId),
            "TRUCK_ROUTE" to lane,
            "DEPARTURE_TIME" to departureLabel,
            "REPORT_DATE" to reportDate,
        )
        val rows = workbenchTruckRoutingSummaryService.search(storeId, truckLanceCode, reportDate)
        val pdfBytes = reportService.createPdfResponse(
            "/DeliveryNote/TruckRoutingSummaryPDF.jrxml",
            params,
            rows
        )
        val headers = HttpHeaders().apply {
            contentType = MediaType.APPLICATION_PDF
            setContentDispositionFormData("attachment", "TruckRoutingSummary.pdf")
            set("filename", "TruckRoutingSummary.pdf")
        }
        return ResponseEntity(pdfBytes, headers, HttpStatus.OK)
    }

}
