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 расписанию business-plan. */
interface PlannedPlanRepository : JpaRepository<PlannedPlanEntity, UUID> {
    /** Ищет persisted plan по стабильному business key. */
    fun findBySpacecraftIdAndStartTimeAndEndTimeAndKppId(
        spacecraftId: String,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        kppId: String
    ): PlannedPlanEntity?

    /** Возвращает планы КА в порядке старта. */
    fun findAllBySpacecraftIdOrderByStartTime(spacecraftId: String): List<PlannedPlanEntity>

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

    /** Возвращает будущие planned-планы КА. */
    fun findAllBySpacecraftIdAndStatusAndStartTimeAfterOrderByStartTime(
        spacecraftId: String,
        status: PlannedPlanStatus,
        startTime: LocalDateTime
    ): List<PlannedPlanEntity>

    /** Возвращает ближайший future planned plan для продвижения после позднего REJECTED. */
    fun findTopBySpacecraftIdAndStatusAndStartTimeAfterOrderByStartTime(
        spacecraftId: String,
        status: PlannedPlanStatus,
        startTime: LocalDateTime
    ): PlannedPlanEntity?

    /** Атомарно создаёт или обновляет PLANNED plan по business key, сохраняя stable planId. */
    @Query(
        value = """
            WITH upsert AS (
                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,
                    'PLANNED',
                    :issueNotBefore,
                    :basePlanId,
                    :chainVersion,
                    :now,
                    :now
                )
                ON CONFLICT (spacecraft_id, start_time, end_time, kpp_id) DO UPDATE SET
                    status = CASE
                        WHEN planned_plan.status IN ('SUPERSEDED', 'EXPIRED') THEN 'PLANNED'
                        ELSE planned_plan.status
                    END,
                    issue_not_before = CASE
                        WHEN planned_plan.status IN ('PLANNED', 'SUPERSEDED', 'EXPIRED') THEN EXCLUDED.issue_not_before
                        ELSE planned_plan.issue_not_before
                    END,
                    base_plan_id = CASE
                        WHEN planned_plan.status IN ('PLANNED', 'SUPERSEDED', 'EXPIRED') THEN EXCLUDED.base_plan_id
                        ELSE planned_plan.base_plan_id
                    END,
                    chain_version = CASE
                        WHEN planned_plan.status IN ('PLANNED', 'SUPERSEDED', 'EXPIRED') THEN EXCLUDED.chain_version
                        ELSE planned_plan.chain_version
                    END,
                    updated_at = CASE
                        WHEN planned_plan.status IN ('PLANNED', 'SUPERSEDED', 'EXPIRED') THEN EXCLUDED.updated_at
                        ELSE planned_plan.updated_at
                    END
                RETURNING *
            )
            SELECT *
            FROM upsert
        """,
        nativeQuery = true
    )
    fun upsertPlannedByBusinessKey(
        @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 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?

    /** Claim due plans через PostgreSQL row locks, чтобы несколько pod не выдали один plan дважды. */
    @Query(
        value = """
            SELECT *
            FROM planned_plan
            WHERE status = 'PLANNED'
              AND (issue_not_before IS NULL OR issue_not_before <= :now)
              AND start_time <= :issueBefore
              AND start_time > :now
            ORDER BY start_time
            LIMIT :limit
            FOR UPDATE SKIP LOCKED
        """,
        nativeQuery = true
    )
    fun findDueForUpdate(
        @Param("now") now: LocalDateTime,
        @Param("issueBefore") issueBefore: LocalDateTime,
        @Param("limit") limit: Int
    ): List<PlannedPlanEntity>
}
