package space.nstart.pcp_tgu_service.service

import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp_tgu_service.entity.SpacecraftVisibilitySnapshotEntity
import space.nstart.pcp_tgu_service.integration.api.VisibilityPayloadParser
import space.nstart.pcp_tgu_service.repository.SpacecraftVisibilitySnapshotRepository
import java.security.MessageDigest
import java.time.LocalDateTime
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes

/**
 * Сохраняет raw visibility payload. Расписание считается on-demand из снапшота
 * ([PlanProjectionService]), поэтому отдельный triggers пересчёта здесь не нужен.
 */
@Service
class SpacecraftVisibilitySnapshotService(
    private val spacecraftVisibilitySnapshotRepository: SpacecraftVisibilitySnapshotRepository,
    private val visibilityPayloadParser: VisibilityPayloadParser
) {
    /**
     * Сохраняет snapshot. Hash используется только для метаданных и диагностики изменений окон.
     */
    @Transactional
    fun saveSnapshot(spacecraftId: String, noradId: Long, payloadJson: String, fetchedAt: LocalDateTime) {
        val payloadHash = sha256(payloadJson)
        val existing = spacecraftVisibilitySnapshotRepository.findById(spacecraftId).orElse(null)
        val metadata = runCatching { extractMetadata(spacecraftId, payloadJson) }.getOrNull()
        val now = UtcDateTimes.now()

        if (existing == null) {
            spacecraftVisibilitySnapshotRepository.save(
                SpacecraftVisibilitySnapshotEntity(
                    spacecraftId = spacecraftId,
                    noradId = noradId,
                    payloadJson = payloadJson,
                    payloadHash = payloadHash,
                    fetchedAt = fetchedAt,
                    visibilityFrom = metadata?.visibilityFrom,
                    visibilityTo = metadata?.visibilityTo,
                    windowsCount = metadata?.windowsCount,
                    createdAt = now,
                    updatedAt = now
                )
            )
            return
        }

        val changed = existing.payloadHash != payloadHash
        existing.noradId = noradId
        existing.fetchedAt = fetchedAt
        existing.updatedAt = now

        if (changed) {
            existing.payloadJson = payloadJson
            existing.payloadHash = payloadHash
            existing.visibilityFrom = metadata?.visibilityFrom
            existing.visibilityTo = metadata?.visibilityTo
            existing.windowsCount = metadata?.windowsCount
        }

        spacecraftVisibilitySnapshotRepository.save(existing)
    }

    private fun extractMetadata(spacecraftId: String, payloadJson: String): VisibilityMetadata {
        val windows = visibilityPayloadParser.parse(spacecraftId, payloadJson)
            .points
            .flatMap { point -> point.observationWindows }

        return VisibilityMetadata(
            visibilityFrom = windows.minOfOrNull { window -> window.startTime },
            visibilityTo = windows.maxOfOrNull { window -> window.endTime },
            windowsCount = windows.size
        )
    }

    private fun sha256(value: String): String {
        val bytes = MessageDigest.getInstance("SHA-256").digest(value.toByteArray(Charsets.UTF_8))
        return bytes.joinToString(separator = "") { byte -> "%02x".format(byte) }
    }

    private data class VisibilityMetadata(
        val visibilityFrom: LocalDateTime?,
        val visibilityTo: LocalDateTime?,
        val windowsCount: Int
    )
}
