package kr.or.bok.rtgs.chanel

import com.fasterxml.jackson.databind.ObjectMapper
import kr.or.bok.rtgs.chanel.jpa.RawMessageRepository
import kr.or.bok.rtgs.common.BmiGenerator
import kr.or.bok.rtgs.common.ledger.RawMessageEntity
import kr.or.bok.rtgs.common.InboundRequest
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.iso20022.XmlValidator
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.stereotype.Service
import java.time.Instant

/**
 * CHANEL(전문송수신) — 참가기관 pacs.008 접수 → XSD검증 → 경량명령문 추출 →
 * 원전문 MongoDB 저장 → 입구(rtgs.inbound) 발행. 응답으로 pacs.002 생성.
 */
@Service
class ChanelService(
    private val kafka: KafkaTemplate<String, String>,
    private val mapper: ObjectMapper,
    private val rawRepo: RawMessageRepository,
    @Value("\${rtgs.center-id:DC1}") private val centerId: String,
) {
    private val log = LoggerFactory.getLogger(javaClass)
    private val validator = XmlValidator()
    private val fallbackBmi = BmiGenerator("9999") // 요청에 BMI 없을 때 접수센터가 부여

    fun accept(rawXml: String): Pacs002 {
        // 1) XSD 유효성 검사
        val v = validator.validate(rawXml)
        if (!v.valid) {
            log.warn("XSD invalid: {}", v.error)
            return reject("", "", "", "XSD validation failed: ${v.error}")
        }

        // 2) 파싱 + 경량명령문 추출
        val req = try {
            Iso20022Codec.parsePacs008(rawXml)
        } catch (e: Exception) {
            return reject("", "", "", "parse error: ${e.message}")
        }
        val bmi = req.bizMsgIdr?.takeIf { it.isNotBlank() } ?: fallbackBmi.next()
        val core = try {
            Iso20022Codec.toCoreMessage(req, rawXml, bmi)
        } catch (e: Exception) {
            return reject(bmi, req.to, req.from, "business rule: ${e.message}")
        }

        // 3) 원전문 저장(PostgreSQL text; 클라우드는 문서형 DB)
        rawRepo.save(
            RawMessageEntity(
                bmi = core.bmi, msgType = core.msgType, rawXml = rawXml,
                origHash = core.origHash, receivedAt = System.currentTimeMillis(), originCenter = centerId,
            )
        )

        // 4) 입구(rtgs.inbound) 발행 → 순번기로
        val inbound = InboundRequest(core = core, originCenter = centerId)
        kafka.send(Topics.INBOUND, core.bmi, mapper.writeValueAsString(inbound))
        log.info("RCVD bmi={} {}->{} amt={} center={}", core.bmi, core.senderCode, core.receiverCode, core.amount, centerId)

        // 5) 접수 응답(RCVD)
        return Pacs002(
            orgnlBizMsgIdr = core.bmi,
            from = "RTGS-$centerId",
            to = core.senderCode,
            txSts = TxSts.RCVD.name,
            clrSysRef = core.bmi,
            creDtTm = Instant.now().toString(),
        )
    }

    private fun reject(bmi: String, to: String, sender: String, reason: String) = Pacs002(
        orgnlBizMsgIdr = bmi,
        from = "RTGS-$centerId",
        to = sender,
        txSts = TxSts.RJCT.name,
        clrSysRef = reason,
        creDtTm = Instant.now().toString(),
    )
}
