package kr.or.bok.rtgs.gucci

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.MediaType
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

/**
 * G2 결과 콜백 송부(전달 보장) — Chanel이 아웃박스(notification, delivered=false)에 적재한 결과 전문을
 * 기관별 콜백 URL로 **POST 송부**하고, 2xx ACK 시 delivered=true로 확정한다. 실패는 attempts를 늘려
 * 다음 주기에 **재시도**하며, 최대 시도 초과분은 보류(운영 알람 대상). 폴링 아웃박스 방식이라
 * 서비스 재기동에도 유실 없이 이어서 송부한다(at-least-once + 수신측 dedup 전제).
 */
@Service
class DeliveryService(
    private val jdbc: JdbcTemplate,
    private val http: RestClient,
    @Value("\${gucci.delivery-max-attempts:5}") private val maxAttempts: Int,
    @Value("\${gucci.delivery-batch:50}") private val batch: Int,
) {
    private val log = LoggerFactory.getLogger(javaClass)

    @Scheduled(fixedDelayString = "\${gucci.delivery-interval-ms:3000}")
    fun deliverPending() {
        val rows = jdbc.queryForList(
            "SELECT id, to_org, pacs002_xml, attempts FROM notification " +
                "WHERE delivered = false AND attempts < ? ORDER BY created_at LIMIT ?",
            maxAttempts, batch,
        )
        if (rows.isEmpty()) return

        for (r in rows) {
            val id = r["id"] as String
            val toOrg = r["to_org"] as String
            val xml = r["pacs002_xml"] as String
            val attempts = (r["attempts"] as Number).toInt()

            val url = endpointOf(toOrg)
            if (url == null) {
                jdbc.update("UPDATE notification SET attempts = attempts + 1 WHERE id = ?", id)
                log.warn("DELIVER skip id={} org={} reason=no-endpoint attempts={}", id, toOrg, attempts + 1)
                continue
            }
            try {
                http.post().uri(url).contentType(MediaType.APPLICATION_XML)
                    .body(xml).retrieve().toBodilessEntity()   // 2xx ACK 아니면 예외
                jdbc.update(
                    "UPDATE notification SET delivered = true, attempts = attempts + 1, delivered_at = ? WHERE id = ?",
                    System.currentTimeMillis(), id,
                )
                log.info("DELIVER ok id={} org={} url={}", id, toOrg, url)
            } catch (e: Exception) {
                jdbc.update("UPDATE notification SET attempts = attempts + 1 WHERE id = ?", id)
                log.warn("DELIVER fail id={} org={} attempts={} err={}", id, toOrg, attempts + 1, e.message)
            }
        }
    }

    private fun endpointOf(orgCode: String): String? =
        jdbc.queryForList(
            "SELECT callback_url FROM institution_endpoint WHERE org_code = ? AND active = true", orgCode,
        ).firstOrNull()?.get("callback_url") as? String
}
