package kr.or.bok.rtgs.gucci

import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.jdbc.core.JdbcTemplate
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.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException

/**
 * 콜백 레지스트리 관리(관리자) + 기관 수신 sink(데모).
 * - 레지스트리: 기관별 결과통보 수신 URL 조회/등록.
 * - sink: 로컬 데모용 "기관 수신 서버" 모사 — pacs.002를 받아 ACK(200) 응답.
 */
@RestController
class CallbackController(
    private val jdbc: JdbcTemplate,
    private val jwt: JwtService,
) {
    private val log = LoggerFactory.getLogger(javaClass)

    data class CallbackReq(val callbackUrl: String = "", val active: Boolean = true)

    /** 콜백 레지스트리 목록(관리자). */
    @GetMapping("/gucci/callbacks", produces = [MediaType.APPLICATION_JSON_VALUE])
    fun list(@RequestHeader("Authorization", required = false) authz: String?): List<Map<String, Any?>> {
        requireAdmin(authz)
        return jdbc.queryForList("SELECT org_code, callback_url, active FROM institution_endpoint ORDER BY org_code")
    }

    /** 기관 콜백 URL 등록/수정(관리자). */
    @PutMapping("/gucci/callbacks/{org}", consumes = [MediaType.APPLICATION_JSON_VALUE])
    fun upsert(
        @PathVariable org: String,
        @RequestBody req: CallbackReq,
        @RequestHeader("Authorization", required = false) authz: String?,
    ): Map<String, Any?> {
        requireAdmin(authz)
        jdbc.update(
            "INSERT INTO institution_endpoint(org_code, callback_url, active, created_at) VALUES (?,?,?,?) " +
                "ON CONFLICT (org_code) DO UPDATE SET callback_url = EXCLUDED.callback_url, active = EXCLUDED.active",
            org, req.callbackUrl, req.active, System.currentTimeMillis(),
        )
        log.info("CALLBACK upsert org={} url={} active={}", org, req.callbackUrl, req.active)
        return mapOf("ok" to true, "orgCode" to org, "callbackUrl" to req.callbackUrl, "active" to req.active)
    }

    /**
     * 기관 수신 sink(데모) — 실제 참가기관 수신 서버 대역. pacs.002를 받아 로그 후 ACK.
     * 로컬 왕복 시연용(운영에선 각 기관의 실제 엔드포인트).
     */
    @PostMapping("/gucci/sink/{org}", consumes = [MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE])
    fun sink(@PathVariable org: String, @RequestBody body: String): Map<String, Any?> {
        log.info("SINK 수신 org={} bytes={} (기관 수신 모사 → ACK)", org, body.length)
        return mapOf("ack" to true, "org" to org, "received" to body.length)
    }

    private fun requireAdmin(authz: String?) {
        val token = authz?.removePrefix("Bearer ")?.trim()
            ?: throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "missing bearer token")
        val p = try { jwt.verify(token, System.currentTimeMillis()) }
        catch (e: JwtException) { throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid token") }
        if (p.role != "ADMIN") throw ResponseStatusException(HttpStatus.FORBIDDEN, "admin only")
    }
}
