package space.nstart.pcp_tgu_service.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import space.nstart.pcp_tgu_service.entity.PlanRecalculationJobEntity
import java.time.LocalDateTime

/** Доступ к jobs перестроения расписания. */
interface PlanRecalculationJobRepository : JpaRepository<PlanRecalculationJobEntity, String> {
    /** Атомарно ставит или переоткрывает coalesced recalculation job по spacecraftId. */
    @Modifying
    @Query(
        value = """
            INSERT INTO plan_recalculation_job(
                spacecraft_id,
                status,
                reason,
                locked_by,
                locked_until,
                rerun_requested,
                requested_at,
                updated_at
            )
            VALUES (
                :spacecraftId,
                'PENDING',
                :reason,
                NULL,
                NULL,
                FALSE,
                :now,
                :now
            )
            ON CONFLICT (spacecraft_id) DO UPDATE SET
                status = CASE
                    WHEN plan_recalculation_job.status = 'RUNNING' THEN plan_recalculation_job.status
                    ELSE 'PENDING'
                END,
                reason = EXCLUDED.reason,
                locked_by = CASE
                    WHEN plan_recalculation_job.status = 'RUNNING' THEN plan_recalculation_job.locked_by
                    ELSE NULL
                END,
                locked_until = CASE
                    WHEN plan_recalculation_job.status = 'RUNNING' THEN plan_recalculation_job.locked_until
                    ELSE NULL
                END,
                rerun_requested = CASE
                    WHEN plan_recalculation_job.status = 'RUNNING' THEN TRUE
                    ELSE FALSE
                END,
                requested_at = EXCLUDED.requested_at,
                updated_at = EXCLUDED.updated_at
        """,
        nativeQuery = true
    )
    fun upsertPending(
        @Param("spacecraftId") spacecraftId: String,
        @Param("reason") reason: String,
        @Param("now") now: LocalDateTime
    ): Int

    /** Claim pending или просроченные running jobs через row locks для горизонтального scale-out. */
    @Query(
        value = """
            SELECT *
            FROM plan_recalculation_job
            WHERE status = 'PENDING'
               OR (
                    status = 'RUNNING'
                    AND locked_until < :now
               )
            ORDER BY requested_at
            LIMIT :limit
            FOR UPDATE SKIP LOCKED
        """,
        nativeQuery = true
    )
    fun findPendingForUpdate(
        @Param("now") now: LocalDateTime,
        @Param("limit") limit: Int
    ): List<PlanRecalculationJobEntity>
}
