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

import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.TruckLaneScheduleStatus
import com.ffii.fpsms.modules.pickOrder.service.TruckLaneScheduleService
import com.ffii.fpsms.modules.pickOrder.web.models.*
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.ServletRequestBindingException
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartHttpServletRequest
import java.time.LocalDateTime

@RestController
@RequestMapping("/truckLaneSchedule")
open class TruckLaneScheduleController @Autowired constructor(
    private val truckLaneScheduleService: TruckLaneScheduleService,
) {
    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping
    open fun create(@Valid @RequestBody request: CreateTruckLaneScheduleRequest): TruckLaneScheduleResponse {
        return truckLaneScheduleService.createManual(request)
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/planFromRouteExcel")
    @Throws(ServletRequestBindingException::class)
    open fun planFromRouteExcel(request: HttpServletRequest): ResponseEntity<RouteExcelSchedulePlanResponse> {
        var workbook: Workbook? = null
        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
            return ResponseEntity.ok(truckLaneScheduleService.planFromRouteExcel(workbook))
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }

    @GetMapping
    open fun list(
        @RequestParam(required = false) status: List<String>?,
        @RequestParam(defaultValue = "50") limit: Int,
    ): List<TruckLaneScheduleResponse> {
        val statuses = status?.mapNotNull { raw ->
            runCatching { TruckLaneScheduleStatus.valueOf(raw.trim().uppercase()) }.getOrNull()
        }
        return truckLaneScheduleService.list(statuses, limit)
    }

    @GetMapping("/pendingShopIds")
    open fun pendingShopIds(): PendingTruckRowIdsResponse {
        return truckLaneScheduleService.pendingTruckRowIds()
    }

    @GetMapping("/{id}")
    open fun getById(@PathVariable id: Long): TruckLaneScheduleResponse {
        return truckLaneScheduleService.getById(id)
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/{id}/cancel")
    open fun cancel(@PathVariable id: Long): TruckLaneScheduleResponse {
        return truckLaneScheduleService.cancel(id)
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/{id}/applyNow")
    open fun applyNow(@PathVariable id: Long): TruckLaneScheduleResponse {
        return truckLaneScheduleService.applyNow(id)
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/{id}/retry-failed")
    open fun retryFailed(
        @PathVariable id: Long,
        @RequestBody(required = false) request: RetryFailedTruckLaneScheduleRequest?,
    ): TruckLaneScheduleResponse {
        return truckLaneScheduleService.retryFailed(id, request?.executeAt)
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/{id}/reactivate")
    open fun reactivateCancelled(
        @PathVariable id: Long,
        @RequestBody(required = false) request: RetryFailedTruckLaneScheduleRequest?,
    ): TruckLaneScheduleResponse {
        return truckLaneScheduleService.reactivateCancelled(id, request?.executeAt)
    }

    @PostMapping("/{id}/ignore")
    open fun ignore(@PathVariable id: Long): TruckLaneScheduleResponse {
        return truckLaneScheduleService.ignore(id)
    }

    @PostMapping("/parseExcel")
    @Throws(ServletRequestBindingException::class)
    open fun parseExcel(
        request: HttpServletRequest,
        @RequestParam(required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
        defaultExecuteAt: LocalDateTime?,
    ): ResponseEntity<ParseTruckLaneScheduleExcelResponse> {
        var workbook: Workbook? = null
        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
            return ResponseEntity.ok(
                truckLaneScheduleService.parseExcel(workbook, defaultExecuteAt),
            )
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }

    @PreAuthorize("hasAnyAuthority('ADMIN','TESTING')")
    @PostMapping("/importExcel")
    @Throws(ServletRequestBindingException::class)
    open fun importExcel(
        request: HttpServletRequest,
        @RequestParam(required = false)
        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
        defaultExecuteAt: LocalDateTime?,
        @RequestParam(required = false) note: String?,
    ): ResponseEntity<*> {
        var workbook: Workbook? = null
        try {
            val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
            workbook = XSSFWorkbook(multipartFile?.inputStream)
            val parsed = truckLaneScheduleService.parseExcel(
                workbook,
                defaultExecuteAt ?: LocalDateTime.now().plusMinutes(1),
            )
            if (parsed.validCount == 0) {
                return ResponseEntity.badRequest().body(
                    MessageResponse(
                        id = null,
                        name = null,
                        code = null,
                        type = "truckLaneSchedule",
                        message = "無有效列可匯入",
                        errorPosition = null,
                        entity = parsed,
                    ),
                )
            }
            val created = truckLaneScheduleService.createFromExcelRows(
                parsed.rows,
                defaultExecuteAt ?: LocalDateTime.now().plusMinutes(1),
                note,
            )
            return ResponseEntity.ok(created)
        } finally {
            try {
                workbook?.close()
            } catch (_: Exception) {
            }
        }
    }
}
