/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 */
package appauth

import std.collection.HashSet
import stdx.encoding.json.JsonObject
import std.collection.HashMap
import std.collection.Set
import std.collection.Map
import stdx.encoding.json.JsonValue

public class AuthorizationRequest <: AuthorizationManagementRequest{
    public static let CODE_CHALLENGE_METHOD_S256: String = "S256"
    public static let CODE_CHALLENGE_METHOD_PLAIN: String = "plain"

    public static let PARAM_CLIENT_ID: String = "client_id"
    public static let PARAM_CODE_CHALLENGE: String = "code_challenge"
    public static let PARAM_CODE_CHALLENGE_METHOD: String = "code_challenge_method"
    public static let PARAM_DISPLAY: String = "display"
    public static let PARAM_LOGIN_HINT: String = "login_hint"
    public static let PARAM_PROMPT: String = "prompt"
    public static let PARAM_UI_LOCALES: String = "ui_locales"
    public static let PARAM_REDIRECT_URI: String = "redirect_uri"
    public static let PARAM_RESPONSE_MODE: String = "response_mode"
    public static let PARAM_RESPONSE_TYPE: String = "response_type"
    public static let PARAM_SCOPE: String = "scope"
    public static let PARAM_STATE: String = "state"
    public static let PARAM_NONCE: String = "nonce"
    public static let PARAM_CLAIMS: String = "claims"
    public static let PARAM_CLAIMS_LOCALES: String = "claims_locales"

    private static let PARAMS_ARRAY: Array<String> = [
        PARAM_CLIENT_ID,
        PARAM_CODE_CHALLENGE,
        PARAM_CODE_CHALLENGE_METHOD,
        PARAM_DISPLAY,
        PARAM_LOGIN_HINT,
        PARAM_PROMPT,
        PARAM_UI_LOCALES,
        PARAM_REDIRECT_URI,
        PARAM_RESPONSE_MODE,
        PARAM_RESPONSE_TYPE,
        PARAM_SCOPE,
        PARAM_NONCE,
        PARAM_CLAIMS,
        PARAM_CLAIMS_LOCALES
        ]

    static let BUILT_IN_PARAMS: HashSet<String> = AdditionalParamsProcessor.builtInParams(PARAMS_ARRAY)

    private static let KEY_CONFIGURATION: String = "configuration"
    private static let KEY_CLIENT_ID: String = "clientId"
    private static let KEY_DISPLAY: String = "display"
    private static let KEY_LOGIN_HINT: String = "login_hint"
    private static let KEY_PROMPT: String = "prompt"
    private static let KEY_UI_LOCALES: String = "ui_locales"
    private static let KEY_RESPONSE_TYPE: String = "responseType"
    private static let KEY_REDIRECT_URI: String = "redirectUri"
    private static let KEY_SCOPE: String = "scope"
    private static let KEY_STATE: String = "state"
    private static let KEY_NONCE: String = "nonce"
    private static let KEY_CODE_VERIFIER: String = "codeVerifier"
    private static let KEY_CODE_VERIFIER_CHALLENGE: String = "codeVerifierChallenge"
    private static let KEY_CODE_VERIFIER_CHALLENGE_METHOD: String = "codeVerifierChallengeMethod"
    private static let KEY_RESPONSE_MODE: String = "responseMode"
    private static let KEY_CLAIMS: String = "claims"
    private static let KEY_CLAIMS_LOCALES: String = "claimsLocales"
    private static let KEY_ADDITIONAL_PARAMETERS: String = "additionalParameters"

    public let configuration: AuthorizationServiceConfiguration
    public let clientId: String
    public let display: String
    public let loginHint: String
    public let prompt: String
    public let uiLocales: String
    public let responseType: String
    public let redirectUri: AuthUri
    public let scope: String
    public let state: String
    public let nonce: String
    public let codeVerifier: String
    public let codeVerifierChallenge: String
    public let codeVerifierChallengeMethod: String
    public let responseMode: String
    public let claims: JsonObject
    public let claimsLocales: String
    public let additionalParameters: Map<String, String>

    init(
        configuration: AuthorizationServiceConfiguration,
        clientId: String,
        responseType: String,
        redirectUri: AuthUri,
        display: String,
        loginHint: String,
        prompt: String,
        uiLocales: String,
        scope: String,
        state: String,
        nonce: String,
        codeVerifier: String,
        codeVerifierChallenge: String,
        codeVerifierChallengeMethod: String,
        responseMode: String,
        claims: JsonObject,
        claimsLocales: String,
        additionalParameters: Map<String, String>
    ) {
    	// mandatory fields
        this.configuration = configuration
        this.clientId = clientId
        this.responseType = responseType
        this.redirectUri = redirectUri
        this.additionalParameters = additionalParameters

        // optional fields
        this.display = display
        this.loginHint = loginHint
        this.prompt = prompt
        this.uiLocales = uiLocales
        this.scope = scope
        this.state = state
        this.nonce = nonce
        this.codeVerifier = codeVerifier
        this.codeVerifierChallenge = codeVerifierChallenge
        this.codeVerifierChallengeMethod = codeVerifierChallengeMethod
        this.responseMode = responseMode
        this.claims = claims
        this.claimsLocales = claimsLocales
    }

    /**
     * 获取scope集合
     * @return scope集合
     */
    public func getScopeSet(): Set<String> {
    	return AsciiStringListUtil.stringToSet(scope)
    }

    /**
     * 获取prompt值集合
     * @return prompt值集合
     */
    public func getPromptValues(): Set<String> {
    	return AsciiStringListUtil.stringToSet(prompt)
    }

    /**
     * 获取UI语言环境集合
     * @return UI语言环境集合
     */
    public func getUiLocales(): Set<String> {
    	return AsciiStringListUtil.stringToSet(uiLocales)
    }

    /**
     * 获取状态值
     * @return 状态值
     */
    public func getState(): String {
        return state
    }

    /**
     * 获取声明语言环境集合
     * @return 声明语言环境集合
     */
    public func getClaimsLocales(): Set<String> {
    	return AsciiStringListUtil.stringToSet(claimsLocales)
    }

    public override func toUri(): AuthUri {
        var authorizationEndpoint = configuration.authorizationEndpoint.getOrThrow()

    	var builder = authorizationEndpoint.buildUpon()
            .appendQueryParameter(PARAM_REDIRECT_URI, redirectUri.toString())
            .appendQueryParameter(PARAM_CLIENT_ID, clientId)
            .appendQueryParameter(PARAM_RESPONSE_TYPE, responseType)

        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_DISPLAY, display)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_LOGIN_HINT, loginHint)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_PROMPT, prompt)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_UI_LOCALES, uiLocales)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_STATE, state)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_NONCE, nonce)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_SCOPE, scope)
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_RESPONSE_MODE, responseMode)
        
        if (!codeVerifier.isEmpty()) {
        	builder.appendQueryParameter(PARAM_CODE_CHALLENGE, codeVerifierChallenge)
                .appendQueryParameter(PARAM_CODE_CHALLENGE_METHOD, codeVerifierChallengeMethod)
        }

        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_CLAIMS, claims.toString())
        UriUtil.appendQueryParameterIfNotNull(builder, PARAM_CLAIMS_LOCALES, claimsLocales)

        var keys = additionalParameters.keys()
        for (key in keys) {
        	builder.appendQueryParameter(key, additionalParameters.get(key).getOrThrow())
        }

        return builder.build()
    }

    /**
     * 将对象序列化为JSON对象
     * @return JSON对象
     */
    public override func jsonSerialize(): JsonObject {
    	var json: JsonObject = JsonObject()
        JsonUtil.put(json, KEY_CONFIGURATION, configuration.toJson())
        JsonUtil.put(json, KEY_CLIENT_ID, clientId)
        JsonUtil.put(json, KEY_RESPONSE_TYPE, responseType)
        JsonUtil.put(json, KEY_REDIRECT_URI, redirectUri.toString())
        JsonUtil.putIfNotNull(json, KEY_DISPLAY, display)
        JsonUtil.putIfNotNull(json, KEY_LOGIN_HINT, loginHint)
        JsonUtil.putIfNotNull(json, KEY_SCOPE, scope)
        JsonUtil.putIfNotNull(json, KEY_PROMPT, prompt)
        JsonUtil.putIfNotNull(json, KEY_UI_LOCALES, uiLocales)
        JsonUtil.putIfNotNull(json, KEY_STATE, state)
        JsonUtil.putIfNotNull(json, KEY_NONCE, nonce)
        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER, codeVerifier)
        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE, codeVerifierChallenge)
        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD,
                codeVerifierChallengeMethod)
        JsonUtil.putIfNotNull(json, KEY_RESPONSE_MODE, responseMode)
        JsonUtil.putIfNotNull(json, KEY_CLAIMS, claims)
        JsonUtil.putIfNotNull(json, KEY_CLAIMS_LOCALES, claimsLocales)
        JsonUtil.put(json, KEY_ADDITIONAL_PARAMETERS,
                JsonUtil.mapToJsonObject(additionalParameters))
        return json
    }

    /**
     * 将对象序列化为JSON字符串
     * @return JSON字符串
     */
    public func jsonSerializeString(): String {
    	return jsonSerialize().toString()
    }

    /**
     * 从JSON对象反序列化为AuthorizationRequest对象
     * @param json JSON对象
     * @return AuthorizationRequest对象
     */
    public static func jsonDeserialize(json: JsonObject): AuthorizationRequest {
    	return AuthorizationRequest(
                AuthorizationServiceConfiguration.fromJson(json.get(KEY_CONFIGURATION).getOrThrow().asObject()),
                JsonUtil.getString(json, KEY_CLIENT_ID),
                JsonUtil.getString(json, KEY_RESPONSE_TYPE),
                JsonUtil.getUri(json, KEY_REDIRECT_URI),
                JsonUtil.getStringIfDefined(json, KEY_DISPLAY),
                JsonUtil.getStringIfDefined(json, KEY_LOGIN_HINT),
                JsonUtil.getStringIfDefined(json, KEY_PROMPT),
                JsonUtil.getStringIfDefined(json, KEY_UI_LOCALES),
                JsonUtil.getStringIfDefined(json, KEY_SCOPE),
                JsonUtil.getStringIfDefined(json, KEY_STATE),
                JsonUtil.getStringIfDefined(json, KEY_NONCE),
                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER),
                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE),
                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD),
                JsonUtil.getStringIfDefined(json, KEY_RESPONSE_MODE),
                JsonUtil.getJsonObjectIfDefined(json, KEY_CLAIMS),
                JsonUtil.getStringIfDefined(json, KEY_CLAIMS_LOCALES),
                JsonUtil.getStringMap(json, KEY_ADDITIONAL_PARAMETERS))
    }

    /**
     * 从JSON字符串反序列化为AuthorizationRequest对象
     * @param jsonStr JSON字符串
     * @return AuthorizationRequest对象
     * @throws NoneValueException 当JSON字符串为空时抛出异常
     */
    public static func jsonDeserialize(jsonStr: String): AuthorizationRequest {
    	if (jsonStr.isEmpty()) {
    		throw NoneValueException("json string cannot be null")
    	}
        return jsonDeserialize(JsonValue.fromStr(jsonStr).asObject())
    }
}

public class AuthorizationRequest_Builder {
    private var mConfiguration: AuthorizationServiceConfiguration
    private var mClientId: String = ""
    private var mDisplay: String = ""
    private var mLoginHint: String = ""
    private var mPrompt: String = ""
    private var mUiLocales: String = ""
    private var mResponseType: String = ""
    private var mRedirectUri: AuthUri
    private var mScope: String = ""
    private var mState: String = ""
    private var mNonce: String = ""
    private var mCodeVerifier: String = ""
    private var mCodeVerifierChallenge: String = ""
    private var mCodeVerifierChallengeMethod: String = ""
    private var mResponseMode: String = ""
    private var mClaims: JsonObject = JsonObject()
    private var mClaimsLocales: String = ""
    private var mAdditionalParameters: HashMap<String, String> = HashMap<String, String>()

    public init(
        configuration: AuthorizationServiceConfiguration,
        clientId: String,
        responseType: String,
        redirectUri: AuthUri
    ) {
        mConfiguration = configuration
        mClientId = clientId
        mRedirectUri = redirectUri

        setResponseType(responseType)
        setState(AuthorizationManagementUtil.generateRandomState())
        setNonce(AuthorizationManagementUtil.generateRandomState())
        setCodeVerifier(CodeVerifierUtil.generateRandomCodeVerifier())
    }

    /**
     * 设置授权服务配置
     * @param configuration 授权服务配置
     * @return AuthorizationRequest_Builder对象
     */
    public func setAuthorizationServiceConfiguration(configuration: AuthorizationServiceConfiguration): AuthorizationRequest_Builder {
        mConfiguration = configuration
    	return this
    }

    /**
     * 设置客户端ID
     * @param clientId 客户端ID
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当客户端ID为空时抛出异常
     */
    public func setClientId(clientId: String): AuthorizationRequest_Builder {
        if (clientId.isEmpty()) {
        	throw NoneValueException("client ID cannot be empty")
        } else {
        	mClientId = clientId
        }
    	return this
    }

    /**
     * 设置显示类型
     * @param display 显示类型
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当显示类型为空时抛出异常
     */
    public func setDisplay(display: String): AuthorizationRequest_Builder {
        if (display.isEmpty()) {
        	throw NoneValueException("display cannot be empty")
        } else {
        	mDisplay = display
        }
    	return this
    }

    /**
     * 设置登录提示
     * @param loginHint 登录提示
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当登录提示为空时抛出异常
     */
    public func setLoginHint(loginHint: String): AuthorizationRequest_Builder {
        if (loginHint.isEmpty()) {
        	throw NoneValueException("loginHint cannot be empty")
        } else {
        	mLoginHint = loginHint
        }
    	return this
    }

    /**
     * 设置提示类型
     * @param prompt 提示类型
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当提示类型为空时抛出异常
     */
    public func setPrompt(prompt: String): AuthorizationRequest_Builder {
        if (prompt.isEmpty()) {
        	throw NoneValueException("prompt cannot be empty")
        } else {
        	mPrompt = prompt
        }
    	return this
    }

    /**
     * 设置提示值数组
     * @param promptValues 提示值数组
     * @return AuthorizationRequest_Builder对象
     */
    public func setPromptValues(promptValues: Array<String>): AuthorizationRequest_Builder {
        if (promptValues.isEmpty()) {
        	mPrompt = ""
        } else {
        	mPrompt = AsciiStringListUtil.iterableToString(promptValues)
        }
    	return this
    }

    /**
     * 设置UI语言环境
     * @param uiLocales UI语言环境
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当UI语言环境为空时抛出异常
     */
    public func setUiLocales(uiLocales: String): AuthorizationRequest_Builder {
        if (uiLocales.isEmpty()) {
        	throw NoneValueException("uiLocales cannot be empty")
        } else {
        	mUiLocales = uiLocales
        }
    	return this
    }

    /**
     * 设置UI语言环境值数组
     * @param promptValues UI语言环境值数组
     * @return AuthorizationRequest_Builder对象
     */
    public func setUiLocalesValues(promptValues: Array<String>): AuthorizationRequest_Builder {
        if (promptValues.isEmpty()) {
        	mUiLocales = ""
        } else {
        	mUiLocales = AsciiStringListUtil.iterableToString(promptValues)
        }
    	return this
    }

    /**
     * 设置响应类型
     * @param responseType 响应类型
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当响应类型为空时抛出异常
     */
    public func setResponseType(responseType: String): AuthorizationRequest_Builder {
        if (responseType.isEmpty()) {
        	throw NoneValueException("expected response type cannot be empty")
        } else {
        	mResponseType = responseType
        }
    	return this
    }

    /**
     * 设置重定向URI
     * @param redirectUri 重定向URI
     * @return AuthorizationRequest_Builder对象
     */
    public func setRedirectUri(redirectUri: AuthUri): AuthorizationRequest_Builder {
        mRedirectUri = redirectUri
    	return this
    }

    /**
     * 设置作用域
     * @param scope 作用域
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当作用域为空时抛出异常
     */
    public func setScope(scope: String): AuthorizationRequest_Builder {
        if (scope.isEmpty()) {
        	throw NoneValueException("scope cannot be empty")
        } else {
        	mScope = scope
        }
    	return this
    }

    /**
     * 设置作用域数组
     * @param scopes 作用域数组
     * @return AuthorizationRequest_Builder对象
     */
    public func setScopes(scopes: Array<String>): AuthorizationRequest_Builder {
        if (scopes.isEmpty()) {
        	mScope = ""
        } else {
        	mScope = AsciiStringListUtil.iterableToString(scopes)
        }
    	return this
    }

    /**
     * 设置状态值
     * @param state 状态值
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当状态值为空时抛出异常
     */
    public func setState(state: String): AuthorizationRequest_Builder {
        if (state.isEmpty()) {
        	throw NoneValueException("state cannot be empty")
        } else {
        	mState = state
        }
    	return this
    }

    /**
     * 设置nonce值
     * @param nonce nonce值
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当nonce值为空时抛出异常
     */
    public func setNonce(nonce: String): AuthorizationRequest_Builder {
        if (nonce.isEmpty()) {
        	throw NoneValueException("nonce cannot be empty")
        } else {
        	mNonce = nonce
        }
    	return this
    }

    /**
     * 设置代码验证器
     * @param codeVerifier 代码验证器
     * @return AuthorizationRequest_Builder对象
     */
    public func setCodeVerifier(codeVerifier: String): AuthorizationRequest_Builder {
        if (!codeVerifier.isEmpty()) {
        	CodeVerifierUtil.checkCodeVerifier(codeVerifier)
            mCodeVerifier = codeVerifier
            mCodeVerifierChallenge = CodeVerifierUtil.deriveCodeVerifierChallenge(codeVerifier)
            mCodeVerifierChallengeMethod = CodeVerifierUtil.getCodeVerifierChallengeMethod()
        } else {
        	mCodeVerifier = ""
            mCodeVerifierChallenge = ""
            mCodeVerifierChallengeMethod = ""
        }
    	return this
    }

    /**
     * 设置代码验证器及相关参数
     * @param codeVerifier 代码验证器
     * @param codeVerifierChallenge 代码验证器挑战值
     * @param codeVerifierChallengeMethod 代码验证器挑战方法
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当参数不匹配时抛出异常
     */
    public func setCodeVerifier(codeVerifier: String, codeVerifierChallenge: String, codeVerifierChallengeMethod: String): AuthorizationRequest_Builder {
        if (!codeVerifier.isEmpty()) {
            CodeVerifierUtil.checkCodeVerifier(codeVerifier)
            if (codeVerifierChallenge.isEmpty()) {
            	throw NoneValueException("code verifier challenge cannot be null or empty if verifier is set")
            }
            if (codeVerifierChallengeMethod.isEmpty()) {
            	throw NoneValueException("code verifier challenge method cannot be null or empty if verifier is set")
            }
        } else {
            if (!codeVerifierChallenge.isEmpty()) {
            	throw NoneValueException("code verifier challenge must be null if verifier is null")
            }
            if (!codeVerifierChallengeMethod.isEmpty()) {
            	throw NoneValueException("code verifier challenge method must be null if verifier is null")
            }
        }
        mCodeVerifier = codeVerifier
        mCodeVerifierChallenge = codeVerifierChallenge
        mCodeVerifierChallengeMethod = codeVerifierChallengeMethod
    	return this
    }

    /**
     * 设置响应模式
     * @param responseMode 响应模式
     * @return AuthorizationRequest_Builder对象
     * @throws NoneValueException 当响应模式为空时抛出异常
     */
    public func setResponseMode(responseMode: String): AuthorizationRequest_Builder {
        if (responseMode.isEmpty()) {
        	throw NoneValueException("responseMode must not be empty")
        } else {
        	mResponseMode = responseMode
        }
    	return this
    }

    /**
     * 设置声明
     * @param claims 声明
     * @return AuthorizationRequest_Builder对象
     */
    public func setClaims(claims: JsonObject): AuthorizationRequest_Builder {
        mClaims = claims
    	return this
    }

    /**
     * 设置声明语言环境值数组
     * @param claimsLocalesValues 声明语言环境值数组
     * @return AuthorizationRequest_Builder对象
     */
    public func setClaimsLocales(claimsLocalesValues: Array<String>): AuthorizationRequest_Builder {
        mClaimsLocales = AsciiStringListUtil.iterableToString(claimsLocalesValues)
    	return this
    }

    /**
     * 设置附加参数
     * @param additionalParameters 附加参数
     * @return AuthorizationRequest_Builder对象
     */
    public func setAdditionalParameters(additionalParameters: HashMap<String, String>): AuthorizationRequest_Builder {
        var tmpMap = AdditionalParamsProcessor.checkAdditionalParams(additionalParameters, AuthorizationRequest.BUILT_IN_PARAMS)
        mAdditionalParameters.add(all: tmpMap)
    	return this
    }

    /**
     * 构建AuthorizationRequest对象
     * @return AuthorizationRequest对象
     */
    public func build(): AuthorizationRequest {
    	return AuthorizationRequest(
            mConfiguration,
            mClientId,
            mResponseType,
            mRedirectUri,
            mDisplay,
            mLoginHint,
            mPrompt,
            mUiLocales,
            mScope,
            mState,
            mNonce,
            mCodeVerifier,
            mCodeVerifierChallenge,
            mCodeVerifierChallengeMethod,
            mResponseMode,
            mClaims,
            mClaimsLocales,
            HashMap<String, String>(mAdditionalParameters)
    	)
    }
}

public class AuthorizationRequest_Display {
    public static let PAGE: String = "page"
    public static let POPUP: String = "popup"
    public static let TOUCH: String = "touch"
    public static let WAP: String = "wap"
}

public class AuthorizationRequest_Prompt {
    public static let NONE: String = "none"
    public static let LOGIN: String = "login"
    public static let CONSENT: String = "consent"
    public static let SELECT_ACCOUNT: String = "select_account"
}

public class AuthorizationRequest_Scope {
    public static let ADDRESS: String = "address"
    public static let EMAIL: String = "email"
    public static let OFFLINE_ACCESS: String = "offline_access"
    public static let OPENID: String = "openid"
    public static let PHONE: String = "phone"
    public static let PROFILE: String = "profile"
}

public class AuthorizationRequest_ResponseMode {
    public static let QUERY: String = "query"
    public static let FRAGMENT: String = "fragment"
}