package org.nstart.dep265.requestservice.service

import org.nstart.dep265.requestservice.dto.GridRebuildResponseDto
import org.nstart.dep265.requestservice.entity.RequestStatus
import org.nstart.dep265.requestservice.repository.EarthCellRepository
import org.nstart.dep265.requestservice.repository.RequestCellRepository
import org.nstart.dep265.requestservice.repository.RequestRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.LocalDateTime

class GridRebuildAlreadyRunningException : RuntimeException("Grid rebuild is already running")

@Service
class GridRebuildService(
    private val gridSettingsService: GridSettingsService,
    private val earthGridCatalogService: EarthGridCatalogService,
    private val requestCellRepository: RequestCellRepository,
    private val requestRepository: RequestRepository,
    private val requestGridProjectionService: RequestGridProjectionService,
    private val earthCellRepository: EarthCellRepository,
    private val gridRebuildLockService: GridRebuildLockService,
    private val clock: Clock = Clock.systemUTC(),
) {
    @Transactional
    fun rebuildGrid(): GridRebuildResponseDto {
        if (!gridRebuildLockService.tryAcquireTransactionLock()) {
            throw GridRebuildAlreadyRunningException()
        }

        val settings = gridSettingsService.getSettings()
        val activeRequests = requestRepository.findByStatusIn(ACTIVE_REQUEST_STATUSES)

        requestCellRepository.deleteAllInBatch()
        earthGridCatalogService.init(settings.calculationStep)

        activeRequests.forEach { request ->
            requestGridProjectionService.rebuildProjection(request)
        }

        return GridRebuildResponseDto(
            cellsCount = earthCellRepository.count(),
            rebuiltRequestProjectionsCount = activeRequests.size.toLong(),
            rebuiltAt = UtcDateTimes.now(clock),
        )
    }

    private companion object {
        val ACTIVE_REQUEST_STATUSES = setOf(RequestStatus.ACCEPTED, RequestStatus.ACTIVE)
    }
}
