package org.nstart.dep265.requestservice.repository

import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.nstart.dep265.requestservice.PcpRequestServiceApplication
import org.nstart.dep265.requestservice.entity.RequestEntity
import org.nstart.dep265.requestservice.entity.RequestStatus
import org.nstart.dep265.requestservice.entity.RequestSurveyType
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
import java.util.UUID
import kotlin.test.assertEquals

@SpringBootTest(
    classes = [PcpRequestServiceApplication::class],
    webEnvironment = SpringBootTest.WebEnvironment.NONE,
    properties = [
        "spring.config.import=",
        "spring.cloud.config.enabled=false",
        "spring.flyway.enabled=false",
        "spring.jpa.hibernate.ddl-auto=create-drop",
        "spring.datasource.url=jdbc:h2:mem:request-expiry;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false",
        "spring.datasource.driver-class-name=org.h2.Driver",
        "spring.datasource.username=sa",
        "spring.datasource.password=",
        "spring.jpa.database-platform=org.hibernate.dialect.H2Dialect",
        "spring.kafka.listener.auto-startup=false",
        "spring.kafka.consumer.group-id=request-service-test",
        "app.kafka.topics.route=request-service-test-route",
    ],
)
class RequestRepositoryJpaTest @Autowired constructor(
    private val requestRepository: RequestRepository,
) {
    private val now: LocalDateTime = LocalDateTime.parse("2026-06-06T12:00:00")

    @BeforeEach
    fun setUp() {
        requestRepository.deleteAll()
    }

    @Test
    @Transactional
    fun `expirable query returns only overdue accepted and active requests`() {
        val overdueAccepted = request(status = RequestStatus.ACCEPTED, end = now.minusHours(1))
        val overdueActive = request(status = RequestStatus.ACTIVE, end = now.minusHours(2))
        val activeInWindow = request(status = RequestStatus.ACTIVE, end = now.plusHours(1))
        val alreadyCompleted = request(status = RequestStatus.COMPLETED, end = now.minusHours(1))
        val alreadyExpired = request(status = RequestStatus.EXPIRED, end = now.minusHours(1))
        val deletedOverdue = request(
            status = RequestStatus.DELETED,
            end = now.minusHours(1),
            deletedAt = now.minusMinutes(30),
        )
        requestRepository.saveAllAndFlush(
            listOf(overdueAccepted, overdueActive, activeInWindow, alreadyCompleted, alreadyExpired, deletedOverdue),
        )

        val expirable = requestRepository.findExpirableForUpdateSkipLocked(now = now, batchSize = 100)

        // Только незавершённые (ACCEPTED/ACTIVE) с истёкшим окном и без soft-delete, по возрастанию end.
        assertEquals(listOf(overdueActive.id, overdueAccepted.id), expirable.map { it.id })
    }

    @Test
    @Transactional
    fun `expirable query honours the batch size limit`() {
        val oldest = request(status = RequestStatus.ACTIVE, end = now.minusHours(3))
        val middle = request(status = RequestStatus.ACCEPTED, end = now.minusHours(2))
        val newest = request(status = RequestStatus.ACTIVE, end = now.minusHours(1))
        requestRepository.saveAllAndFlush(listOf(newest, oldest, middle))

        val expirable = requestRepository.findExpirableForUpdateSkipLocked(now = now, batchSize = 2)

        assertEquals(listOf(oldest.id, middle.id), expirable.map { it.id })
    }

    private fun request(
        status: RequestStatus,
        end: LocalDateTime,
        begin: LocalDateTime = end.minusDays(1),
        deletedAt: LocalDateTime? = null,
    ): RequestEntity {
        val geometry = "POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))"
        return RequestEntity(
            id = UUID.randomUUID(),
            name = "Тест1",
            geometry = geometry,
            remainingGeometry = geometry,
            geometryArea = 1.0,
            remainingArea = 1.0,
            importance = 10.0,
            beginDateTime = begin,
            endDateTime = end,
            surveyType = RequestSurveyType.OPTICS,
            status = status,
            createdAt = begin.minusDays(1),
            updatedAt = begin.minusDays(1),
            deletedAt = deletedAt,
        )
    }
}
