package space.nstart.pcp.pcp_satellites_service.service

import jakarta.persistence.criteria.JoinType
import org.slf4j.LoggerFactory
import org.springframework.data.domain.Sort
import org.springframework.data.jpa.domain.Specification
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_satellites_service.configuration.CustomValidationException
import space.nstart.pcp.pcp_satellites_service.dto.ComplexPlanRunFilterDTO
import space.nstart.pcp.pcp_satellites_service.dto.ComplexPlanRunResponseDTO
import space.nstart.pcp.pcp_satellites_service.dto.ComplexPlanRunSummaryDTO
import space.nstart.pcp.pcp_satellites_service.repository.ComplexPlanRunRepository
import space.nstart.pcp.pcp_satellites_service.repository.SatelliteModeSnapshotRepository
import kotlin.system.measureTimeMillis

@Service
class ComplexPlanRunQueryService(
    private val complexPlanRunRepository: ComplexPlanRunRepository,
    private val satelliteModeSnapshotRepository: SatelliteModeSnapshotRepository,
    private val complexPlanRunMapper: ComplexPlanRunMapper
) {

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

    @Transactional(readOnly = true)
    fun getRun(runId: Long): ComplexPlanRunResponseDTO {
        lateinit var response: ComplexPlanRunResponseDTO
        val durationMs = measureTimeMillis {
            val run = complexPlanRunRepository.findDetailedById(runId)
                ?: throw CustomValidationException("Complex plan run $runId not found")
            run.snapshots = satelliteModeSnapshotRepository.findAllByComplexPlanRun_IdOrderByIdAsc(runId).toMutableList()
            response = complexPlanRunMapper.toResponse(run)
        }
        logger.info(
            "complexPlanRun response loaded: runId={}, status={}, snapshots={}, durationMs={}",
            runId,
            response.status,
            response.snapshotIds.size,
            durationMs
        )
        return response
    }

    @Transactional(readOnly = true)
    fun findRuns(filter: ComplexPlanRunFilterDTO): List<ComplexPlanRunSummaryDTO> =
        complexPlanRunRepository.findAll(
            buildSpecification(filter),
            Sort.by(
                Sort.Order.desc("createdAt"),
                Sort.Order.desc("id")
            )
        ).map(complexPlanRunMapper::toSummary)

    private fun buildSpecification(filter: ComplexPlanRunFilterDTO): Specification<space.nstart.pcp.pcp_satellites_service.entity.ComplexPlanRunEntity> =
        Specification { root, query, criteriaBuilder ->
            val predicates = mutableListOf<jakarta.persistence.criteria.Predicate>()
            query.distinct(true)

            filter.status?.let { status ->
                predicates += criteriaBuilder.equal(root.get<space.nstart.pcp.pcp_satellites_service.entity.ComplexPlanRunStatus>("status"), status)
            }
            filter.createdFrom?.let { createdFrom ->
                predicates += criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), createdFrom)
            }
            filter.createdTo?.let { createdTo ->
                predicates += criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), createdTo)
            }
            filter.intervalStart?.let { intervalStart ->
                predicates += criteriaBuilder.greaterThanOrEqualTo(root.get("intervalStart"), intervalStart)
            }
            filter.intervalEnd?.let { intervalEnd ->
                predicates += criteriaBuilder.lessThanOrEqualTo(root.get("intervalEnd"), intervalEnd)
            }
            filter.satelliteId?.let { satelliteId ->
                val satellitesJoin = root.join<Any, Any>("satellites", JoinType.LEFT)
                predicates += criteriaBuilder.equal(satellitesJoin.get<Long>("satelliteId"), satelliteId)
            }

            criteriaBuilder.and(*predicates.toTypedArray())
        }
}
