/*
 * Copyright (c) 2025 SimcuTeam. All rights reserved.
 * 遵循 MIT 许可证。
 * 自定义 IRequestDelegateFactory：在结果派发时自动封装响应，
 * 即 SimApiResponseFilter（IResultFilter）的行为：
 *   - 返回 SimApiBaseResponse 或其子类 → 原样输出
 *   - 返回 null/void（Unit）→ SimApiBaseResponse()（{code:200, message:成功}）
 *   - 返回 String → SimApiResponse<String>（data 为字符串）
 *   - 返回其他对象 → SimApiResponse<Any>（data 为对象）
 *
 * 注册方式：必须在 soulsoft addControllers() 之前注册（tryAddSingleton 先到先得）。
 */

package simcu::simapi.helpers

import std.collection.*
import std.io.*
import std.reflect.*
import stdx.encoding.json.*
import stdx.serialization.serialization.*
import soulsoft_web_http.*
import soulsoft_web_mvc.annotations.*
import soulsoft_web_mvc.core.*
import soulsoft_web_mvc.routing.*
import soulsoft_web_mvc.controllers.*
import soulsoft_web_mvc.modelBindings.*
import soulsoft_web_mvc.abstractions.*
import soulsoft_extensions_options.*
import soulsoft_extensions_injection.*
import simcu::serialization.*
import simcu::simapi.annotations.{SimApiAuth as SimApiAuthAttribute, OriginResponse, SimApiSign, AesBody}
import simcu::simapi.communications.*
import simcu::simapi.configurations.*
import simcu::simapi.interfaces.*

/**
 * 自定义请求委托工厂：接管 soulsoft 的 ControllerRequestDelegateFactory，
 * 在结果派发时自动封装响应。
 */
public class SimApiRequestDelegateFactory <: IRequestDelegateFactory {
    private let _mvcOptions: MvcOptions
    private let _modelBinder: IActionModelBinder

    public init(mvcOptions: IOptions<MvcOptions>, modelBinder: IActionModelBinder, _: IServiceProvider) {
        _mvcOptions = mvcOptions.value
        _modelBinder = modelBinder
    }

    public func createRequestDelegate(actionDescriptor: ControllerActionDescriptor): RequestDelegate {
        return {
            context => SimApiActionInvoker(context, _modelBinder, actionDescriptor, _mvcOptions).apply()
        }
    }
}

/**
 * 单次请求的动作执行器（复制 soulsoft ControllerActionInvoker，
 * dispatchResult 改为自动封装响应）。
 */
struct SimApiActionInvoker {
    SimApiActionInvoker(let context: HttpContext, let modelBinder: IActionModelBinder,
        let actionDescriptor: ControllerActionDescriptor, let mvcOptions: MvcOptions) {
    }

    public func apply(): Unit {
        let controller = createControllerInstance()
        checkSimApiAuth()
        checkSimApiSign()
        // 预读并缓存请求体（body 流不可重读；若请求日志中间件已读，直接用其缓存）
        if (!context.items.contains(BODY_CACHE_KEY)) {
            context.items[BODY_CACHE_KEY] = readBody()
        }
        let modelBindingContext = ActionBindingContext(context, actionDescriptor.actionFunction.parameters)
        // 自定义绑定：无注解参数（FromBody）用 simapi_serialization 反序列化，
        // 显式注解参数（Query/Form/Route/Header/Services）委托 soulsoft binder。
        let boundParameters = bindParameters(modelBindingContext)
        if (!modelBindingContext.modelState.isValid) {
            handleInvalidModelState(modelBindingContext)
        } else {
            let actionResult = actionDescriptor.actionFunction.apply(controller, boundParameters)
            dispatchResult(actionResult)
        }
    }

    /// 请求体缓存键（body 流不可重读，读一次后缓存）
    private static let BODY_CACHE_KEY = "SimApi:BodyCache"

    /// 绑定全部参数：FromBody 自实现（simapi_serialization），其余委托 soulsoft
    private func bindParameters(context: ActionBindingContext): Array<Any> {
        let params = context.parameters
        if (params.size == 0) {
            return []
        }
        let bound = Array<Any>(params.size, repeat: ())
        // 存在显式参数时才委托 soulsoft 绑定（Query/Form/Route/Header/Services）
        var hasExplicit = false
        for (parameter in params) {
            if (isExplicitlyBound(parameter)) {
                hasExplicit = true
                break
            }
        }
        let soulsoftBound = if (hasExplicit) { modelBinder.bind(context) } else { Array<Any>(params.size, repeat: ()) }
        for ((index, parameter) in params |> enumerate) {
            if (isExplicitlyBound(parameter)) {
                // Query/Form/Route/Header/Services → soulsoft
                bound[index] = soulsoftBound[index]
            } else if (let Some(aes) <- parameter.findAnnotation<AesBody>()) {
                // @AesBody → 解密 body 后按参数类型反序列化
                bound[index] = bindAesBody(context, parameter, aes)
            } else {
                // FromBody → simapi_serialization 按运行时类型反序列化（免 @Serialization 宏）
                bound[index] = bindFromBody(context, parameter)
            }
        }
        bound
    }

    /// 参数是否显式指定绑定源（FromQuery/FromForm/FromRoute/FromHeader/FromServices）
    private func isExplicitlyBound(parameter: ParameterInfo): Bool {
        parameter.findAnnotation<FromQuery>().isSome() ||
            parameter.findAnnotation<FromForm>().isSome() ||
            parameter.findAnnotation<FromRoute>().isSome() ||
            parameter.findAnnotation<FromHeader>().isSome() ||
            parameter.findAnnotation<FromServices>().isSome()
    }

    /// 从请求体反序列化（simapi_serialization 按运行时类型，DTO 免标注）
    private func bindFromBody(context: ActionBindingContext, parameter: ParameterInfo): Any {
        let body = match (context.httpContext.items.get(BODY_CACHE_KEY)) {
            case Some(v) => if (let s: String <- v) { s } else { "" }
            case None => ""
        }
        if (body.isEmpty()) {
            SimApiError.error(code: 400, message: "请求体不能为空")
        }
        try {
            return JsonSerializer.deserialize(parameter.typeInfo, body)
        } catch (ex: Exception) {
            SimApiError.error(code: 400, message: "请求体反序列化失败: ${ex.message}")
        }
        ()
    }

    /// @AesBody 参数绑定：解密 body 后按参数类型反序列化
    private func bindAesBody(context: ActionBindingContext, parameter: ParameterInfo, aes: AesBody): Any {
        // 1. 从 DI 解析 keyProvider（AesBodyProviderBase 实现）
        let provider = resolveAesProvider(aes.keyProvider)
        // 2. 读取并解密 body（SimApiAesBodyChecker.decryptBody 内部读取原始 body 流）
        let plain = SimApiAesBodyChecker.decryptBody(context.httpContext, provider)
        // 3. 按参数类型反序列化明文 JSON
        try {
            return JsonSerializer.deserialize(parameter.typeInfo, plain)
        } catch (ex: Exception) {
            SimApiError.error(code: 400, message: "AES body 反序列化失败: ${ex.message}")
        }
        ()
    }

    /// 从 DI 解析 AesBodyProviderBase 实现（注解未指定类型名时返回默认空实现）
    private func resolveAesProvider(keyProvider: String): AesBodyProviderBase {
        if (keyProvider.isEmpty()) {
            return AesBodyProviderBase()
        }
        var typeInfo: ?TypeInfo = None
        try {
            typeInfo = Some(TypeInfo.get(keyProvider))
        } catch (_: Exception) {
            SimApiError.error(code: 400, message: "未找到 AES 密钥提供器 ${keyProvider}")
        }
        let instance = context.services.getOrThrow(typeInfo.getOrThrow())
        if (let p: AesBodyProviderBase <- instance) {
            return p
        }
        SimApiError.error(code: 400, message: "密钥提供器 ${keyProvider} 未实现 AesBodyProviderBase")
        AesBodyProviderBase()
    }

    /// 检查 @SimApiSign 注解并执行验签
    private func checkSimApiSign() {
        var sign: ?SimApiSign = None
        for (item in actionDescriptor.endpointMetadata) {
            if (let s: SimApiSign <- item) {
                sign = Some(s)
                break
            }
        }
        if (let Some(sign) <- sign) {
            // 1. 从 DI 解析 keyProvider（SimApiSignProviderBase 实现）
            let provider = resolveSignProvider(sign.keyProvider)
            // 2. 解析缓存（nonce 去重；DI 有 SimApiCache 则用）
            var cache: ?SimApiCache = None
            try {
                cache = Some(context.services.getOrThrow<SimApiCache>())
            } catch (_: Exception) {
                // 未注册缓存 → 跳过 nonce 去重
            }
            // 3. 执行验签
            SimApiSignChecker.verify(context, provider, cache)
        }
    }

    /// 从 DI 解析 SimApiSignProviderBase 实现（注解未指定类型名时返回默认空实现）
    private func resolveSignProvider(keyProvider: String): SimApiSignProviderBase {
        if (keyProvider.isEmpty()) {
            return SimApiSignProviderBase()
        }
        var typeInfo: ?TypeInfo = None
        try {
            typeInfo = Some(TypeInfo.get(keyProvider))
        } catch (_: Exception) {
            SimApiError.error(code: 400, message: "未找到签名提供器 ${keyProvider}")
        }
        let instance = context.services.getOrThrow(typeInfo.getOrThrow())
        if (let p: SimApiSignProviderBase <- instance) {
            return p
        }
        SimApiError.error(code: 400, message: "签名提供器 ${keyProvider} 未实现 SimApiSignProviderBase")
        SimApiSignProviderBase()
    }

    /// 读取并重置请求体流（供后续业务读取）
    private func readBody(): String {
        try {
            context.request.enableBuffering()
            var buffer = Array<Byte>(4096, repeat: 0)
            var sb = StringBuilder()
            var read = context.request.body.read(buffer)
            while (read > 0) {
                sb.appendFromUtf8(buffer.slice(0, read))
                read = context.request.body.read(buffer)
            }
            let text = sb.toString()
            if (let seekable: Seekable <- context.request.body) {
                seekable.seek(SeekPosition.Begin(0))
            }
            text
        } catch (ex: Exception) {
            SimApiError.error(code: 400, message: "读取请求体失败: ${ex.message}")
        }
        ""
    }

    /// 检查 @SimApiAuth 注解并执行鉴权：
    /// 未登录 401 → 遍历执行 SimApiAuthChecker → 类型权限 403
    private func checkSimApiAuth() {
        var auth: ?SimApiAuthAttribute = None
        for (item in actionDescriptor.endpointMetadata) {
            if (let a: SimApiAuthAttribute <- item) {
                auth = Some(a)
                break
            }
        }
        if (let Some(auth) <- auth) {
            // 1. 未登录 → 401
            var loginItem: SimApiLoginItem = SimApiLoginItem("")
            match (context.items.get("LoginInfo")) {
                case Some(v) =>
                    if (let l: SimApiLoginItem <- v) {
                        loginItem = l
                    } else {
                        SimApiError.error(code: 401, message: "需要登录")
                    }
                case None =>
                    SimApiError.error(code: 401, message: "需要登录")
            }

            // 2. 遍历执行 SimApiAuthChecker
            let token = match (context.items.get("LoginToken")) {
                case Some(v) => if (let s: String <- v) { s } else { "" }
                case None => ""
            }
            let checkers = context.services.getAll<SimApiAuthChecker>()
            for (checker in checkers) {
                checker.run(loginItem, token)
            }

            // 3. 类型权限校验 → 403
            if (!auth.`type`.isEmpty()) {
                let requiredTypes = auth.`type`.split(",")
                var matched = false
                for (t in requiredTypes) {
                    if (loginItem.types.contains(t)) {
                        matched = true
                        break
                    }
                }
                if (!matched) {
                    SimApiError.error(code: 403, message: "无权访问")
                }
            }
        }
    }

    /// 模型绑定失败时写入 ProblemDetails 响应
    private func handleInvalidModelState(modelBindingContext: ActionBindingContext) {
        let options = context.services.getOrThrow<IOptions<ApiBehaviorOptions>>()
        if (let Some(factory) <- options.value.invalidModelStateResponseFactory) {
            let actionContext = ActionContext(context, modelBindingContext.modelState)
            factory(actionContext).invoke(context)
        } else {
            let details = createValidationProblemDetails(modelBindingContext)
            if (let Some(status) <- details.status) {
                context.response.statusCode = UInt16(status)
            }
            // 与 soulsoft writeAsJson 的 Serializable<T> 分支一致：serialize().toJson().toString()
            // （toJson 为 DataModel 接口扩展，需先上转）；writeAsJson 内部会设置 contentType，此处补上
            context.response.contentType = "application/json; charset=utf-8"
            let dm: DataModel = details.serialize()
            let jsonText = dm.toJson().toString()
            SimApiResponseWriter.write(context, jsonText)
        }
    }

    /// 结果派发 + 自动封装
    private func dispatchResult(actionResult: Any) {
        // @OriginResponse：跳过统一封装，原样输出
        var originResponse = false
        for (item in actionDescriptor.endpointMetadata) {
            if (item is OriginResponse) {
                originResponse = true
                break
            }
        }
        if (originResponse) {
            if (let s: String <- actionResult) {
                // String 原样输出文本
                context.response.contentType = "application/json; charset=utf-8"
                SimApiResponseWriter.write(context, s)
            } else {
                ObjectResult<Any>(actionResult).invoke(context)
            }
            return
        }
        // 统一封装（IActionResult/SimApiBaseResponse/String/Unit/其他对象）
        SimApiResultWriter.write(context, actionResult)
    }

    /// 根据 ModelState 错误构建 ValidationProblemDetails
    private func createValidationProblemDetails(modelBindingContext: ActionBindingContext) {
        let details = ValidationProblemDetails()
        if (hasUnsupportedContentTypeError(modelBindingContext.modelState)) {
            details.`type` = "https" + "://" + "tools.ietf.org" + "/html/rfc9110#section-15.5.16"
            details.title = "Unsupported Media Type"
            details.status = 415
        } else {
            details.`type` = "https" + "://" + "tools.ietf.org" + "/html/rfc9110#section-15.5.1"
            details.title = "One or more validation errors occurred."
            details.status = 400
            for ((name, entry) in modelBindingContext.modelState) {
                details.errors.add(name, entry.errors |> map {f => f.description} |> collectArray)
            }
        }
        return details
    }

    /// 检查 ModelState 中是否含有 UnsupportedContentTypeException 错误
    private func hasUnsupportedContentTypeError(modelState: ModelStateDictionary) {
        for ((_, entry) in modelState) {
            for (error in entry.errors) {
                if (error.exception.flatMap {f => f as UnsupportedContentTypeException}.isSome()) {
                    return true
                }
            }
        }
        return false
    }

    /// 通过 DI 容器实例化控制器，并注入当前 HttpContext
    /// （通过 BindRequestContext 接口而非 SimApiBaseController，避免 helpers↔controllers 循环依赖）
    private func createControllerInstance(): Object {
        let instance = ActivatorUtilities.createInstance(context.services, actionDescriptor.controllerType)
        if (let controller: BindRequestContext <- instance) {
            controller.bindRequestContext(context)
        }
        return instance
    }
}
