package space.nstart.pcp.pcp_mission_planing_service.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.ArraySchema
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
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.RestController
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.ModeResponseDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.DropRouteSaveRequestDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.MissionRequestDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.MissionSurveyCalculationResponseDTO
import space.nstart.pcp.pcp_types_lib.dto.satellite.mission.SurveyRouteBuildRequestDTO
import space.nstart.pcp.pcp_mission_planing_service.service.MissionPlaningService
import java.time.LocalDateTime
import java.util.UUID


@RestController
@RequestMapping("/api/missions")
@Tag(name = "Missions", description = "Управление миссиями и режимами")
class MissionController(
    private val missionPlaningService: MissionPlaningService
) {

    @Operation(
        summary = "Построить один маршрут съёмки",
        description = "Строит контур полосы съёмки по параметрам видимости точки (момент, крен, угол захвата). " +
            "Орбитальные точки берутся из pcp-ballistics-service, контур — тем же алгоритмом, что и при расчёте плана съёмки. " +
            "Маршрут не сохраняется в БД, только возвращается клиенту (live-превью)."
    )
    @PostMapping("/survey-route/build")
    fun buildSurveyRoute(@Valid @RequestBody body: SurveyRouteBuildRequestDTO) =
        missionPlaningService.buildSurveyRoute(body)

    @Operation(
        summary = "Построить и сохранить маршрут съёмки по выбранному плану",
        description = "Строит контур полосы съёмки тем же алгоритмом, что и /survey-route/build, " +
            "и сохраняет его в БД как режим съёмки (source = MANUAL) под основным планом миссии. " +
            "planId — идентификатор выбранного плана в pcp-tgu-service, совпадает с mission_id этого сервиса. " +
            "Адресуется существующая миссия-черновик (DRAFT-клон); если миссии с таким id нет — 404."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "200", description = "Маршрут построен и сохранён")
        ]
    )
    @PostMapping("/{plan_id}/survey-route")
    fun saveSurveyRoute(
        @Parameter(description = "Идентификатор выбранного плана (= mission_id)")
        @PathVariable("plan_id") planId: UUID,
        @Valid @RequestBody body: SurveyRouteBuildRequestDTO
    ) = missionPlaningService.saveSurveyRoute(planId, body)

    @Operation(
        summary = "Добавить сброс вручную по выбранной зоне радиовидимости",
        description = "Сохраняет режим сброса (DROP) под основным планом миссии по выбранной оператором ЗРВ " +
            "и набору маршрутов съёмки, заканчивающихся строго до начала зоны. " +
            "planId — идентификатор выбранного плана в pcp-tgu-service (= mission_id). " +
            "Адресуется существующая миссия-черновик (DRAFT-клон); если миссии с таким id нет — 404."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "200", description = "Сброс добавлен")
        ]
    )
    @PostMapping("/{plan_id}/drop-route")
    fun saveDropRoute(
        @Parameter(description = "Идентификатор выбранного плана (= mission_id)")
        @PathVariable("plan_id") planId: UUID,
        @Valid @RequestBody body: DropRouteSaveRequestDTO
    ) = missionPlaningService.saveDropRoute(planId, body)

    @Operation(summary = "Получить список миссий")
    @GetMapping
    fun all() = missionPlaningService.all()

    @Operation(summary = "Получить миссию по id")
    @GetMapping("/{mission_id}")
    fun byId(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID
    ) = missionPlaningService.byId(id)

    @Operation(summary = "Получить режимы миссии")
    @ApiResponses(
        value = [
            ApiResponse(
                responseCode = "200",
                description = "Список режимов (SURVEY и Drop)",
                content = [
                    Content(
                        mediaType = "application/json",
                        array = ArraySchema(schema = Schema(implementation = ModeResponseDTO::class))
                    )
                ]
            ),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @GetMapping("/{mission_id}/modes")
    fun modes(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID
    ) = missionPlaningService.modesByMission(id)

    @Operation(summary = "Получить режимы аппарата за интервал времени")
    @ApiResponses(
        value = [
            ApiResponse(
                responseCode = "200",
                description = "Список режимов (SURVEY и DROP) для аппарата в указанном интервале",
                content = [
                    Content(
                        mediaType = "application/json",
                        array = ArraySchema(schema = Schema(implementation = ModeResponseDTO::class))
                    )
                ]
            ),
            ApiResponse(responseCode = "400", description = "Некорректный интервал времени")
        ]
    )
    @GetMapping("/modes/satellite")
    fun modesBySatelliteAndInterval(
        @Parameter(description = "Идентификатор аппарата")
        @RequestParam("satellite_id") satelliteId: Long,
        @Parameter(description = "Начало интервала")
        @RequestParam timeStart: LocalDateTime,
        @Parameter(description = "Конец интервала")
        @RequestParam timeStop: LocalDateTime
    ) = missionPlaningService.modesBySatelliteAndInterval(satelliteId, timeStart, timeStop)

    @Operation(summary = "Запустить расчет сбросов для миссии")
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "204", description = "Расчет выполнен"),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @PostMapping("/{mission_id}/drops/calculate")
    fun calculateDrops(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID
    ): ResponseEntity<Void> {
        missionPlaningService.calculateDrops(id)
        return ResponseEntity.noContent().build()
    }


    @Operation(summary = "Запустить расчет плана съемки для миссии")
    @ApiResponses(
        value = [
            ApiResponse(
                responseCode = "200",
                description = "Расчет выполнен",
                content = [
                    Content(
                        mediaType = "application/json",
                        schema = Schema(implementation = MissionSurveyCalculationResponseDTO::class)
                    )
                ]
            ),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @PostMapping("/{mission_id}/surveys/calculate")
    fun calculateSurveys(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID,
        @Parameter(description = "Конкретный snapshot этапа 4")
        @RequestParam(name = "comPlanSnapshotId", required = false) comPlanSnapshotId: Long?
    ): MissionSurveyCalculationResponseDTO =
        missionPlaningService.loadSlots(id, comPlanSnapshotId = comPlanSnapshotId)

    @Operation(summary = "Подтвердить миссию")
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "204", description = "Миссия подтверждена"),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @PostMapping("/{mission_id}/confirm")
    fun confirmMission(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID
    ): ResponseEntity<Void> {
        missionPlaningService.confirmMission(id)
        return ResponseEntity.noContent().build()
    }

    @Operation(summary = "Изменить статус режима съемки")
    @PutMapping("/survey-modes/{mode_id}/status")
    fun updateSurveyModeStatus(
        @PathVariable("mode_id") id: Long,
        @Valid @RequestBody body: SurveyModeStatusUpdateRequest
    ): ResponseEntity<Void> {
        missionPlaningService.updateSurveyModeStatus(id, body.status)
        return ResponseEntity.noContent().build()
    }

    @Operation(
        summary = "Перестроить маршрут съёмки",
        description = "Перестраивает контур существующего режима съёмки по новым параметрам (окно/крен/" +
            "угол захвата) тем же алгоритмом, что и /survey-route/build, и сохраняет результат на месте. " +
            "Используется для правки включения в плане-черновике."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "200", description = "Маршрут перестроен"),
            ApiResponse(responseCode = "404", description = "Режим съёмки не найден")
        ]
    )
    @PatchMapping("/survey-modes/{mode_id}")
    fun updateSurveyRoute(
        @Parameter(description = "Идентификатор режима съёмки")
        @PathVariable("mode_id") modeId: Long,
        @Valid @RequestBody body: SurveyRouteBuildRequestDTO
    ) = missionPlaningService.updateSurveyRoute(modeId, body)

    @Operation(
        summary = "Удалить включение (съёмку или сброс)",
        description = "Удаляет режим (SURVEY или DROP) из плана-черновика по его id. Для съёмки снимаются " +
            "брони слотов и связи со сбросами, осиротевший сброс (без единой съёмки) тоже удаляется. " +
            "Для сброса удаляются только его связи; съёмки сохраняются."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "204", description = "Включение удалено"),
            ApiResponse(responseCode = "404", description = "Режим не найден")
        ]
    )
    @DeleteMapping("/modes/{mode_id}")
    fun deleteMode(
        @Parameter(description = "Идентификатор режима (survey или drop)")
        @PathVariable("mode_id") modeId: Long
    ): ResponseEntity<Void> {
        missionPlaningService.deleteMode(modeId)
        return ResponseEntity.noContent().build()
    }

    @Operation(
        summary = "Очистить включения плана",
        description = "Удаляет включения плана миссии вместе с бронями слотов и связями съёмка↔сброс. " +
            "Сбросы удаляются всегда. Для съёмок поведение выбирает оператор флагом keepManual: " +
            "keepManual=true — ручные маршруты съёмки (source=MANUAL) сохраняются, удаляются только " +
            "рассчитанные (как при пересчёте плана); keepManual=false (по умолчанию) — удаляются ВСЕ " +
            "съёмки, включая ручные. Используется кнопкой «Очистить план» в редакторе."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "204", description = "План очищен"),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @DeleteMapping("/{mission_id}/modes")
    fun clearPlan(
        @Parameter(description = "Идентификатор миссии")
        @PathVariable("mission_id") id: UUID,
        @Parameter(description = "Сохранить ручные маршруты съёмки (source=MANUAL)")
        @RequestParam(name = "keepManual", required = false, defaultValue = "false") keepManual: Boolean
    ): ResponseEntity<Void> {
        missionPlaningService.clearPlan(id, keepManual)
        return ResponseEntity.noContent().build()
    }

    @Operation(
        summary = "Проверить миссию-черновик на конфликты",
        description = "Считает конфликты/наложения включений плана-черновика и возвращает дифф по " +
            "режимам. MVP: валидатор-заглушка — контракт корректен, список конфликтов пуст; " +
            "реальные правила (пересечения по времени, видимость цели, ЗРВ, порядок сброса, борт) — " +
            "отдельной задачей."
    )
    @ApiResponses(
        value = [
            ApiResponse(responseCode = "200", description = "Валидация выполнена, конфликты в теле ответа"),
            ApiResponse(responseCode = "404", description = "Миссия не найдена")
        ]
    )
    @PostMapping("/{mission_id}/validate")
    fun validateMission(
        @Parameter(description = "Идентификатор миссии-черновика")
        @PathVariable("mission_id") id: UUID
    ): MissionValidationResponse = missionPlaningService.validateMission(id)



    @Operation(summary = "Создать миссию")
    @PostMapping
    fun add(@Valid @RequestBody body: MissionRequestDTO) = missionPlaningService.add(body)

    @Operation(summary = "Обновить миссию")
    @PutMapping("/{mission_id}")
    fun update(@PathVariable("mission_id") id: UUID, @Valid @RequestBody body: MissionRequestDTO) =
        missionPlaningService.update(id, body)

    @Operation(summary = "Удалить миссию")
    @DeleteMapping("/{mission_id}")
    fun delete(@PathVariable("mission_id") id: UUID): ResponseEntity<Void> {
        missionPlaningService.delete(id)
        return ResponseEntity.noContent().build()
    }

    @Operation(
        summary = "Клонировать миссию",
        description = "Создаёт полный (replace) или частичный (fork-forward) клон миссии с новым missionId. " +
            "Для fork-forward клонируются только включения, перекрывающиеся с окном [windowStart, windowStop). " +
            "Возвращает newMissionId, который tgu-service использует как planId черновика."
    )
    @PostMapping("/{mission_id}/clone")
    fun cloneMission(
        @PathVariable("mission_id") id: UUID,
        @RequestBody body: MissionCloneRequest
    ): MissionCloneResponse = missionPlaningService.cloneMission(id, body)

    @Operation(
        summary = "Создать пустую миссию",
        description = "Создаёт миссию без режимов на заданное окно — для плана новой цепочки «без истории». " +
            "Возвращает newMissionId, который tgu-service использует как planId черновика."
    )
    @PostMapping("/blank")
    fun createBlankMission(@RequestBody body: MissionBlankRequest): MissionCloneResponse =
        missionPlaningService.createBlankMission(body)
}
