package space.nstart.pcp.routepassportconsumer.service

import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import space.nstart.pcp.routepassportconsumer.config.RoutePassportOutboxProperties
import space.nstart.pcp.routepassportconsumer.entity.RoutePassportOutboxEntity
import space.nstart.pcp.routepassportconsumer.entity.RoutePassportOutboxStatus

/**
 * Scheduled worker that publishes already persisted route passport outbox rows.
 */
@Component
class RoutePassportOutboxWorker(
    private val routePassportOutboxService: RoutePassportOutboxService,
    private val routePassportKafkaPublisherService: RoutePassportKafkaPublisherService,
    private val outboxProperties: RoutePassportOutboxProperties,
) {
    private val log = LoggerFactory.getLogger(this::class.java)

    @Scheduled(fixedDelayString = "\${app.route-passport.outbox.fixed-delay-ms:1000}")
    fun publishDueOutboxRows() {
        log.debug("Route passport outbox worker tick started: enabled={}", outboxProperties.enabled)
        if (!outboxProperties.enabled) {
            log.debug("Route passport outbox worker tick skipped because worker is disabled")
            return
        }

        val outboxRows = routePassportOutboxService.claimDueBatch()
        log.debug("Route passport outbox worker batch claimed: batchSize={}", outboxRows.size)
        outboxRows.forEach { outbox ->
            logClaimed(outbox)
        }

        outboxRows.forEach { outbox ->
            try {
                publishOne(outbox)
            } catch (exception: Exception) {
                log.error(
                    "Kafka outbox row processing failed: outboxId={}, routeId={}, eventType={}, topic={}, attempts={}, status={}, exceptionClass={}, exceptionMessage={}",
                    outbox.id,
                    outbox.routeId,
                    outbox.eventType,
                    outbox.topic,
                    outbox.attempts,
                    outbox.status,
                    exception::class.java.name,
                    exception.message,
                )
            }
        }
        log.debug("Route passport outbox worker tick finished: batchSize={}", outboxRows.size)
    }

    private fun publishOne(outbox: RoutePassportOutboxEntity) {
        try {
            routePassportKafkaPublisherService.publish(outbox)
            routePassportOutboxService.markPublished(outbox.id)

            log.info(
                "Kafka outbox row publish succeeded: outboxId={}, routeId={}, eventType={}, topic={}, attempts={}, status={}",
                outbox.id,
                outbox.routeId,
                outbox.eventType,
                outbox.topic,
                outbox.attempts,
                RoutePassportOutboxStatus.PUBLISHED,
            )
        } catch (exception: Exception) {
            routePassportOutboxService.markRetryOrFailed(outbox.id, exception)

            val attemptsAfterFailure = outbox.attempts + 1
            val statusAfterFailure = if (attemptsAfterFailure >= outbox.maxAttempts) {
                RoutePassportOutboxStatus.FAILED
            } else {
                RoutePassportOutboxStatus.PENDING
            }

            log.warn(
                "Kafka outbox row publish failed: outboxId={}, routeId={}, eventType={}, topic={}, attempts={}, status={}, exceptionClass={}, exceptionMessage={}",
                outbox.id,
                outbox.routeId,
                outbox.eventType,
                outbox.topic,
                attemptsAfterFailure,
                statusAfterFailure,
                exception::class.java.name,
                exception.message,
            )
        }
    }

    private fun logClaimed(outbox: RoutePassportOutboxEntity) {
        log.info(
            "Kafka outbox row claimed: outboxId={}, routeId={}, eventType={}, topic={}, attempts={}, status={}",
            outbox.id,
            outbox.routeId,
            outbox.eventType,
            outbox.topic,
            outbox.attempts,
            outbox.status,
        )
    }
}
