package org.nstart.dep265.requestservice.service

import com.fasterxml.jackson.databind.ObjectMapper
import org.nstart.dep265.requestservice.entity.OutboxEventStatus
import org.nstart.dep265.requestservice.entity.RequestEntity
import org.nstart.dep265.requestservice.entity.RequestStatus
import org.nstart.dep265.requestservice.repository.OutboxEventRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime
import java.util.UUID

@Service
class RequestCompletedOutboxService(
    private val outboxEventRepository: OutboxEventRepository,

    private val objectMapper: ObjectMapper,

    private val geometryService: GeometryService,
) {
    private val log = LoggerFactory.getLogger(this::class.java)

    @Transactional(propagation = Propagation.MANDATORY)
    fun createRequestCompletedIfAbsent(request: RequestEntity, eventCreatedAt: LocalDateTime) {
        val completedAt = request.completedAt ?: return
        val payload = RequestCompletedPayload(
            requestId = request.id,
            status = RequestStatus.COMPLETED.name,
            completedAt = completedAt,
            coveragePercent = geometryService.coveredPercent(request.geometryArea, request.remainingArea),
            matchedAt = request.lastMatchedAt,
            eventCreatedAt = eventCreatedAt,
        )

        val inserted = outboxEventRepository.insertIfAbsent(
            id = UUID.randomUUID(),
            eventType = REQUEST_COMPLETED_EVENT_TYPE,
            aggregateType = REQUEST_AGGREGATE_TYPE,
            aggregateId = request.id,
            payload = objectMapper.writeValueAsString(payload),
            status = OutboxEventStatus.NEW.name,
            createdAt = eventCreatedAt,
        )

        if (inserted == 0) {
            log.debug(
                "REQUEST_COMPLETED outbox event already exists for requestId={}",
                request.id,
            )
        }
    }

    private data class RequestCompletedPayload(
        val requestId: UUID,
        val status: String,
        val completedAt: LocalDateTime,
        val coveragePercent: Double,
        val matchedAt: LocalDateTime?,
        val eventCreatedAt: LocalDateTime,
    )

    companion object {
        const val REQUEST_COMPLETED_EVENT_TYPE = "REQUEST_COMPLETED"
        const val REQUEST_AGGREGATE_TYPE = "REQUEST"
    }
}
