package com.ffii.fpsms.modules.chart.web

import com.ffii.fpsms.modules.chart.service.ChartService
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDate

@RestController
@RequestMapping("/chart")
class ChartController(
    private val chartService: ChartService,
) {

    /**
     * GET /chart/stock-transactions-by-date?startDate=2025-01-01&endDate=2025-01-31
     * Returns [{ date, inQty, outQty, totalQty }]
     */
    @GetMapping("/stock-transactions-by-date")
    fun getStockTransactionsByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getStockTransactionsByDate(startDate, endDate)

    /**
     * GET /chart/delivery-order-by-date?startDate=&endDate=
     * Returns [{ date, orderCount, totalQty }]. Date axis: delivery_order.estimatedArrivalDate only (null ETA excluded).
     */
    @GetMapping("/delivery-order-by-date")
    fun getDeliveryOrderByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getDeliveryOrderByDate(startDate, endDate)

    /**
     * GET /chart/purchase-order-by-status?targetDate=2025-03-15&supplierId=1&supplierId=2&itemCode=A&purchaseOrderNo=PO001
     * Returns [{ status, count }]. targetDate defaults to today on frontend; when set, only POs with orderDate on that date.
     */
    @GetMapping("/purchase-order-by-status")
    fun getPurchaseOrderByStatus(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false) supplierId: List<Long>?,
        @RequestParam(required = false) itemCode: List<String>?,
        @RequestParam(required = false) purchaseOrderNo: List<String>?,
    ): List<Map<String, Any>> =
        chartService.getPurchaseOrderByStatus(targetDate, supplierId, itemCode, purchaseOrderNo)

    /**
     * GET /chart/purchase-order-filter-options?targetDate=2025-03-15
     * Returns { suppliers, items, poNos } for multi-select (POs with orderDate on targetDate, deleted = 0).
     */
    @GetMapping("/purchase-order-filter-options")
    fun getPurchaseOrderFilterOptions(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
    ): Map<String, Any> = chartService.getPurchaseOrderFilterOptions(targetDate)

    /**
     * GET /chart/purchase-order-estimated-arrival-summary?targetDate=2025-03-15
     * 預計送貨: POs with estimatedArrivalDate on targetDate; buckets cancelled / delivered / not_delivered / other.
     */
    @GetMapping("/purchase-order-estimated-arrival-summary")
    fun getPurchaseOrderEstimatedArrivalSummary(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false) supplierId: List<Long>?,
        @RequestParam(required = false) itemCode: List<String>?,
        @RequestParam(required = false) purchaseOrderNo: List<String>?,
    ): List<Map<String, Any>> =
        chartService.getPurchaseOrderEstimatedArrivalSummary(targetDate, supplierId, itemCode, purchaseOrderNo)

    /**
     * GET /chart/purchase-order-estimated-arrival-breakdown?targetDate=&estimatedArrivalBucket=not_delivered
     * Related suppliers / items / POs for that 預計送貨 segment (same filters as summary).
     */
    @GetMapping("/purchase-order-estimated-arrival-breakdown")
    fun getPurchaseOrderEstimatedArrivalBreakdown(
        @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate,
        @RequestParam estimatedArrivalBucket: String,
        @RequestParam(required = false) supplierId: List<Long>?,
        @RequestParam(required = false) itemCode: List<String>?,
        @RequestParam(required = false) purchaseOrderNo: List<String>?,
    ): Map<String, Any> =
        chartService.getPurchaseOrderEstimatedArrivalBreakdown(targetDate, estimatedArrivalBucket, supplierId, itemCode, purchaseOrderNo)

    /**
     * GET /chart/purchase-order-details-by-status?status=pending&targetDate=2025-03-15
     * Returns PO header rows for status drill-down.
     */
    @GetMapping("/purchase-order-details-by-status")
    fun getPurchaseOrderDetailsByStatus(
        @RequestParam status: String,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false, defaultValue = "order") dateFilter: String,
        @RequestParam(required = false) supplierId: List<Long>?,
        @RequestParam(required = false) itemCode: List<String>?,
        @RequestParam(required = false) purchaseOrderNo: List<String>?,
        @RequestParam(required = false) supplierCode: String?,
        @RequestParam(required = false) estimatedArrivalBucket: String?,
    ): List<Map<String, Any>> =
        chartService.getPurchaseOrderDetailsByStatus(status, targetDate, dateFilter, supplierId, itemCode, purchaseOrderNo, supplierCode, estimatedArrivalBucket)

    /**
     * GET /chart/purchase-order-items?purchaseOrderId=123
     * Returns PO item line rows with ordered/received/pending qty and uom.
     */
    @GetMapping("/purchase-order-items")
    fun getPurchaseOrderItems(
        @RequestParam purchaseOrderId: Long,
    ): List<Map<String, Any>> =
        chartService.getPurchaseOrderItems(purchaseOrderId)

    /**
     * GET /chart/purchase-order-items-by-status?status=completed&targetDate=2025-03-15
     * Returns item rows aggregated across matching purchase orders.
     */
    @GetMapping("/purchase-order-items-by-status")
    fun getPurchaseOrderItemsByStatus(
        @RequestParam status: String,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false, defaultValue = "order") dateFilter: String,
        @RequestParam(required = false) supplierId: List<Long>?,
        @RequestParam(required = false) itemCode: List<String>?,
        @RequestParam(required = false) purchaseOrderNo: List<String>?,
        @RequestParam(required = false) supplierCode: String?,
        @RequestParam(required = false) estimatedArrivalBucket: String?,
    ): List<Map<String, Any>> =
        chartService.getPurchaseOrderItemsByStatus(status, targetDate, dateFilter, supplierId, itemCode, purchaseOrderNo, supplierCode, estimatedArrivalBucket)

    /**
     * GET /chart/stock-in-out-by-date?startDate=&endDate=
     * Returns [{ date, inQty, outQty }]. Date range pushed into each UNION branch; si/so read before lines.
     */
    @GetMapping("/stock-in-out-by-date")
    fun getStockInOutByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getStockInOutByDate(startDate, endDate)

    /**
     * GET /chart/top-delivery-items-item-options?startDate=&endDate=
     * Returns [{ itemCode, itemName }] — distinct items in delivery lines in the period (for multi-select).
     * Period: delivery_order.estimatedArrivalDate only (null ETA excluded).
     */
    @GetMapping("/top-delivery-items-item-options")
    fun getTopDeliveryItemsItemOptions(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getTopDeliveryItemsItemOptions(startDate, endDate)

    /**
     * GET /chart/top-delivery-items?startDate=&endDate=&limit=20&itemCode=A&itemCode=B
     * Returns [{ itemCode, itemName, totalQty }]. When itemCode present, only those items (still by totalQty, limit).
     * Period: delivery_order.estimatedArrivalDate only (null ETA excluded).
     */
    @GetMapping("/top-delivery-items")
    fun getTopDeliveryItems(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
        @RequestParam(required = false, defaultValue = "10") limit: Int,
        @RequestParam(required = false) itemCode: List<String>?,
    ): List<Map<String, Any>> =
        chartService.getTopDeliveryItems(startDate, endDate, limit.coerceIn(1, 200), itemCode)

    /**
     * GET /chart/stock-balance-trend?startDate=&endDate=&itemCode=
     * Returns [{ date, balance }]
     */
    @GetMapping("/stock-balance-trend")
    fun getStockBalanceTrend(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
        @RequestParam(required = false) itemCode: String?,
    ): List<Map<String, Any>> = chartService.getStockBalanceTrend(startDate, endDate, itemCode)

    /**
     * GET /chart/consumption-trend-by-month?year=&startDate=&endDate=&itemCode=
     * Returns [{ month, outQty }]
     */
    @GetMapping("/consumption-trend-by-month")
    fun getConsumptionTrendByMonth(
        @RequestParam(required = false) year: Int?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
        @RequestParam(required = false) itemCode: String?,
    ): List<Map<String, Any>> =
        chartService.getConsumptionTrendByMonth(year, startDate, endDate, itemCode)

    /**
     * GET /chart/staff-delivery-performance-handlers — staff from user table (staffNo, name), only those with staffNo.
     */
    @GetMapping("/staff-delivery-performance-handlers")
    fun getStaffDeliveryPerformanceHandlers(): List<Map<String, Any>> =
        chartService.getStaffDeliveryPerformanceHandlers()

    /**
     * GET /chart/staff-delivery-performance?startDate=&endDate=&staffNo=A001&staffNo=A002&storeId=2/F&storeIdNull=true
     * Returns [{ date, staffName, orderCount, totalMinutes }]. Data from delivery_order_pick_order
     * (handledBy), orderCount = completed pick tickets, totalMinutes = sum(ticketCompleteDateTime - ticketReleaseTime).
     * Optional storeId filters delivery_order_pick_order.storeId; storeIdNull=true means IS NULL (overrides storeId).
     */
    @GetMapping("/staff-delivery-performance")
    fun getStaffDeliveryPerformance(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
        @RequestParam(required = false) staffNo: List<String>?,
        @RequestParam(required = false) storeId: String?,
        @RequestParam(required = false) storeIdNull: Boolean?,
    ): List<Map<String, Any>> =
        chartService.getStaffDeliveryPerformance(startDate, endDate, staffNo, storeId, storeIdNull)

    // ---------- Job order reports ----------

    /** GET /chart/job-order-by-status?targetDate= — count by status, optional planStart date filter. */
    @GetMapping("/job-order-by-status")
    fun getJobOrderByStatus(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobOrderByStatus(targetDate)

    /** GET /chart/job-order-count-by-date?startDate=&endDate= — count by planStart date. */
    @GetMapping("/job-order-count-by-date")
    fun getJobOrderCountByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobOrderCountByDate(startDate, endDate)

    /** GET /chart/job-order-created-completed-by-date?startDate=&endDate= — created vs completed by date. */
    @GetMapping("/job-order-created-completed-by-date")
    fun getJobOrderCreatedCompletedByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobOrderCreatedCompletedByDate(startDate, endDate)

    /** GET /chart/job-material-pending-picked-by-date?startDate=&endDate= — material 待領/已揀 by date. */
    @GetMapping("/job-material-pending-picked-by-date")
    fun getJobMaterialPendingPickedByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobMaterialPendingPickedByDate(startDate, endDate)

    /** GET /chart/job-process-pending-completed-by-date?startDate=&endDate= — process 待完成/已完成 by date. */
    @GetMapping("/job-process-pending-completed-by-date")
    fun getJobProcessPendingCompletedByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobProcessPendingCompletedByDate(startDate, endDate)

    /** GET /chart/job-equipment-working-worked-by-date?startDate=&endDate= — equipment 使用中/已使用 by date. */
    @GetMapping("/job-equipment-working-worked-by-date")
    fun getJobEquipmentWorkingWorkedByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getJobEquipmentWorkingWorkedByDate(startDate, endDate)

    /**
     * GET /chart/equipment-usage-board?targetDate=
     * — equipment usage for the day: [job_order_process_detail] (legacy) **union** [productprocessline] (工藝流程),
     * matching `/jo/edit` line equipment. Omit targetDate = server today.
     */
    @GetMapping("/equipment-usage-board")
    fun getEquipmentUsageBoard(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getEquipmentUsageBoardRows(targetDate)

    /**
     * GET /chart/job-order-board?targetDate=&incompleteOnly=
     * — per-job rows for status board (optional planStart date; incompleteOnly excludes status completed).
     */
    @GetMapping("/job-order-board")
    fun getJobOrderBoard(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false, defaultValue = "false") incompleteOnly: Boolean,
    ): List<Map<String, Any>> = chartService.getJobOrderBoardRows(targetDate, incompleteOnly)

    /**
     * GET /chart/process-board?targetDate=&incompleteOnly=
     * — per job_order_process row: job, master process, times, derived boardStatus (pending / in_progress / completed)
     *   from product process lines when job_order_process times are unset.
     */
    @GetMapping("/process-board")
    fun getProcessBoard(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) targetDate: LocalDate?,
        @RequestParam(required = false, defaultValue = "false") incompleteOnly: Boolean,
    ): List<Map<String, Any>> = chartService.getProcessBoardRows(targetDate, incompleteOnly)

    // ---------- Forecast & planning ----------

    /** GET /chart/production-schedule-by-date?startDate=&endDate= — schedule count & total est prod by produce date. */
    @GetMapping("/production-schedule-by-date")
    fun getProductionScheduleByDate(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getProductionScheduleByDate(startDate, endDate)

    /** GET /chart/planned-daily-output-by-item?limit=20 — top items by planned daily qty (item_daily_out). */
    @GetMapping("/planned-daily-output-by-item")
    fun getPlannedDailyOutputByItem(
        @RequestParam(required = false, defaultValue = "20") limit: Int,
    ): List<Map<String, Any>> = chartService.getPlannedDailyOutputByItem(limit.coerceIn(1, 200))

    /** GET /chart/planned-output-by-date-and-item?startDate=&endDate= — all items' planned production by date (production_schedule). */
    @GetMapping("/planned-output-by-date-and-item")
    fun getPlannedOutputByDateAndItem(
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) startDate: LocalDate?,
        @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) endDate: LocalDate?,
    ): List<Map<String, Any>> = chartService.getPlannedOutputByDateAndItem(startDate, endDate)
}
