package org.nstart.dep265.requestservice.service

import jakarta.persistence.EntityManager
import org.junit.jupiter.api.Test
import org.nstart.dep265.requestservice.dto.RequestListFilter
import org.nstart.dep265.requestservice.dto.RequestStatusDto
import org.nstart.dep265.requestservice.dto.SurveyTypeDto
import org.nstart.dep265.requestservice.PcpRequestServiceApplication
import org.nstart.dep265.requestservice.entity.EarthCellEntity
import org.nstart.dep265.requestservice.entity.RequestEntity
import org.nstart.dep265.requestservice.entity.RequestCellEntity
import org.nstart.dep265.requestservice.entity.RequestStatus
import org.nstart.dep265.requestservice.entity.RequestSurveyType
import org.nstart.dep265.requestservice.repository.EarthCellRepository
import org.nstart.dep265.requestservice.repository.RequestCellRepository
import org.nstart.dep265.requestservice.repository.RequestOpticsParamsRepository
import org.nstart.dep265.requestservice.repository.RequestRepository
import org.nstart.dep265.requestservice.repository.RequestRsaParamsRepository
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-list;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",
    ],
)
@Transactional
class RequestServiceListRequestsJpaTest @Autowired constructor(
    private val earthCellRepository: EarthCellRepository,
    private val requestRepository: RequestRepository,
    private val service: RequestService,
    @Suppress("unused") private val requestOpticsParamsRepository: RequestOpticsParamsRepository,
    @Suppress("unused") private val requestRsaParamsRepository: RequestRsaParamsRepository,
    @Suppress("unused") private val requestCellRepository: RequestCellRepository,
    @Suppress("unused") private val entityManager: EntityManager,
) {
    @Test
    fun `list requests excludes deleted by default`() {
        val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00")
        saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00")

        val response = service.listRequests(RequestListFilter())

        assertEquals(listOf(activeId), response.items.map { request -> request.id })
        assertEquals("Тест1", response.items.single().name)
        assertEquals(1, response.totalItems)
        assertEquals(1, response.totalPages)
    }

    @Test
    fun `list requests with geometry returns geometry and keeps list filters`() {
        val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00")
        saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00")

        val response = service.listRequestsForMap(RequestListFilter())

        assertEquals(listOf(activeId), response.items.map { request -> request.id })
        assertEquals("POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))", response.items.single().geometry)
        assertEquals(1, response.totalItems)
        assertEquals(1, response.totalPages)
    }

    @Test
    fun `list requests includes deleted when includeDeleted is true`() {
        val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00")
        val deletedId = saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00")

        val response = service.listRequests(RequestListFilter(includeDeleted = true))

        assertEquals(listOf(deletedId, activeId), response.items.map { request -> request.id })
        assertEquals(2, response.totalItems)
    }

    @Test
    fun `list requests filters by status`() {
        val activeId = saveRequest(status = RequestStatus.ACTIVE)
        saveRequest(status = RequestStatus.ACCEPTED)

        val response = service.listRequests(RequestListFilter(status = RequestStatusDto.ACTIVE))

        assertEquals(listOf(activeId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests filters by survey type`() {
        val rsaId = saveRequest(surveyType = RequestSurveyType.RSA)
        saveRequest(surveyType = RequestSurveyType.OPTICS)

        val response = service.listRequests(RequestListFilter(surveyType = SurveyTypeDto.RSA))

        assertEquals(listOf(rsaId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests filters by kpp in database collection`() {
        val matchedId = saveRequest(kpp = mutableListOf(1, 2))
        saveRequest(kpp = mutableListOf(3, 4))

        val response = service.listRequests(RequestListFilter(kpp = 2))

        assertEquals(listOf(matchedId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests filters by high priority transmit`() {
        val highPriorityId = saveRequest(highPriorityTransmit = true)
        saveRequest(highPriorityTransmit = false)

        val response = service.listRequests(RequestListFilter(highPriorityTransmit = true))

        assertEquals(listOf(highPriorityId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests filters by begin date range inclusively`() {
        saveRequest(beginDateTime = "2026-01-01T00:00:00")
        val matchedId = saveRequest(beginDateTime = "2026-01-15T00:00:00")
        saveRequest(beginDateTime = "2026-02-01T00:00:00", endDateTime = "2026-02-28T00:00:00")

        val response = service.listRequests(
            RequestListFilter(
                beginFrom = LocalDateTime.of(2026, 1, 10, 0, 0, 0),
                beginTo = LocalDateTime.of(2026, 1, 31, 23, 59, 59),
            )
        )

        assertEquals(listOf(matchedId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests filters by end date range inclusively`() {
        saveRequest(endDateTime = "2026-01-09T00:00:00")
        val matchedId = saveRequest(endDateTime = "2026-01-20T00:00:00")
        saveRequest(endDateTime = "2026-02-01T00:00:00")

        val response = service.listRequests(
            RequestListFilter(
                endFrom = LocalDateTime.of(2026, 1, 10, 0, 0, 0),
                endTo = LocalDateTime.of(2026, 1, 31, 23, 59, 59),
            )
        )

        assertEquals(listOf(matchedId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests paginates with total items and total pages from database page`() {
        saveRequest(createdAt = "2026-01-03T00:00:00")
        saveRequest(createdAt = "2026-01-02T00:00:00")
        val oldestId = saveRequest(createdAt = "2026-01-01T00:00:00")

        val response = service.listRequests(RequestListFilter(page = 1, size = 2))

        assertEquals(listOf(oldestId), response.items.map { request -> request.id })
        assertEquals(1, response.page)
        assertEquals(2, response.size)
        assertEquals(3, response.totalItems)
        assertEquals(2, response.totalPages)
    }

    @Test
    fun `list requests sorts by created at descending by default`() {
        val oldestId = saveRequest(createdAt = "2026-01-01T00:00:00")
        val newestId = saveRequest(createdAt = "2026-01-03T00:00:00")
        val middleId = saveRequest(createdAt = "2026-01-02T00:00:00")

        val response = service.listRequests(RequestListFilter())

        assertEquals(listOf(newestId, middleId, oldestId), response.items.map { request -> request.id })
    }

    @Test
    fun `list requests supports whitelisted sort field`() {
        val latestBeginId = saveRequest(beginDateTime = "2026-01-03T00:00:00")
        val earliestBeginId = saveRequest(beginDateTime = "2026-01-01T00:00:00")

        val response = service.listRequests(RequestListFilter(sort = "beginDateTime,asc"))

        assertEquals(listOf(earliestBeginId, latestBeginId), response.items.map { request -> request.id })
    }

    @Test
    fun `get request with cells loads request cell projections with earth cell numbers`() {
        val requestId = saveRequest()
        val earthCell = earthCellRepository.saveAndFlush(
            EarthCellEntity(
                cellNum = 32400,
                latitude = 55.0,
                longitude = 37.0,
                importance = 5.0,
                contour = "POLYGON ((36 55, 37 55, 37 56, 36 56, 36 55))",
            )
        )
        requestCellRepository.saveAndFlush(
            RequestCellEntity(
                requestId = requestId,
                coveragePercent = 25.0,
                importance = 7.0,
                contour = "POLYGON ((36.5 55.5, 37 55.5, 37 56, 36.5 56, 36.5 55.5))",
                cell = earthCell,
            )
        )
        entityManager.clear()

        val response = service.getRequestWithCells(requestId)

        assertEquals(requestId, response?.request?.id)
        assertEquals("Тест1", response?.request?.name)
        val cell = response?.cells?.single()
        assertEquals(32400L, cell?.cellNum)
        assertEquals(25.0, cell?.coveragePercent)
        assertEquals(7.0, cell?.importance)
        assertEquals("POLYGON ((36.5 55.5, 37 55.5, 37 56, 36.5 56, 36.5 55.5))", cell?.contour)
    }

    private fun saveRequest(
        status: RequestStatus = RequestStatus.ACTIVE,
        surveyType: RequestSurveyType = RequestSurveyType.OPTICS,
        kpp: MutableList<Int> = mutableListOf(1),
        highPriorityTransmit: Boolean = false,
        beginDateTime: String = "2026-01-01T00:00:00",
        endDateTime: String = "2026-01-31T00:00:00",
        createdAt: String = "2026-01-01T00:00:00",
    ): UUID {
        val id = UUID.randomUUID()
        val geometry = "POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))"
        requestRepository.saveAndFlush(
            RequestEntity(
                id = id,
                name = "Тест1",
                geometry = geometry,
                remainingGeometry = geometry,
                geometryArea = 1.0,
                remainingArea = 0.5,
                coverageRequiredPercent = 100.0,
                importance = 10.0,
                beginDateTime = LocalDateTime.parse(beginDateTime),
                endDateTime = LocalDateTime.parse(endDateTime),
                kpp = kpp,
                highPriorityTransmit = highPriorityTransmit,
                surveyType = surveyType,
                status = status,
                createdAt = LocalDateTime.parse(createdAt),
                updatedAt = LocalDateTime.parse(createdAt),
                deletedAt = if (status == RequestStatus.DELETED) LocalDateTime.parse(createdAt) else null,
            )
        )
        return id
    }
}
