package quant.rich.emoney.controller;

import java.lang.reflect.Method;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import jakarta.validation.UnexpectedTypeException;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.controller.common.BaseController;
import quant.rich.emoney.exception.PageNotFoundException;
import quant.rich.emoney.interfaces.ConfigInfo;
import quant.rich.emoney.interfaces.IConfig;
import quant.rich.emoney.pojo.dto.R;
import quant.rich.emoney.service.ConfigService;

@Controller
@RequestMapping("/admin/v1/config")
@Slf4j
public class ConfigControllerV1 extends BaseController {
    
    @Autowired
    Validator validator;

    @Autowired
    ConfigService configService;

    @GetMapping({ "/{configField}", "/{configField}/", "/{configField}/index" })
    public String configPage(@PathVariable String configField) {
        ConfigInfo info = configService.getConfigInfoByField(configField);
        if (info == null || !info.managed()) {
            throw new PageNotFoundException();
        }
        request.setAttribute("title", info.name());
        return "admin/v1/config/" + configField + "/index";

    }
    
    /**
     * 从文件重载
     * @param configField
     * @return
     */
    @SuppressWarnings("unchecked")
    @GetMapping("/reload/{configField}")
    @ResponseBody
    public <T extends IConfig<T>> R<?> reload(@PathVariable String configField) {
        return R.judge(() -> 
                configService.saveOrUpdate((T)configService.getOrCreateConfig(configField))
        );
    }

    /**
     * 保存配置项统一接口
     * @throws Exception 
     */
    @PostMapping({ "/{configField}", "/{configField}/", "/{configField}/index" })
    @ResponseBody
    public <T extends IConfig<T>> R<?> saveConfig(@PathVariable String configField, @Validated @RequestBody JsonNode config) throws Exception {

        Class<T> clazz = configService.getConfigClassByField(configField);
        if (clazz == null) {
            throw new PageNotFoundException();
        }
        
        return R.judgeThrow(() -> {
            T newConfig = (T)new ObjectMapper().treeToValue(config, clazz);
            
            Method method = this.getClass().getMethod("saveConfig", String.class, JsonNode.class);
            MethodParameter methodParameter = new MethodParameter(method, 1);

            // 执行校验
            if (validator.supports(clazz)) {
                try {
                    BindingResult bindingResult = new BeanPropertyBindingResult(newConfig, clazz.getSimpleName());
                    validator.validate(newConfig, bindingResult);
    
                    if (bindingResult.hasErrors()) {
                        throw new MethodArgumentNotValidException(methodParameter, bindingResult);
                    }
                }
                catch (UnexpectedTypeException e) {
                    // 对指定类型未找到 validator，忽略之
                    log.debug("Cannot find a validator for {}", clazz.getName());
                }
            }
            
            T oldConfig = configService.getConfig(clazz);
            oldConfig.mergeFrom(newConfig);
            return oldConfig.saveOrUpdate();
        });
    }

}
