package space.nstart.pcp.complan

import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
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.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
import space.nstart.pcp.complan.apl.BasicPlanCalculator
import space.nstart.pcp.complan.service.DynamicPlanRunService
import space.nstart.pcp.complan.types.ComplexPlanCalculationRequestDTO
import space.nstart.pcp.complan.types.ComplexPlanCalculationResultDTO
import space.nstart.pcp.complan.types.DynamicPlanIntervalDTO
import space.nstart.pcp.complan.types.DynamicPlanRouteDTO
import space.nstart.pcp.complan.types.DynamicPlanRunDTO
import space.nstart.pcp.complan.types.DynamicPlanRunStatus
import space.nstart.pcp.complan.types.GreedyMathDebugRequestDTO
import space.nstart.pcp.complan.types.GreedyMathDebugResultDTO
import space.nstart.pcp.complan.types.ObservationParametersDebugRequestDTO
import space.nstart.pcp.complan.types.ObservationParametersDebugResultDTO
import space.nstart.pcp.complan.types.TestCalcMarsDebugRequestDTO
import space.nstart.pcp.complan.types.TestCalcMarsDebugResultDTO
import java.nio.file.NoSuchFileException
import java.util.UUID

@RestController
@RequestMapping("/v1/main")
@Tag(name = "Расчет комплексного плана", description = "API расчета комплексного плана")
class ComPlanController(
    private val dynamicPlanRunService: DynamicPlanRunService,
    private val basicPlanCalculator: BasicPlanCalculator
) {

    @PostMapping("/calcPlan")
    @ResponseStatus(HttpStatus.ACCEPTED)
    fun calcPlan(
        @RequestBody request: ComplexPlanCalculationRequestDTO
    ): DynamicPlanRunDTO = dynamicPlanRunService.startCalculation(request)

    @GetMapping("/calcPlan/runs")
    fun getCalculations(
        @RequestParam(required = false) status: DynamicPlanRunStatus?,
        @RequestParam(defaultValue = "50") limit: Int,
        @RequestParam(defaultValue = "0") offset: Int
    ): List<DynamicPlanRunDTO> = dynamicPlanRunService.listRuns(status, limit, offset)

    @GetMapping("/calcPlan/{runId}")
    fun getCalculation(
        @PathVariable runId: UUID
    ): DynamicPlanRunDTO = dynamicPlanRunService.getRun(runId)

    @GetMapping("/calcPlan/{runId}/result")
    fun getCalculationResult(
        @PathVariable runId: UUID
    ): ComplexPlanCalculationResultDTO = dynamicPlanRunService.getResult(runId)

    @GetMapping("/calcPlan/{runId}/routes")
    fun getCalculationRoutes(
        @PathVariable runId: UUID,
        @RequestParam(defaultValue = "1000") limit: Int,
        @RequestParam(defaultValue = "0") offset: Int
    ): List<DynamicPlanRouteDTO> = dynamicPlanRunService.getRoutes(runId, limit, offset)

    @GetMapping("/calcPlan/{runId}/intervals")
    fun getCalculationIntervals(
        @PathVariable runId: UUID,
        @RequestParam(defaultValue = "1000") limit: Int,
        @RequestParam(defaultValue = "0") offset: Int
    ): List<DynamicPlanIntervalDTO> = dynamicPlanRunService.getIntervals(runId, limit, offset)

    @PostMapping("/calcPlan/debug/observation-parameters")
    fun debugObservationParameters(
        @RequestBody request: ObservationParametersDebugRequestDTO
    ): ObservationParametersDebugResultDTO {
        if (request.calculationEnd <= request.calculationStart) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "calculationEnd must be after calculationStart")
        }
        if (request.satelliteIds.isEmpty()) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "satelliteIds must not be empty")
        }
        if (request.chunkSize != null && request.chunkSize < 1) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "chunkSize must be greater than or equal to 1")
        }
        if (request.parallelism != null && request.parallelism < 1) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "parallelism must be greater than or equal to 1")
        }
        return basicPlanCalculator.calculateObservationParametersDebug(request)
    }

    @PostMapping("/calcPlan/debug/greedy-math")
    fun debugGreedyMath(
        @RequestBody request: GreedyMathDebugRequestDTO
    ): GreedyMathDebugResultDTO {
        if (request.calculationEnd <= request.calculationStart) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "calculationEnd must be after calculationStart")
        }
        if (request.satelliteIds.isEmpty()) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "satelliteIds must not be empty")
        }
        return try {
            basicPlanCalculator.calculateGreedyMathDebug(request)
        } catch (error: NoSuchFileException) {
            throw ResponseStatusException(
                HttpStatus.BAD_REQUEST,
                "observation parameters file not found: ${error.file}",
                error
            )
        }
    }

    @PostMapping("/calcPlan/debug/testcalcMars")
    fun testCalcMars(
        @RequestBody request: TestCalcMarsDebugRequestDTO
    ): TestCalcMarsDebugResultDTO {
        if (request.calculationEnd <= request.calculationStart) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "calculationEnd must be after calculationStart")
        }
        if (request.satelliteIds.isEmpty()) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "satelliteIds must not be empty")
        }
        return try {
            basicPlanCalculator.calculateTestCalcMarsDebug(request)
        } catch (error: NoSuchFileException) {
            throw ResponseStatusException(
                HttpStatus.BAD_REQUEST,
                "observation parameters file not found: ${error.file}",
                error
            )
        }
    }
}
