package org.nstart.dep265.requestservice.service

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.mockingDetails
import org.nstart.dep265.requestservice.entity.RequestEntity
import org.nstart.dep265.requestservice.entity.RequestStatus
import org.nstart.dep265.requestservice.entity.RequestSurveyType
import org.nstart.dep265.requestservice.repository.OutboxEventRepository
import java.time.LocalDateTime
import java.util.UUID

class RequestStatusOutboxServiceTest {
    private val objectMapper = jacksonObjectMapper()
        .registerModule(JavaTimeModule())
        .findAndRegisterModules()

    @Test
    fun `completed terminal payload contains required fields`() {
        val outboxEventRepository = mock(OutboxEventRepository::class.java)
        val service = service(outboxEventRepository)
        val requestId = UUID.fromString("5f57cd22-44a7-4fe8-956f-7fd0c52df846")
        val completedAt = LocalDateTime.parse("2026-01-01T10:15:30")
        val matchedAt = LocalDateTime.parse("2026-01-01T10:14:00")
        val eventCreatedAt = LocalDateTime.parse("2026-01-01T10:16:00")
        val request = RequestEntity(
            id = requestId,
            name = "Тест1",
            geometry = "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))",
            remainingGeometry = "POLYGON EMPTY",
            geometryArea = 1.0,
            remainingArea = 0.0,
            importance = 10.0,
            beginDateTime = LocalDateTime.parse("2026-01-01T10:00:00"),
            endDateTime = LocalDateTime.parse("2026-01-01T11:00:00"),
            surveyType = RequestSurveyType.OPTICS,
            status = RequestStatus.COMPLETED,
            lastMatchedAt = matchedAt,
            completedAt = completedAt,
            createdAt = LocalDateTime.parse("2026-01-01T09:00:00"),
            updatedAt = completedAt,
        )

        service.createTerminalIfAbsent(request, eventCreatedAt)

        val payload = objectMapper.readTree(insertedPayload(outboxEventRepository))
        assertEquals(requestId.toString(), payload["requestId"].asText())
        assertEquals("COMPLETED", payload["status"].asText())
        assertEquals(completedAt, objectMapper.treeToValue(payload["occurredAt"], LocalDateTime::class.java))
        assertEquals(100.0, payload["coveragePercent"].asDouble())
        assertNotNull(payload["matchedAt"])
        assertNotNull(payload["eventCreatedAt"])
    }

    @Test
    fun `expired terminal payload carries expired status and partial coverage`() {
        val outboxEventRepository = mock(OutboxEventRepository::class.java)
        val service = service(outboxEventRepository)
        val requestId = UUID.fromString("9c2f0c4b-3a51-4e2d-9d8a-1f4b2c6e7a01")
        val eventCreatedAt = LocalDateTime.parse("2026-02-01T00:00:00")
        val request = RequestEntity(
            id = requestId,
            name = "Тест1",
            geometry = "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))",
            remainingGeometry = "POLYGON((0 0, 0.5 0, 0.5 1, 0 1, 0 0))",
            geometryArea = 1.0,
            remainingArea = 0.5,
            importance = 10.0,
            beginDateTime = LocalDateTime.parse("2026-01-01T00:00:00"),
            endDateTime = LocalDateTime.parse("2026-01-31T23:59:59"),
            surveyType = RequestSurveyType.OPTICS,
            status = RequestStatus.EXPIRED,
            completedAt = null,
            createdAt = LocalDateTime.parse("2025-12-31T00:00:00"),
            updatedAt = eventCreatedAt,
        )

        service.createTerminalIfAbsent(request, eventCreatedAt)

        val payload = objectMapper.readTree(insertedPayload(outboxEventRepository))
        assertEquals("EXPIRED", payload["status"].asText())
        // completedAt отсутствует у EXPIRED → occurredAt = момент фиксации истечения.
        assertEquals(eventCreatedAt, objectMapper.treeToValue(payload["occurredAt"], LocalDateTime::class.java))
        assertEquals(50.0, payload["coveragePercent"].asDouble())
        assertTrue(payload["matchedAt"].isNull)
    }

    private fun service(outboxEventRepository: OutboxEventRepository): RequestStatusOutboxService {
        return RequestStatusOutboxService(
            outboxEventRepository = outboxEventRepository,
            objectMapper = objectMapper,
            geometryService = GeometryService(),
        )
    }

    private fun insertedPayload(outboxEventRepository: OutboxEventRepository): String {
        val insertInvocation = mockingDetails(outboxEventRepository).invocations.single { invocation ->
            invocation.method.name == "insertIfAbsent"
        }
        return insertInvocation.arguments[4] as String
    }
}
