package kr.or.bok.rtgs.chanel

import kr.or.bok.rtgs.chanel.jpa.AccountRepository
import kr.or.bok.rtgs.chanel.jpa.NotificationRepository
import kr.or.bok.rtgs.chanel.jpa.RawMessageRepository
import kr.or.bok.rtgs.chanel.jpa.TransferRepository
import kr.or.bok.rtgs.common.iso20022.Iso20022Codec
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

/**
 * 전문송수신 제어 — 신청/접수(pacs.008) 및 조회(inquiry/accounts) API.
 * 조회는 권위 원장(PostgreSQL)에서 읽는다(프로토타입; 부하 분리 시 조회 전용 사본으로 확장).
 */
@RestController
class ChanelController(
    private val service: ChanelService,
    private val transfers: TransferRepository,
    private val accounts: AccountRepository,
    private val notifications: NotificationRepository,
    private val rawMessages: RawMessageRepository,
) {
    /** 자금이체 신청/접수: pacs.008 XML → pacs.002 XML. */
    @PostMapping(
        "/pay/customer",
        consumes = [MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.TEXT_PLAIN_VALUE],
        produces = [MediaType.APPLICATION_XML_VALUE],
    )
    fun payCustomer(@RequestBody rawXml: String): String =
        Iso20022Codec.writePacs002(service.accept(rawXml))

    /** 거래 상태 조회. 서비스별 단계 처리시각(타임라인) 포함. */
    @GetMapping("/inquiry/{bmi}")
    fun inquiry(@PathVariable bmi: String): Map<String, Any?> {
        val t = transfers.findById(bmi).orElse(null)
            ?: return mapOf("bmi" to bmi, "status" to "IN_FLIGHT", "note" to "not yet persisted")
        val finalized = t.status.name == "ACCC" || t.status.name == "RJCT"
        val rcvdAt = rawMessages.findById(bmi).orElse(null)?.receivedAt
        return mapOf(
            "bmi" to t.bmi,
            "globalSeq" to t.globalSeq,
            "status" to t.status.name,
            "senderCode" to t.senderCode,
            "receiverCode" to t.receiverCode,
            "amount" to t.amount,
            "originCenter" to t.originCenter,
            "updatedAt" to t.updatedAt,
            "reason" to t.reason,
            // 서비스별 처리시각(epoch ms). 접수=Chanel, 순번=Sequencer, 기록=Dior, 정산=Hermes, 완결=Prada.
            "rcvdAt" to rcvdAt,
            "actcAt" to t.createdAt,
            "pdngAt" to t.pdngAt,
            "acspAt" to t.acspAt,
            "acccAt" to if (finalized) t.updatedAt else null,
        )
    }

    /** 참가기관 당좌계좌 잔액 목록(대사·모니터링용). */
    @GetMapping("/accounts")
    fun accounts(): List<Map<String, Any?>> =
        accounts.findAll().sortedBy { it.code }.map {
            mapOf("code" to it.code, "name" to it.name, "balance" to it.balance)
        }

    /** 결과통보 송부 내역(최근 100건). 신청/수취기관 앞 pacs.002 아웃박스. */
    @GetMapping("/notifications")
    fun notificationList(): List<Map<String, Any?>> =
        notifications.findTop100ByOrderByCreatedAtDesc().map(::toMap)

    /** 특정 거래의 결과통보(신청기관·수취기관) 내역 — 원문 pacs.002 포함. */
    @GetMapping("/notifications/{bmi}")
    fun notificationOf(@PathVariable bmi: String): List<Map<String, Any?>> =
        notifications.findByBmiOrderByRole(bmi).map { toMap(it, includeXml = true) }

    private fun toMap(n: kr.or.bok.rtgs.common.ledger.NotificationEntity, includeXml: Boolean = false): Map<String, Any?> =
        buildMap {
            put("bmi", n.bmi); put("toOrg", n.toOrg); put("role", n.role)
            put("finalStatus", n.finalStatus); put("originCenter", n.originCenter)
            put("reason", n.reason); put("delivered", n.delivered)
            put("createdAt", n.createdAt); put("deliveredAt", n.deliveredAt)
            if (includeXml) put("pacs002", n.pacs002Xml)
        }
}
