package kr.or.bok.rtgs.gucci

import org.slf4j.LoggerFactory
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service

/** 로그인 실패(자격증명 불일치). */
class AuthFailedException(msg: String) : RuntimeException(msg)

/**
 * 참가기관/관리자 로그인 인증. 자격증명(비밀키)은 **LouisVuitton이 관리하는 app_user**(권한 마스터)에서
 * 확인한다(인증=Gucci, 권한마스터=LouisVuitton). 비밀키는 **BCrypt 해시**로 저장·대조. 인증 성공 시 JWT 발급.
 */
@Service
class AuthService(
    private val jdbc: JdbcTemplate,
    private val jwt: JwtService,
    private val encoder: PasswordEncoder,
) {
    private val log = LoggerFactory.getLogger(javaClass)

    data class LoginResult(
        val token: String, val role: String, val orgCode: String?,
        val expiresIn: Long, val mustChangePassword: Boolean,
    )

    fun login(username: String, secret: String, nowMs: Long): LoginResult {
        val rows = jdbc.queryForList(
            "SELECT role, org_code, secret, must_change_password FROM app_user WHERE username = ?", username,
        )
        val row = rows.firstOrNull() ?: run {
            log.warn("AUTH login FAIL user={} reason=unknown-user", username)
            throw AuthFailedException("invalid credentials")
        }
        val stored = row["secret"] as? String
        if (stored.isNullOrBlank() || !encoder.matches(secret, stored)) {
            log.warn("AUTH login FAIL user={} reason=bad-secret", username)
            throw AuthFailedException("invalid credentials")
        }
        val role = row["role"] as? String ?: ""
        val orgCode = row["org_code"] as? String
        val mustChange = row["must_change_password"] as? Boolean ?: false
        val token = jwt.issue(Principal(username, role, orgCode), nowMs)
        log.info("AUTH login OK user={} role={} org={} mustChange={}", username, role, orgCode, mustChange)
        return LoginResult(token, role, orgCode, jwt.ttl(), mustChange)
    }

    /** 초기암호(또는 비밀번호) 변경. 기존 비번 확인 후 새 비번(BCrypt) 저장 + 변경필요 해제. */
    fun changePassword(username: String, oldSecret: String, newSecret: String) {
        if (newSecret.length < 1) throw AuthFailedException("new secret too short")
        val rows = jdbc.queryForList("SELECT secret FROM app_user WHERE username = ?", username)
        val stored = rows.firstOrNull()?.get("secret") as? String
            ?: throw AuthFailedException("invalid credentials")
        if (!encoder.matches(oldSecret, stored)) {
            log.warn("AUTH changePw FAIL user={} reason=bad-old", username)
            throw AuthFailedException("invalid credentials")
        }
        jdbc.update(
            "UPDATE app_user SET secret = ?, must_change_password = false WHERE username = ?",
            encoder.encode(newSecret), username,
        )
        log.info("AUTH changePw OK user={}", username)
    }
}
