package gaf3.core.util

import com.alibaba.fastjson.JSON
import gaf3.core.exception.BusinessError
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.crypto.Signer
import org.bouncycastle.crypto.digests.SHA256Digest
import org.bouncycastle.crypto.params.AsymmetricKeyParameter
import org.bouncycastle.crypto.signers.RSADigestSigner
import org.bouncycastle.crypto.util.PublicKeyFactory
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.IOException
import java.util.regex.Pattern

/**
 * 基于证书签名的认证Token
 * Token由三部分组成，通过'.'分割，格式如下：payload.sig.cert
 * 三部分均为Base64编码，payload内容为JSON数据，sig为签名数据，cert为签名证书
 * payload包含:
 *  sub - 用户主题（用户名、账号等，与证书CN对应）
 *  exp - token的过期时间戳，从1970年1月1日至今的秒数
 *  alg - hash算法，默认为sha1
 */
class JsonCertToken private constructor() {
    private var payload: String? = null
    private var signature: ByteArray? = null
    private var certificate: X509CertificateHolder? = null

    val alg: String = "HS256"
    var exp: Long = 0
    lateinit var sub: String

    companion object {
        val log: Logger = LoggerFactory.getLogger(JsonCertToken::class.java)

        fun parse(token: String): JsonCertToken {
            val jct = JsonCertToken()

            // 验证token
            val tokens = token.split(".").toTypedArray()
            if (tokens.size != 3) throw BusinessError(BusinessError.ERR_TOKEN_INVALID,"证书认证Token格式无效")
            lateinit var payload: ByteArray
            lateinit var signData: ByteArray
            lateinit var cert: ByteArray
            try {
                val decoder = java.util.Base64.getUrlDecoder()
                payload = decoder.decode(tokens[0])
                signData = decoder.decode(tokens[1])
                cert = decoder.decode(tokens[2])
            } catch (e: Throwable) {
                log.error("解析Token失败", e)
                BusinessError(BusinessError.ERR_TOKEN_INVALID,"证书认证Token格式无效")
            }

            // 解析证书
            val certHolder: X509CertificateHolder
            val pubKey: AsymmetricKeyParameter
            try {
                certHolder = X509CertificateHolder(cert)
                pubKey = PublicKeyFactory.createKey(certHolder.subjectPublicKeyInfo)
            } catch (e: IOException) {
                log.error("解析证书失败", e)
                throw BusinessError(BusinessError.ERR_TOKEN_INVALID,"解析证书失败")
            }

            val signer: Signer = RSADigestSigner(SHA256Digest(), NISTObjectIdentifiers.id_sha256)
            signer.init(false, pubKey)
            signer.update(payload, 0, payload.size)
            if (!signer.verifySignature(signData)) {
                log.debug("签名验证失败")
                throw BusinessError(BusinessError.ERR_TOKEN_INVALID,"Token验证失败: 签名验证失败")
            }

            // 验证有效期
            val json = JSON.parseObject(String(payload))
            val timestamp = json.getLong("exp")
                ?: throw BusinessError(BusinessError.ERR_TOKEN_INVALID,"证书认证Token格式无效")

            // 验证用户名称
            val name: String? = json.getString("sub")
            val dn = certHolder.subject.toString()
            val cn: String = parseDN(dn)
            if (name != null && name != cn) {
                log.debug("名称不匹配: {} - {}", name, cn)
                throw BusinessError(BusinessError.ERR_TOKEN_INVALID,"Token验证失败: 名称不匹配")
            }

            log.trace("Token验证成功: {}", name ?: cn)
            jct.exp = timestamp
            jct.sub = name ?: cn

            return jct
        }

        private fun parseDN(dn: String?): String {
            // 解析DN
            val extractor = "CN=([^,]*),?"
            val pattern = Pattern.compile(extractor, Pattern.CASE_INSENSITIVE)
            val matcher = pattern.matcher(dn)
            if (!matcher.find()) {
                log.error("解析DN失败: {}", dn)
                throw BusinessError("解析DN失败")
            }
            return matcher.group(1)
        }
    }

}