package gaf3.core.gateway.filter.factory

import io.jsonwebtoken.Claims
import org.slf4j.LoggerFactory
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.util.UriComponentsBuilder
import java.util.regex.Matcher
import java.util.regex.Pattern


/**
 * @author dyg
 */
class SetRequestParameterGatewayFilterFactory : AbstractNameValueGatewayFilterFactory() {

    override fun apply(config: NameValueConfig): GatewayFilter {
        return GatewayFilter { exchange: ServerWebExchange, chain: GatewayFilterChain ->
            val uri = exchange.request.uri
            var query: String? = uri.rawQuery
            if (query != null) {
                // 清空已有参数
                val regex = String.format("(?i)%s=([^&]*)", config.name)
                query = query.replace(regex.toRegex(), "").replace("&&".toRegex(), "&")
                if (query.startsWith("&")) {
                    query = query.substring(1)
                } else if (query.endsWith("&")) {
                    query = query.substring(0, query.length - 1)
                }
            } else {
                query = ""
            }

            // 处理参数值, 支持Jwt Claim变量 {jwt:xxx}
            var value: String? = config.value
            if (value != null && value.isNotEmpty()) {
                value = patternReplace(value, P_JWTVAR,
                        exchange.attributes[JwtParserGatewayFilterFactory.JWT_CLAIMS_ATTRIBUTE] as Claims?)
                value = patternReplace(value, P_WXVAR,
                        exchange.attributes[WeixinTokenGatewayFilterFactory.WEIXIN_CLAIMS_ATTRIBUTE] as Claims?)
            }

            // 生成新的query
            var part = ""
            if (value != null && value.isNotEmpty()) {
                part = String.format("%s=%s", config.name, value)
            }
            if (part.isNotEmpty() && query.isNotEmpty()) {
                query += "&$part"
            } else if (query.isEmpty()) {
                query = part
            }

            try {
                val newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query).build(true).toUri()

                val request = exchange.request.mutate().uri(newUri).build()

                return@GatewayFilter chain.filter(exchange.mutate().request(request).build())
            } catch (ex: RuntimeException) {
                throw IllegalStateException("Invalid URI query: \"$query\"")
            }
        }
    }

    private fun patternReplace(value: String?, p: Pattern, claims: Claims?): String? {
        if(value == null) return value
        val m: Matcher = p.matcher(value)
        if (m.matches()) {
            val str = try {
                val key: String = m.group(1)
                claims?.let { it[key, String::class.java] }
            } catch (e: Throwable) {
                log.warn("extract {} fail: {}", value, e.message)
                if (log.isDebugEnabled) e.printStackTrace()
                null
            } ?: ""
            return value.replace(p.toRegex(), str)
        }
        return value
    }

    @Suppress("SpellCheckingInspection")
    companion object {

        internal val log = LoggerFactory.getLogger(SetRequestParameterGatewayFilterFactory::class.java)
        internal val P_JWTVAR = Pattern.compile("\\{jwt:(.*)}")
        internal val P_WXVAR = Pattern.compile("\\{wx:(.*)}")
    }

}
