package space.nstart.pcp_tgu_service.service

import space.nstart.pcp_tgu_service.domain.CalculatedPlan
import java.time.Duration
import java.time.LocalDateTime

/**
 * Сверка набранного оператором окна плана с «сырыми» ЗРВ КА.
 *
 * Окно собирается из двух реальных зон: старт (и КПП) берётся у одной ЗРВ, конец — у другой (не
 * раньше старта). Поэтому окно валидно, если:
 *  - начало совпадает (с точностью до секунды) с началом какой-то ЗРВ **этого же КПП**, и
 *  - конец совпадает с концом какой-то ЗРВ, и
 *  - конец строго позже начала.
 *
 * Случай «одна ЗРВ» (текущие replace/fork-forward, где старт и конец — границы одной зоны) — частный
 * случай этого правила, поэтому проверка единая для создания плана и для контроля слота на promote.
 */
object ZrvWindowMatcher {

    /**
     * Допуск сравнения времён — компенсирует усечение ISO-строк на фронте до целых секунд
     * (`.slice(0,19)`): реальная ЗРВ имеет доли секунды, набранное окно — нет, расхождение строго
     * меньше секунды. Сравнивается истинная абсолютная длительность (не усечённые секунды): у
     * отрицательного `Duration` доли нормализуются в +nanos, поэтому `.toSeconds()` округлял бы 0.18с
     * вниз до −1 и ломал совпадение.
     */
    private val TOLERANCE = Duration.ofSeconds(1)

    fun matches(
        windows: List<CalculatedPlan>,
        windowStart: LocalDateTime,
        windowStop: LocalDateTime,
        windowKppId: String
    ): Boolean {
        if (!windowStop.isAfter(windowStart)) {
            return false
        }
        val startMatches = windows.any { zrv ->
            zrv.kppId == windowKppId && within(zrv.startTime, windowStart)
        }
        val stopMatches = windows.any { zrv ->
            within(zrv.endTime, windowStop)
        }
        return startMatches && stopMatches
    }

    private fun within(a: LocalDateTime, b: LocalDateTime): Boolean =
        Duration.between(a, b).abs() <= TOLERANCE
}
