code

package site.daydream.colleen

import java.io.InputStream
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.full.findAnnotation

// ===== Exception Definitions =====

sealed class ExtractionError(message: String, cause: Throwable? = null) : RuntimeException(message, cause) {
    class MissingParameter(name: String, type: String) :
        ExtractionError("Required parameter '$name' of type $type not found")

    class InvalidType(type: String, reason: String) :
        ExtractionError("Invalid type $type: $reason")

    class ConversionFailed(value: String, from: String, to: String, cause: Throwable? = null) :
        ExtractionError("Cannot convert '$value' from $from to $to", cause)
}

// ===== Annotations =====

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Param(val value: String = "")

// ===== 核心设计:两阶段提取 =====

/**
 * 参数提取器基类
 * 只负责携带值,不负责提取逻辑
 */
abstract class ParamExtractor<T>(val value: T)

/**
 * 提取器工厂接口
 * 传递完整的参数元信息,提取器根据需要使用
 */
interface ExtractorFactory<T : ParamExtractor<*>> {
    /**
     * 构建提取函数
     * @param param 参数的完整元信息(包含名称、类型、注解等)
     */
    fun build(param: KParameter): (Context) -> T
}

// ===== Parameter Wrapper Types =====

class Path<T>(value: T) : ParamExtractor<T>(value) {
    companion object : ExtractorFactory<Path<*>> {
        override fun build(param: KParameter): (Context) -> Path<*> {
            val name = param.paramName()
            val type = param.innerType()
            
            require(name.isNotEmpty()) { "Path parameter requires @Param annotation" }
            require(!type.isMarkedNullable) { "Path parameter cannot be nullable" }

            val targetClass = type.toClass()

            return { ctx ->
                val value = ctx.param(name)
                    ?: throw ExtractionError.MissingParameter(name, "Path")
                Path(value.convertTo(targetClass, name))
            }
        }
    }
}

class Header(value: String) : ParamExtractor<String>(value) {
    companion object : ExtractorFactory<Header> {
        override fun build(param: KParameter): (Context) -> Header {
            val name = param.paramName()
            require(name.isNotEmpty()) { "Header parameter requires @Param annotation" }
            return { ctx -> Header(ctx.header(name) ?: "") }
        }
    }
}

class Cookie(value: String) : ParamExtractor<String>(value) {
    companion object : ExtractorFactory<Cookie> {
        override fun build(param: KParameter): (Context) -> Cookie {
            val name = param.paramName()
            require(name.isNotEmpty()) { "Cookie parameter requires @Param annotation" }
            return { ctx -> Cookie(ctx.cookie(name) ?: "") }
        }
    }
}

class Query<T>(value: T) : ParamExtractor<T>(value) {
    companion object : ExtractorFactory<Query<*>> {
        override fun build(param: KParameter): (Context) -> Query<*> {
            val name = param.paramName()
            val type = param.innerType()
            val targetClass = type.toClass()

            // Query<Map<String, List<String>>>
            if (targetClass.isMap()) {
                return { ctx -> Query(ctx.queries()) }
            }

            // Query<List<T>>
            if (targetClass.isList()) {
                val elementClass = type.listElementClass()
                return { ctx ->
                    val values = ctx.request.queryAll(name)
                    Query(values.map { it.convertTo(elementClass, name) })
                }
            }

            // Query<Int> 或 Query<Int?>
            if (targetClass.isSimple()) {
                return { ctx ->
                    val value = ctx.query(name)
                    when {
                        value != null -> Query(value.convertTo(targetClass, name))
                        type.isMarkedNullable -> Query(null)
                        else -> throw ExtractionError.MissingParameter(name, "Query")
                    }
                }
            }

            // Query<CustomDto>
            return { ctx ->
                try {
                    Query(ctx.queriesAs(targetClass)!!)
                } catch (e: Exception) {
                    throw ExtractionError.ConversionFailed("query data", "Query", targetClass.simpleName, e)
                }
            }
        }
    }
}

class Text(value: String) : ParamExtractor<String>(value) {
    companion object : ExtractorFactory<Text> {
        override fun build(param: KParameter): (Context) -> Text {
            // Text 不需要任何参数信息
            return { ctx -> Text(ctx.text() ?: "") }
        }
    }
}

class Json<T>(value: T) : ParamExtractor<T>(value) {
    companion object : ExtractorFactory<Json<*>> {
        override fun build(param: KParameter): (Context) -> Json<*> {
            val targetClass = param.innerType().toClass()
            
            return { ctx ->
                try {
                    Json(ctx.jsonAs(targetClass))
                } catch (e: Exception) {
                    throw ExtractionError.ConversionFailed("request body", "JSON", targetClass.simpleName, e)
                }
            }
        }
    }
}

class Form<T>(value: T) : ParamExtractor<T>(value) {
    companion object : ExtractorFactory<Form<*>> {
        override fun build(param: KParameter): (Context) -> Form<*> {
            val name = param.paramName()
            val type = param.innerType()
            val targetClass = type.toClass()

            if (targetClass.isMap()) {
                return { ctx -> Form(ctx.forms()) }
            }

            if (targetClass.isList()) {
                val elementClass = type.listElementClass()
                return { ctx ->
                    val values = ctx.request.formAll(name)
                    Form(values.map { it.convertTo(elementClass, name) })
                }
            }

            if (targetClass.isSimple()) {
                return { ctx ->
                    val value = ctx.form(name)
                    when {
                        value != null -> Form(value.convertTo(targetClass, name))
                        type.isMarkedNullable -> Form(null)
                        else -> throw ExtractionError.MissingParameter(name, "Form")
                    }
                }
            }

            return { ctx ->
                try {
                    Form(ctx.formsAs(targetClass)!!)
                } catch (e: Exception) {
                    throw ExtractionError.ConversionFailed("form data", "Form", targetClass.simpleName, e)
                }
            }
        }
    }
}

class Stream(value: InputStream) : ParamExtractor<InputStream>(value) {
    companion object : ExtractorFactory<Stream> {
        override fun build(param: KParameter): (Context) -> Stream {
            // Stream 不需要任何参数信息
            return { ctx ->
                Stream(ctx.request.stream ?: throw ExtractionError.MissingParameter("stream", "InputStream"))
            }
        }
    }
}

class UploadedFile(value: Request.UploadedFile) : ParamExtractor<Request.UploadedFile>(value) {
    companion object : ExtractorFactory<UploadedFile> {
        override fun build(param: KParameter): (Context) -> UploadedFile {
            val name = param.paramName().ifEmpty { "file" }
            
            return { ctx ->
                val file = ctx.file(name)
                    ?: throw ExtractionError.MissingParameter(name, "UploadedFile")
                UploadedFile(file)
            }
        }
    }
}

// ===== cx Function Implementation =====

fun cx(fn: KFunction<*>, instance: Any? = null): Handler {
    val instanceParam = fn.parameters.firstOrNull { it.kind == KParameter.Kind.INSTANCE }
    val valueParams = fn.parameters.filter { it.kind == KParameter.Kind.VALUE }
    
    // 构建阶段:预先生成所有提取函数
    val extractors = valueParams.map { param -> buildExtractor(param) }

    return Handler { ctx ->
        val args = buildMap {
            if (instanceParam != null && instance != null) {
                put(instanceParam, instance)
            }

            // 执行阶段:只需调用提取函数
            valueParams.forEachIndexed { index, param ->
                put(param, extractors[index](ctx))
            }
        }

        fn.callBy(args)
    }
}

// ===== Core Extractor Builder =====

private fun buildExtractor(param: KParameter): (Context) -> Any? {
    val kType = param.type
    val wrapperClass = kType.classifier as? kotlin.reflect.KClass<*>
        ?: throw ExtractionError.InvalidType(kType.toString(), "Cannot resolve classifier")

    // Context 直接传递
    if (wrapperClass == Context::class) {
        return { it }
    }

    // 检查是否是 ParamExtractor
    if (ParamExtractor::class.java.isAssignableFrom(wrapperClass.java)) {
        val factory = getExtractorFactory(wrapperClass)
        
        // 直接传递 KParameter,让提取器自己决定需要什么信息
        return factory.build(param)
    }

    // 回退到服务注入
    return { ctx -> ctx.getService(wrapperClass.java) }
}

private fun getExtractorFactory(wrapperClass: kotlin.reflect.KClass<*>): ExtractorFactory<*> {
    return try {
        // Kotlin: companion object
        wrapperClass.companionObjectInstance as? ExtractorFactory<*>
    } catch (e: Exception) {
        null
    } ?: try {
        // Java: FACTORY 静态字段
        wrapperClass.java.getField("FACTORY").get(null) as? ExtractorFactory<*>
    } catch (e: Exception) {
        null
    } ?: throw ExtractionError.InvalidType(
        wrapperClass.simpleName ?: "Unknown",
        "ParamExtractor must have a companion object (Kotlin) or FACTORY field (Java) implementing ExtractorFactory"
    )
}

// ===== Helper Methods =====

// KParameter 扩展方法
private fun KParameter.paramName(): String {
    return this.findAnnotation<Param>()?.value?.takeIf { it.isNotEmpty() }
        ?: this.name
        ?: ""
}

private fun KParameter.innerType(): KType {
    return this.type.arguments.firstOrNull()?.type
        ?: throw ExtractionError.InvalidType(this.type.toString(), "Missing generic type parameter")
}

// KType 扩展方法
private fun KType.toClass(): Class<*> {
    return (classifier as? kotlin.reflect.KClass<*>)?.java
        ?: throw ExtractionError.InvalidType(toString(), "Cannot resolve class")
}

private fun KType.listElementClass(): Class<*> {
    val elementType = arguments.firstOrNull()?.type
        ?: throw ExtractionError.InvalidType(toString(), "List missing element type")
    return elementType.toClass()
}

// String 类型转换
internal fun String.convertTo(targetClass: Class<*>, paramName: String): Any {
    return try {
        when (targetClass) {
            String::class.java -> this
            Int::class.java, Integer::class.java ->
                toIntOrNull() ?: throw ExtractionError.ConversionFailed(this, "String", "Int")
            Long::class.java, java.lang.Long::class.java ->
                toLongOrNull() ?: throw ExtractionError.ConversionFailed(this, "String", "Long")
            Double::class.java, java.lang.Double::class.java ->
                toDoubleOrNull() ?: throw ExtractionError.ConversionFailed(this, "String", "Double")
            Float::class.java, java.lang.Float::class.java ->
                toFloatOrNull() ?: throw ExtractionError.ConversionFailed(this, "String", "Float")
            Boolean::class.java, java.lang.Boolean::class.java ->
                toLenientBoolean()
            else ->
                throw ExtractionError.InvalidType(targetClass.simpleName, "Unsupported conversion type")
        }
    } catch (e: ExtractionError) {
        throw e
    } catch (e: Exception) {
        throw ExtractionError.ConversionFailed(this, "String", targetClass.simpleName, e)
    }
}

internal fun String.toLenientBoolean(): Boolean {
    return lowercase() in setOf("true", "1", "yes", "y")
}

internal fun Class<*>.isSimple() = this in SIMPLE_TYPES
internal fun Class<*>.isMap() = Map::class.java.isAssignableFrom(this)
internal fun Class<*>.isList() = List::class.java.isAssignableFrom(this)

internal val SIMPLE_TYPES = setOf(
    String::class.java,
    Int::class.java, Integer::class.java,
    Long::class.java, java.lang.Long::class.java,
    Double::class.java, java.lang.Double::class.java,
    Float::class.java, java.lang.Float::class.java,
    Boolean::class.java, java.lang.Boolean::class.java
)