package kr.or.bok.rtgs.chanel

import com.fasterxml.jackson.databind.ObjectMapper
import kr.or.bok.rtgs.chanel.jpa.NotificationRepository
import kr.or.bok.rtgs.common.SettlementNotice
import kr.or.bok.rtgs.common.Topics
import kr.or.bok.rtgs.common.TxSts
import kr.or.bok.rtgs.common.iso20022.Iso20022Codec
import kr.or.bok.rtgs.common.iso20022.Pacs002
import kr.or.bok.rtgs.common.ledger.NotificationEntity
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.kafka.annotation.KafkaListener
import org.springframework.stereotype.Service
import java.time.Instant

/**
 * 결과통보(이체결과 송부) — Gucci에서 Chanel로 이관(2026-07-10). Chanel 헌장 "접수 + 처리결과 통보".
 *
 * Prada가 완결(ACCC/RJCT) 후 발행한 rtgs.notify를 받아, **접수센터(originCenter)의 Chanel만**
 * 신청기관(송신)·수취기관 앞 최종 pacs.002를 생성해 송부한다(개요 프로세스 (4)).
 * - 신청기관(APPLICANT): 항상 결과 통보(성공/반려)
 * - 수취기관(BENEFICIARY): 완결(ACCC) 시 입금 통보
 * 로컬은 실제 외부 전송 대신 아웃박스(notification) 기록 + 로그로 송부를 모사한다.
 * (실외부 채널/콜백 전송·전달보장은 향후 Gucci 관문이 담당)
 */
@Service
class NotificationService(
    private val notifications: NotificationRepository,
    private val mapper: ObjectMapper,
    @Value("\${rtgs.center-id:DC1}") private val centerId: String,
) {
    private val log = LoggerFactory.getLogger(javaClass)

    // 센터별 그룹(chanel-DC1/DC2/DC3): 각 센터가 notify 전량 소비 후 origin만 통보.
    @KafkaListener(topics = [Topics.NOTIFY], groupId = "chanel-\${rtgs.center-id:DC1}", concurrency = "1")
    fun onNotice(payload: String) {
        val n = mapper.readValue(payload, SettlementNotice::class.java)
        if (n.originCenter != centerId) return   // 접수센터만 통보 책임

        val pacs002 = Iso20022Codec.writePacs002(
            Pacs002(
                orgnlBizMsgIdr = n.bmi,
                from = "RTGS-$centerId",
                to = n.senderCode,
                txSts = n.finalStatus.name,
                clrSysRef = n.reason ?: "seq:${n.globalSeq}",
                creDtTm = Instant.now().toString(),
            )
        )

        // 신청기관(송신) 앞 결과 통보 — 성공/반려 모두
        send(n, n.senderCode, "APPLICANT", pacs002)
        // 수취기관 앞 입금 통보 — 완결(ACCC)만
        if (n.finalStatus == TxSts.ACCC) send(n, n.receiverCode, "BENEFICIARY", pacs002)

        log.info("NOTIFY 송부 bmi={} status={} → 신청기관 {}{}",
            n.bmi, n.finalStatus, n.senderCode,
            if (n.finalStatus == TxSts.ACCC) " · 수취기관 ${n.receiverCode}" else "")
    }

    private fun send(n: SettlementNotice, toOrg: String, role: String, pacs002: String) {
        val now = System.currentTimeMillis()
        // 생성 시점엔 아웃박스에 pending(delivered=false)으로 기록.
        // 실제 외부 기관 앞 송부(콜백 POST·재시도·ACK)는 Gucci 관문(G2)이 담당한다.
        notifications.save(
            NotificationEntity(
                id = "${n.bmi}:$toOrg", bmi = n.bmi, toOrg = toOrg, role = role,
                finalStatus = n.finalStatus.name, pacs002Xml = pacs002,
                originCenter = n.originCenter, reason = n.reason,
                delivered = false, attempts = 0, createdAt = now, deliveredAt = null,
            )
        )
    }
}
