package space.nstart.pcp_tgu_service.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import space.nstart.pcp_tgu_service.entity.PlannedPlanEntity
import space.nstart.pcp_tgu_service.entity.PlannedPlanStatus
import java.time.LocalDateTime
import java.util.UUID

/** Доступ к persisted планам (только выданные и их история; будущее не хранится). */
interface PlannedPlanRepository : JpaRepository<PlannedPlanEntity, UUID> {
    /** Возвращает планы КА в порядке старта (для read-проекции и истории). */
    fun findAllBySpacecraftIdOrderByStartTime(spacecraftId: String): List<PlannedPlanEntity>

    /** Планы, порождённые от данного базового плана с указанным статусом (для гарда дублей DRAFT). */
    fun findAllByBasePlanIdAndStatus(basePlanId: UUID, status: PlannedPlanStatus): List<PlannedPlanEntity>

    /**
     * Планы КА в одном из статусов — для вычисления «в полёте» (ISSUING/WAITING_DECISION) и набора
     * конкурентов при закладке (supersede). Заменяет опору на единственный active-слот состояния.
     */
    fun findAllBySpacecraftIdAndStatusIn(
        spacecraftId: String,
        statuses: Collection<PlannedPlanStatus>
    ): List<PlannedPlanEntity>

    /**
     * Планы КА с точным совпадением окна (старт+конец) и статусом — гард на дубли черновиков:
     * сочетание (КА + начало + конец) уникально среди DRAFT.
     */
    fun findAllBySpacecraftIdAndStartTimeAndEndTimeAndStatus(
        spacecraftId: String,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        status: PlannedPlanStatus
    ): List<PlannedPlanEntity>

    /** Возвращает все планы в порядке старта (для read-проекции и истории). */
    fun findAllByOrderByStartTime(): List<PlannedPlanEntity>

    /**
     * Создаёт новую строку выдаваемого плана сразу в статусе ISSUING.
     *
     * В on-demand-модели каждая выдача — это INSERT с новым plan_id; апсерта по бизнес-ключу нет,
     * повторная выдача того же окна после негатива создаёт отдельную строку (история хранится вечно).
     */
    @Query(
        value = """
            INSERT INTO planned_plan(
                plan_id,
                spacecraft_id,
                start_time,
                end_time,
                kpp_id,
                status,
                issue_not_before,
                base_plan_id,
                chain_version,
                created_at,
                updated_at
            )
            VALUES (
                :planId,
                :spacecraftId,
                :startTime,
                :endTime,
                :kppId,
                'ISSUING',
                :issueNotBefore,
                :basePlanId,
                :chainVersion,
                :now,
                :now
            )
            RETURNING *
        """,
        nativeQuery = true
    )
    fun insertIssuingPlan(
        @Param("planId") planId: UUID,
        @Param("spacecraftId") spacecraftId: String,
        @Param("startTime") startTime: LocalDateTime,
        @Param("endTime") endTime: LocalDateTime,
        @Param("kppId") kppId: String,
        @Param("issueNotBefore") issueNotBefore: LocalDateTime,
        @Param("basePlanId") basePlanId: UUID?,
        @Param("chainVersion") chainVersion: Long,
        @Param("now") now: LocalDateTime
    ): PlannedPlanEntity

    /** Блокирует plan для обработки decision/feedback event или issue state transition. */
    @Query(
        value = """
            SELECT *
            FROM planned_plan
            WHERE plan_id = :planId
            FOR UPDATE
        """,
        nativeQuery = true
    )
    fun lockByPlanId(@Param("planId") planId: UUID): PlannedPlanEntity?
}
