package space.nstart.pcp.pcp_satellites_service.service

import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_satellites_service.entity.ComplexPlanRunEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeEntity
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeSource
import space.nstart.pcp.pcp_satellites_service.entity.SatelliteModeSnapshotEntity
import space.nstart.pcp.pcp_satellites_service.configuration.CustomValidationException
import space.nstart.pcp.pcp_satellites_service.model.mode.SurveyMode
import space.nstart.pcp.pcp_satellites_service.repository.SatelliteModeBookedSlotRepository
import space.nstart.pcp.pcp_satellites_service.repository.SatelliteModeCellRepository
import space.nstart.pcp.pcp_satellites_service.repository.SatelliteModeRepository
import space.nstart.pcp.pcp_satellites_service.repository.SatelliteModeSnapshotRepository
import java.time.Clock
import java.time.LocalDateTime
import kotlin.system.measureTimeMillis

@Service
class SatelliteModePersistenceService(
    private val satelliteModeRepository: SatelliteModeRepository,
    private val satelliteModeBookedSlotRepository: SatelliteModeBookedSlotRepository,
    private val satelliteModeCellRepository: SatelliteModeCellRepository,
    private val satelliteModeSnapshotRepository: SatelliteModeSnapshotRepository,
    private val satelliteModeMapper: SatelliteModeMapper,
    private val clock: Clock
) {

    private val logger = LoggerFactory.getLogger(this::class.java)

    @Transactional
    fun deleteModesForInterval(
        satelliteIds: Collection<Long>,
        intervalStart: LocalDateTime,
        intervalEnd: LocalDateTime
    ) {
        validateInterval(intervalStart, intervalEnd)

        val selectedSatelliteIds = satelliteIds.distinct()
        if (selectedSatelliteIds.isEmpty()) {
            logger.info("Удаление режимов пропущено: пустой список спутников")
            return
        }

        var modeIds = emptyList<Long>()
        var snapshotIds = emptyList<Long>()
        val lookupMs = measureTimeMillis {
            modeIds = satelliteModeRepository.findIntersectingIdsBySatelliteIdsAndInterval(
                selectedSatelliteIds,
                intervalStart,
                intervalEnd
            )
            snapshotIds = satelliteModeSnapshotRepository.findIntersectingIdsBySatelliteIdsAndInterval(
                selectedSatelliteIds,
                intervalStart,
                intervalEnd
            )
        }
        logger.info(
            "Satellite mode delete stage lookup completed: satellites={}, snapshots={}, modes={}, durationMs={}",
            selectedSatelliteIds,
            snapshotIds.size,
            modeIds.size,
            lookupMs
        )

        if (modeIds.isEmpty() && snapshotIds.isEmpty()) {
            logger.info(
                "Для удаления не найдено пересекающихся snapshot/result-set: satellites={}, interval=[{} - {}]",
                selectedSatelliteIds,
                intervalStart,
                intervalEnd
            )
            return
        }

        logger.info(
            "Удаление legacy snapshot/result-set: satellites={}, interval=[{} - {}], snapshots={}, modes={}",
            selectedSatelliteIds,
            intervalStart,
            intervalEnd,
            snapshotIds.size,
            modeIds.size
        )
        val deleteMs = measureTimeMillis {
            if (modeIds.isNotEmpty()) {
                satelliteModeBookedSlotRepository.deleteAllByModeIds(modeIds)
                satelliteModeCellRepository.deleteAllByModeIds(modeIds)
                satelliteModeRepository.deleteIntersectingBySatelliteIdsAndInterval(
                    selectedSatelliteIds,
                    intervalStart,
                    intervalEnd
                )
            }
            if (snapshotIds.isNotEmpty()) {
                satelliteModeSnapshotRepository.deleteAllByIdInBatch(snapshotIds)
            }
        }
        logger.info("Удаление legacy snapshot/result-set завершено: durationMs={}", deleteMs)
    }

    @Transactional
    fun saveModesForInterval(
        satelliteIds: Collection<Long>,
        intervalStart: LocalDateTime,
        intervalEnd: LocalDateTime,
        surveysBySatelliteId: Map<Long, List<SurveyMode>>,
        complexPlanRun: ComplexPlanRunEntity? = null
    ): SatelliteModeSaveResult {
        validateInterval(intervalStart, intervalEnd)

        val selectedSatelliteIds = satelliteIds.distinct()
        if (selectedSatelliteIds.isEmpty()) {
            logger.info("Сохранение режимов пропущено: пустой список спутников")
            return SatelliteModeSaveResult()
        }

        return saveSnapshotModes(
            satelliteIds = selectedSatelliteIds,
            intervalStart = intervalStart,
            intervalEnd = intervalEnd,
            surveysBySatelliteId = surveysBySatelliteId,
            complexPlanRun = complexPlanRun,
            activateSnapshot = true
        )
    }

    @Transactional
    fun replaceModesForInterval(
        satelliteIds: Collection<Long>,
        intervalStart: LocalDateTime,
        intervalEnd: LocalDateTime,
        surveysBySatelliteId: Map<Long, List<SurveyMode>>,
        complexPlanRun: ComplexPlanRunEntity? = null
    ): SatelliteModeSaveResult {
        validateInterval(intervalStart, intervalEnd)
        val selectedSatelliteIds = satelliteIds.distinct()
        if (selectedSatelliteIds.isEmpty()) {
            logger.info("Atomic replace режимов пропущен: пустой список спутников")
            return SatelliteModeSaveResult()
        }

        return saveSnapshotModes(
            satelliteIds = selectedSatelliteIds,
            intervalStart = intervalStart,
            intervalEnd = intervalEnd,
            surveysBySatelliteId = surveysBySatelliteId,
            complexPlanRun = complexPlanRun,
            activateSnapshot = true
        )
    }

    @Transactional
    fun clearAll() {
        logger.info("Полная очистка таблиц satellite_mode_snapshots и satellite_modes")
        val durationMs = measureTimeMillis {
            satelliteModeBookedSlotRepository.deleteAllInBatch()
            satelliteModeCellRepository.deleteAllInBatch()
            satelliteModeRepository.deleteAllInBatch()
            satelliteModeSnapshotRepository.deleteAllInBatch()
        }
        logger.info("Полная очистка таблиц satellite_mode_snapshots и satellite_modes завершена: durationMs={}", durationMs)
    }

    private fun saveSnapshotModes(
        satelliteIds: Collection<Long>,
        intervalStart: LocalDateTime,
        intervalEnd: LocalDateTime,
        surveysBySatelliteId: Map<Long, List<SurveyMode>>,
        complexPlanRun: ComplexPlanRunEntity?,
        activateSnapshot: Boolean
    ): SatelliteModeSaveResult {
        lateinit var snapshots: List<SatelliteModeSnapshotEntity>
        val createSnapshotsMs = measureTimeMillis {
            snapshots = satelliteIds.map { satelliteId ->
                SatelliteModeSnapshotEntity(
                    satelliteId = satelliteId,
                    intervalStart = intervalStart,
                    intervalEnd = intervalEnd,
                    createdAt = LocalDateTime.now(clock),
                    isActive = false,
                    complexPlanRun = complexPlanRun
                )
            }
            satelliteModeSnapshotRepository.saveAll(snapshots)
        }
        logger.info(
            "Satellite mode save stage snapshots-created: snapshots={}, durationMs={}",
            snapshots.size,
            createSnapshotsMs
        )

        lateinit var entities: List<SatelliteModeEntity>
        val mapEntitiesMs = measureTimeMillis {
            entities = snapshots.flatMap { snapshot ->
                surveysBySatelliteId[snapshot.satelliteId]
                    .orEmpty()
                    .filter { intersects(it.time, it.timeStop, intervalStart, intervalEnd) }
                    .map { survey ->
                        satelliteModeMapper.toEntity(
                            satelliteId = snapshot.satelliteId,
                            survey = survey,
                            snapshot = snapshot,
                            complexPlanRun = complexPlanRun
                        )
                    }
            }
        }
        logger.info(
            "Satellite mode save stage entities-mapped: snapshots={}, modes={}, durationMs={}",
            snapshots.size,
            entities.size,
            mapEntitiesMs
        )

        if (entities.isNotEmpty()) {
            logger.info(
                "Сохранение stage 4 snapshot: satellites={}, interval=[{} - {}], snapshots={}, modes={}",
                satelliteIds,
                intervalStart,
                intervalEnd,
                snapshots.size,
                entities.size
            )
            val saveModesMs = measureTimeMillis {
                satelliteModeRepository.saveAll(entities)
            }
            logger.info(
                "Satellite mode save stage modes-saved: modes={}, durationMs={}",
                entities.size,
                saveModesMs
            )
        } else {
            logger.info(
                "Сохранение stage 4 snapshot без режимов: satellites={}, interval=[{} - {}], snapshots={}",
                satelliteIds,
                intervalStart,
                intervalEnd,
                snapshots.size
            )
        }

        val activeSnapshotIds = if (activateSnapshot) {
            val activateMs = measureTimeMillis {
                snapshots.forEach { snapshot ->
                    satelliteModeSnapshotRepository.deactivateActiveSnapshots(snapshot.satelliteId)
                    snapshot.isActive = true
                }
            }
            logger.info(
                "Satellite mode save stage snapshots-activated: snapshots={}, durationMs={}",
                snapshots.size,
                activateMs
            )
            snapshots.mapNotNull { it.id }
        } else {
            emptyList()
        }

        logger.info(
            "Сохранение stage 4 snapshot завершено: snapshots={}, activeSnapshots={}",
            snapshots.mapNotNull { it.id },
            activeSnapshotIds
        )
        return SatelliteModeSaveResult.from(
            entities = entities,
            snapshotIds = snapshots.mapNotNull { it.id },
            activeSnapshotIds = activeSnapshotIds
        )
    }

    private fun validateInterval(intervalStart: LocalDateTime, intervalEnd: LocalDateTime) {
        if (intervalEnd.isBefore(intervalStart)) {
            throw CustomValidationException("Параметр intervalEnd должен быть больше или равен intervalStart")
        }
    }

    private fun intersects(
        start1: LocalDateTime,
        end1: LocalDateTime,
        start2: LocalDateTime,
        end2: LocalDateTime
    ): Boolean = start1 < end2 && start2 < end1
}

data class SatelliteModeSaveResult(
    val calculatedModesCount: Int = 0,
    val bookedModesCount: Int = 0,
    val complanModesCount: Int = 0,
    val mixedModesCount: Int = 0,
    val affectedSatellitesCount: Int = 0,
    val processedCellsCount: Int = 0,
    val bookedSlotLinksCount: Int = 0,
    val modeIds: List<Long> = emptyList(),
    val snapshotIds: List<Long> = emptyList(),
    val activeSnapshotIds: List<Long> = emptyList()
) {
    companion object {
        fun from(
            entities: List<SatelliteModeEntity>,
            snapshotIds: List<Long>,
            activeSnapshotIds: List<Long>
        ): SatelliteModeSaveResult =
            SatelliteModeSaveResult(
                calculatedModesCount = entities.size,
                bookedModesCount = entities.count { it.source == SatelliteModeSource.SLOTS },
                complanModesCount = entities.count { it.source == SatelliteModeSource.COMPLAN },
                mixedModesCount = entities.count { it.source == SatelliteModeSource.MIXED },
                affectedSatellitesCount = entities.map { it.satelliteId }.distinct().size,
                processedCellsCount = entities.flatMap { entity -> entity.cells.map { it.cellNum } }
                    .filter { it > 0 }
                    .distinct()
                    .size,
                bookedSlotLinksCount = entities.sumOf { it.bookedSlots.size },
                modeIds = entities.mapNotNull { it.id },
                snapshotIds = snapshotIds,
                activeSnapshotIds = activeSnapshotIds
            )
    }
}
