package com.ffii.fpsms.modules.pickOrder.web

import com.ffii.fpsms.modules.master.entity.projections.ShopAndTruck
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.Truck
import org.springframework.web.bind.ServletRequestBindingException
import jakarta.servlet.http.HttpServletRequest
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.http.ContentDisposition
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import java.nio.charset.StandardCharsets
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartHttpServletRequest

import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckRequest
import com.ffii.fpsms.modules.pickOrder.web.models.CreateTruckWithoutShopRequest
import com.ffii.fpsms.modules.pickOrder.web.models.UpdateTruckShopDetailsRequest
import com.ffii.fpsms.modules.pickOrder.service.TruckService
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
import com.ffii.fpsms.modules.pickOrder.web.models.SaveTruckLane
import com.ffii.fpsms.modules.pickOrder.web.models.TruckLaneCombinationResponse
import com.ffii.fpsms.modules.pickOrder.web.models.ExportRouteLanesRequest
import com.ffii.fpsms.modules.pickOrder.web.models.ExportRouteReportRequest
import com.ffii.fpsms.modules.pickOrder.web.models.ExportTruckLaneVersionReportExcelRequest
import com.ffii.fpsms.modules.pickOrder.web.models.UpdateLaneLogisticRequest
import com.ffii.fpsms.modules.pickOrder.web.models.ParseRouteLanesExcelResponse
import com.ffii.fpsms.modules.pickOrder.web.models.deleteTruckLane
import com.ffii.fpsms.modules.pickOrder.web.models.toLaneCombinationResponse
import jakarta.validation.Valid

@RestController
@RequestMapping("/truck")
open class TruckController(
    private val truckService: TruckService,
    private val truckRepository: TruckRepository,
) {

    
    @PostMapping("/save")
    fun saveTruck(@RequestBody request: SaveTruckRequest): MessageResponse {
        try {
            val truck = truckService.saveTruck(request)
            return MessageResponse(
                id = truck.id,
                name = truck.shopName,
                code = truck.truckLanceCode,
                type = "truck",
                message = if (truck.id != null) "Truck updated successfully" else "Truck created successfully",
                errorPosition = null,
                entity = truck
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

    @PostMapping("/createTruckInShop")
    fun createTruckInShop(@Valid @RequestBody request: SaveTruckRequest): MessageResponse {
        try {
            val truck = truckService.createTruckInShop(request)
            return MessageResponse(
                id = truck.id,
                name = truck.shopName,
                code = truck.truckLanceCode,
                type = "truck",
                message = "Truck created successfully",
                errorPosition = null,
                entity = truck
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

    /**
     * PDF 圖1：多車線匯出；每個 laneId（encodeLaneId）一個 worksheet，格式 MTMS_ROUTE_V1。
     */
    @PostMapping(
        "/exportRouteLanesExcel",
        consumes = [MediaType.APPLICATION_JSON_VALUE],
        produces = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
    )
    fun exportRouteLanesExcel(@RequestBody req: ExportRouteLanesRequest): ResponseEntity<ByteArray> {
        val bytes = truckService.exportRouteLanesExcelBytes(req.laneIds)
        val filename = "MTMS_車線_${System.currentTimeMillis()}.xlsx"
        val disposition = ContentDisposition.attachment()
            .filename(filename, StandardCharsets.UTF_8)
            .build()
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString())
            .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
            .body(bytes)
    }

    /**
     * 圖2：車線 Report（單一 sheet；每間物流公司一個水平區塊）
     */
    @PostMapping(
        "/exportRouteReportExcel",
        consumes = [MediaType.APPLICATION_JSON_VALUE],
        produces = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
    )
    fun exportRouteReportExcel(
        request: HttpServletRequest,
        @RequestBody req: ExportRouteReportRequest,
    ): ResponseEntity<ByteArray> {
        val preparedBy = request.userPrincipal?.name?.trim().takeUnless { it.isNullOrBlank() } ?: "current user"
        val bytes = truckService.exportRouteReportExcelBytes(req.laneIds, preparedBy)
        val filename = truckService.buildRouteReportFilename()
        val disposition = ContentDisposition.attachment()
            .filename(filename, StandardCharsets.UTF_8)
            .build()
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString())
            .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
            .body(bytes)
    }

    @PostMapping(
        "/exportTruckLaneVersionReportExcel",
        consumes = [MediaType.APPLICATION_JSON_VALUE],
        produces = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
    )
    open fun exportTruckLaneVersionReportExcel(
        request: HttpServletRequest,
        @RequestBody req: ExportTruckLaneVersionReportExcelRequest,
    ): ResponseEntity<ByteArray> {
        val preparedBy = request.userPrincipal?.name?.trim().takeUnless { it.isNullOrBlank() } ?: "current user"
        val bytes = truckService.exportTruckLaneVersionReportExcelBytes(
            TruckService.ExportTruckLaneVersionReportInput(
                fromVersionId = req.fromVersionId,
                toVersionId = req.toVersionId,
                preparedBy = preparedBy,
            ),
        )
        val filename = "車線版本報告_${System.currentTimeMillis()}.xlsx"
        val disposition = ContentDisposition.attachment()
            .filename(filename, StandardCharsets.UTF_8)
            .build()
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString())
            .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
            .body(bytes)
    }

    /** 與 [importRouteLanesExcel] 同一格式；僅解析、不寫入 DB（看板 staged import 預覽）。 */
    @PostMapping("/parseRouteLanesExcel")
    @Throws(ServletRequestBindingException::class)
    fun parseRouteLanesExcel(request: HttpServletRequest): ResponseEntity<ParseRouteLanesExcelResponse> {
        var workbook: Workbook? = null
        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
            return ResponseEntity.ok(truckService.parseRouteLanesExcel(workbook))
        } catch (e: Exception) {
            println("Error reading Excel file: ${e.message}")
            return ResponseEntity.badRequest().body(
                ParseRouteLanesExcelResponse(0, 0, emptyList()),
            )
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }

    /** 與 [exportRouteLanesExcel] 同一格式；一個檔案內多 sheet，每 sheet 一條車線。 */
    @PostMapping("/importRouteLanesExcel")
    @Throws(ServletRequestBindingException::class)
    fun importRouteLanesExcel(request: HttpServletRequest): ResponseEntity<*> {
        var workbook: Workbook? = null
        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
        } catch (e: Exception) {
            println("Error reading Excel file: ${e.message}")
            return ResponseEntity.badRequest().body(
                MessageResponse(
                    id = null,
                    name = null,
                    code = null,
                    type = "truck",
                    message = "Error reading Excel file: ${e.message}",
                    errorPosition = null,
                    entity = null,
                ),
            )
        }
        try {
            val result = truckService.importRouteLanesExcel(workbook)
            return ResponseEntity.ok(
                MessageResponse(
                    id = null,
                    name = null,
                    code = null,
                    type = "truck",
                    message = result,
                    errorPosition = null,
                    entity = null,
                ),
            )
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }

    @PostMapping("/importExcel")
    @Throws(ServletRequestBindingException::class)
    fun importExcel(request: HttpServletRequest): ResponseEntity<*> {
        var workbook: Workbook? = null

        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
        } catch (e: Exception) {
            println("Error reading Excel file: ${e.message}")
            return ResponseEntity.badRequest().body(
                MessageResponse(
                    id = null,
                    name = null,
                    code = null,
                    type = "truck",
                    message = "Error reading Excel file: ${e.message}",
                    errorPosition = null,
                    entity = null
                )
            )
        }

        try {
            val result = truckService.importExcel(workbook)
            return ResponseEntity.ok(
                MessageResponse(
                    id = null,
                    name = null,
                    code = null,
                    type = "truck",
                    message = result,
                    errorPosition = null,
                    entity = null
                )
            )
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }

    @GetMapping("/findTruckLane/{shopId}")
    fun findAllByShopId(@PathVariable shopId: Long): List<Truck> {
        return truckService.findAllByShopId(shopId);
    }

    @PostMapping("/updateTruckLane")
    fun updateTruckLane(@Valid @RequestBody request: SaveTruckLane): MessageResponse {
        try {
            val trucks = truckService.updateTruckLaneByTruckLanceCode(
                request
            )
            val truck = trucks.firstOrNull()
            return MessageResponse(
                id = truck?.id,
                name = truck?.shopName,
                code = truck?.truckLanceCode,
                type = "truck",
                message = if (truck != null) "Truck lane updated successfully" else "Truck lane not found",
                errorPosition = null,
                entity = null
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

    @PostMapping("/updateLaneLogistic")
    fun updateLaneLogistic(@Valid @RequestBody request: UpdateLaneLogisticRequest): MessageResponse {
        try {
            val n = truckService.updateLogisticForEntireLane(request)
            return MessageResponse(
                id = null,
                name = null,
                code = request.truckLanceCode,
                type = "truck",
                message = "Updated logistic for $n truck row(s)",
                errorPosition = null,
                entity = null,
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null,
            )
        }
    }

    @PostMapping("/deleteTruckLane")
    fun deleteTruckLane(@Valid @RequestBody request: deleteTruckLane): MessageResponse {
        try {
            val result = truckService.deleteById(request.id)
            return MessageResponse(
                id = request.id,
                name = null,
                code = null,
                type = "truck",
                message = result,
                errorPosition = null,
                entity = null
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

    @GetMapping("/findAllUniqueTruckLanceCodeAndRemarkCombinations")
    fun findAllUniqueTruckLanceCodeAndRemarkCombinations(): List<TruckLaneCombinationResponse> {
        return truckService
            .findAllUniqueTruckLanceCodeAndRemarkCombinations()
            .map { it.toLaneCombinationResponse() }
    }


    @GetMapping("/findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse")
    fun findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(@RequestParam truckLanceCode: String): List<ShopAndTruck> {
        return truckService.findAllFromShopAndTruckByTruckLanceCodeAndDeletedFalse(truckLanceCode)
    }

    @GetMapping("/findAllByTruckLanceCodeAndDeletedFalse")
    fun findAllByTruckLanceCodeAndDeletedFalse(@RequestParam truckLanceCode: String): List<Truck> {
        return truckService.findAllByTruckLanceCodeAndDeletedFalse(truckLanceCode)
    }

    /**
     * Filter trucks by the same (truckLanceCode, remark) group as the unique-combinations query.
     * Omit `remark` or pass empty for rows with NULL/empty remark.
     */
    @GetMapping("/findAllByTruckLanceCodeAndRemarkAndDeletedFalse")
    fun findAllByTruckLanceCodeAndRemarkAndDeletedFalse(
        @RequestParam truckLanceCode: String,
        @RequestParam(required = false) remark: String?,
    ): List<Truck> {
        return truckService.findAllByTruckLanceCodeAndRemarkAndDeletedFalse(truckLanceCode, remark)
    }

    /**
     * RouteBoard O(1) load: return all truck rows (deleted=false) once.
     * Frontend groups by (truckLanceCode, normalizedRemark).
     */
    @GetMapping("/findAllForRouteBoard")
    fun findAllForRouteBoard(): List<Truck> {
        return truckService.findAllForRouteBoard()
    }

    @GetMapping("/findAllUniqueShopNamesAndCodesFromTrucks")
    fun findAllUniqueShopNamesAndCodesFromTrucks(): List<Map<String, String>> {
        return truckService.findAllUniqueShopNamesAndCodesFromTrucks()
    }

    @GetMapping("/findAllUniqueRemarksFromTrucks")
    fun findAllUniqueRemarksFromTrucks(): List<String> {
        return truckService.findAllUniqueRemarksFromTrucks()
    }

    @GetMapping("/findAllUniqueShopCodesFromTrucks")
    fun findAllUniqueShopCodesFromTrucks(): List<String> {
        return truckService.findAllUniqueShopCodesFromTrucks()
    }

    @GetMapping("/findAllUniqueShopNamesFromTrucks")
    fun findAllUniqueShopNamesFromTrucks(): List<String> {
        return truckService.findAllUniqueShopNamesFromTrucks()
    }

    @PostMapping("/updateTruckShopDetails")
    fun updateTruckShopDetails(@Valid @RequestBody request: UpdateTruckShopDetailsRequest): MessageResponse {
        try {
            val truck = truckService.updateTruckShopDetails(request)
            return MessageResponse(
                id = truck.id,
                name = truck.shopName,
                code = truck.truckLanceCode,
                type = "truck",
                message = "Truck shop details updated successfully",
                errorPosition = null,
                entity = truck
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

    @PostMapping("/createTruckWithoutShop")
    fun createTruckWithoutShop(@Valid @RequestBody request: CreateTruckWithoutShopRequest): MessageResponse {
        try {
            val truck = truckService.createTruckWithoutShop(request)
            return MessageResponse(
                id = truck.id,
                name = truck.shopName,
                code = truck.truckLanceCode,
                type = "truck",
                message = "Truck created successfully",
                errorPosition = null,
                entity = truck
            )
        } catch (e: Exception) {
            return MessageResponse(
                id = null,
                name = null,
                code = null,
                type = "truck",
                message = "Error: ${e.message}",
                errorPosition = null,
                entity = null
            )
        }
    }

}
