package com.njzscloud.common.http.resolver;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.utils.Mime;
import com.njzscloud.common.http.annotation.FormBodyParam;
import com.njzscloud.common.http.constant.HttpMethod;
import com.njzscloud.common.http.processor.FormBodyParamProcessor;

import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class FormBodyParamResolver extends ParamResolver<FormBodyParam, Tuple2<String, byte[]>> {
    Map<String, List<String>> result = new HashMap<>();

    public FormBodyParamResolver() {
        super(FormBodyParam.class);
    }

    @Override
    public Tuple2<String, byte[]> resolve(HttpMethod httpMethod, String urlTpl) {
        if (result.isEmpty()) return null;
        StringJoiner joiner = new StringJoiner("&");
        result.forEach((k, v) -> v.forEach(it -> joiner.add(k + "=" + it)));
        byte[] bytes = joiner.toString().getBytes(StandardCharsets.UTF_8);
        return Tuple2.of(Mime.FORM, bytes);
    }

    @Override
    @SuppressWarnings("unchecked")
    protected boolean resolveParameter(FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
        String value = anno.value();
        if (isKv(paramClazz)) resolveKv(paramValue);
        else if (isArr(paramClazz)) resolveArr(StrUtil.isBlank(value) ? paramName : value, paramValue);
        else if (isStr(paramClazz) || isNum(paramClazz) || isDt(paramClazz)) resolveFormable(anno, paramName, paramClazz, paramValue);
        else return false;
        return true;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected boolean resolveField(boolean hasParameterAnno, FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
        if (anno != null) {
            Class<? extends FormBodyParamProcessor> processorClazz = anno.processor();
            if (processorClazz != FormBodyParamProcessor.class) {
                ReflectUtil.newInstance(processorClazz).process(anno, paramName, paramClazz, paramValue, result);
            } else if (!hasParameterAnno && isKv(paramClazz)) {
                resolveKv(paramValue);
            }
        }
        if (anno != null) {
            String value = anno.value();
            if (StrUtil.isNotBlank(value)) paramName = value;
        }
        if (isArr(paramClazz)) resolveArr(paramName, paramValue);
        else resolveFormable(anno, paramName, paramClazz, paramValue);
        return true;
    }


    protected void resolveKv(Object paramValue) {
        ((Map<?, ?>) paramValue).forEach((k, v) -> {
            if (v == null) return;
            Class<?> clazz = v.getClass();
            String paramName = k.toString();
            if (isArr(clazz)) resolveArr(paramName, v);
            else resolveFormable(null, paramName, clazz, v);
        });
    }


    protected void resolveFormable(FormBodyParam anno, String paramName, Class<?> paramClazz, Object paramValue) {
        String key = paramName;
        String val;

        String format = null;
        RoundingMode roundingMode = null;
        if (anno != null) {
            format = anno.format();
            roundingMode = anno.roundingMode();
            String value = anno.value();
            if (StrUtil.isNotBlank(value)) key = value;
        }

        if (isNum(paramClazz)) {
            val = formatNum(format, roundingMode, paramValue);
        } else if (isDt(paramClazz)) {
            val = formatDt(format, paramValue);
        } else {
            val = paramValue.toString();
        }
        result.computeIfAbsent(key, it -> new ArrayList<>()).add(val);
    }

}
