package kr.or.bok.rtgs.louisvuitton

import kr.or.bok.rtgs.common.ledger.Account
import org.slf4j.LoggerFactory
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.web.bind.annotation.DeleteMapping
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.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.transaction.annotation.Transactional

data class InstitutionReq(val code: String, val name: String, val balance: Long = 1_000_000_000)
data class UserReq(val username: String, val displayName: String = "", val role: String = "ORG_S", val orgCode: String? = null)

/**
 * 관리자(Louis Vuitton) API. 인증 강제는 후속 단계이므로 현재는 관리 마스터/운영 기능만.
 */
@RestController
class AdminController(
    private val jdbc: JdbcTemplate,
    private val accounts: AccountRepository,
    private val users: AppUserRepository,
) {
    private val log = LoggerFactory.getLogger(javaClass)

    // ---------- 1) 테스트용 DB 초기화 ----------
    @PostMapping("/admin/reset")
    @Transactional
    fun reset(@RequestParam(defaultValue = "1000000000") initialBalance: Long): Map<String, Any?> {
        jdbc.execute("TRUNCATE transfer, journal_log, settlement_view, raw_message, notification")
        val updated = jdbc.update("UPDATE account SET balance = ?", initialBalance)
        log.warn("ADMIN RESET: cleared ledger tables, reset {} accounts to {}", updated, initialBalance)
        return mapOf("ok" to true, "accountsReset" to updated, "initialBalance" to initialBalance,
            "cleared" to listOf("transfer", "journal_log", "settlement_view", "raw_message", "notification"),
            "note" to "Kafka 저널 이력은 보존(오프셋 커밋으로 재처리 안 함). DB만 초기화.")
    }

    // ---------- 2) 코드 관리: 참가기관(당좌계좌) 마스터 ----------
    @GetMapping("/admin/institutions")
    fun institutions() = accounts.findAll().sortedBy { it.code }
        .map { mapOf("code" to it.code, "name" to it.name, "balance" to it.balance) }

    @PostMapping("/admin/institutions")
    fun upsertInstitution(@RequestBody r: InstitutionReq): Map<String, Any?> {
        require(r.code.matches(Regex("[0-9]{4}"))) { "기관코드는 숫자 4자리" }
        accounts.save(Account(code = r.code, name = r.name, balance = r.balance))
        return mapOf("ok" to true, "code" to r.code)
    }

    @DeleteMapping("/admin/institutions/{code}")
    fun deleteInstitution(@PathVariable code: String): Map<String, Any?> {
        accounts.deleteById(code)
        return mapOf("ok" to true, "deleted" to code)
    }

    /** 처리상태 코드 데이터 사전. */
    @GetMapping("/admin/status-codes")
    fun statusCodes() = listOf(
        mapOf("code" to "RCVD", "name" to "접수", "desc" to "Received - 접수됨"),
        mapOf("code" to "ACTC", "name" to "승인", "desc" to "AcceptedTechnicalValidation - 검증 통과"),
        mapOf("code" to "PDNG", "name" to "대기", "desc" to "Pending - 타 센터 수신/결제 전"),
        mapOf("code" to "ACSP", "name" to "예약", "desc" to "AcceptedSettlementInProcess - 결제 반영, 결과 대기"),
        mapOf("code" to "ACCC", "name" to "입금처리완료", "desc" to "AcceptedSettlementCompleted - 최종 완결"),
        mapOf("code" to "RJCT", "name" to "반려", "desc" to "Rejected - 검증 실패/잔액부족 등"),
    )

    // ---------- 3) 사용자 권한 관리 (관리 마스터) ----------
    @GetMapping("/admin/users")
    fun listUsers() = users.findAll().sortedBy { it.username }
        .map { mapOf("username" to it.username, "displayName" to it.displayName, "role" to it.role, "orgCode" to it.orgCode) }

    @PostMapping("/admin/users")
    fun upsertUser(@RequestBody r: UserReq): Map<String, Any?> {
        require(r.role in setOf("ADMIN", "ORG_S", "ORG_R")) { "role은 ADMIN/ORG_S/ORG_R" }
        users.save(AppUser(username = r.username, displayName = r.displayName, role = r.role,
            orgCode = r.orgCode, createdAt = System.currentTimeMillis()))
        return mapOf("ok" to true, "username" to r.username)
    }

    @DeleteMapping("/admin/users/{username}")
    fun deleteUser(@PathVariable username: String): Map<String, Any?> {
        users.deleteById(username)
        return mapOf("ok" to true, "deleted" to username)
    }

    // ---------- 4) 대사(정합성) 대시보드 ----------
    @GetMapping("/admin/summary")
    fun summary(): Map<String, Any?> = mapOf(
        "byStatus" to jdbc.queryForList("SELECT status, count(*) AS cnt FROM transfer GROUP BY status ORDER BY status"),
        "totalBalance" to jdbc.queryForObject("SELECT COALESCE(sum(balance),0) FROM account", Long::class.java),
        "accounts" to jdbc.queryForObject("SELECT count(*) FROM account", Long::class.java),
        "transfers" to jdbc.queryForObject("SELECT count(*) FROM transfer", Long::class.java),
        "maxSeq" to jdbc.queryForObject("SELECT COALESCE(max(global_seq),0) FROM transfer", Long::class.java),
    )

    // ---------- 5) 서비스 헬스(B5) — 하트비트 기반 liveness(헤드리스 포함) ----------
    @GetMapping("/admin/health")
    fun health(): List<Map<String, Any?>> {
        val now = System.currentTimeMillis()
        return jdbc.queryForList("SELECT service, center, last_seen, pid FROM service_heartbeat ORDER BY service")
            .map { r ->
                val last = (r["last_seen"] as? Number)?.toLong() ?: 0
                val ageMs = now - last
                mapOf(
                    "service" to r["service"], "center" to r["center"], "pid" to r["pid"],
                    "lastSeen" to last, "ageMs" to ageMs,
                    "status" to if (ageMs <= 15000) "UP" else "STALE",
                )
            }
    }

    private fun lim(n: Int) = n.coerceIn(1, 200).toString()

    @GetMapping("/admin/ledger/transfers")
    fun transfers(@RequestParam(defaultValue = "20") limit: Int) = jdbc.queryForList(
        "SELECT bmi, global_seq, status, sender_code, receiver_code, amount, origin_center, updated_at " +
            "FROM transfer ORDER BY global_seq DESC NULLS LAST LIMIT ${lim(limit)}")

    @GetMapping("/admin/ledger/journal")
    fun journal(@RequestParam(defaultValue = "20") limit: Int) = jdbc.queryForList(
        "SELECT global_seq, bmi, applied FROM journal_log ORDER BY global_seq DESC LIMIT ${lim(limit)}")

    @GetMapping("/admin/ledger/views")
    fun views(@RequestParam(defaultValue = "20") limit: Int) = jdbc.queryForList(
        "SELECT bmi, global_seq, final_status, debtor_balance_after, creditor_balance_after, finalized_at " +
            "FROM settlement_view ORDER BY global_seq DESC NULLS LAST LIMIT ${lim(limit)}")
}
