package cn.ey.biz.module.system.service.category.dynamic.provider;


import cn.ey.biz.common.pojo.PageParam;
import cn.ey.biz.common.pojo.PageResult;
import cn.ey.biz.framework.mybatis.core.util.MyBatisUtils;
import cn.ey.biz.module.system.api.category.dto.CategoryDynamicReq;
import cn.ey.biz.module.system.dal.dataobject.category.CategoryDO;
import cn.ey.biz.module.system.dal.mapper.category.CategoryMapper;
import cn.ey.biz.module.system.dal.redis.RedisKeyConstants;
import cn.ey.biz.module.system.enums.StatusEnum;
import cn.ey.biz.module.system.enums.YesOrNoStatusEnum;
import cn.ey.biz.module.system.enums.category.CategoryCommonConstants;
import cn.ey.biz.module.system.service.category.dynamic.service.IBaseService;
import cn.ey.biz.module.system.service.category.v2.CategoryV2Service;
import cn.ey.biz.module.system.util.MapKeyConvert;
import cn.ey.biz.module.system.util.SQLFilterUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class CategoryRequestProvider {

    private final Map<String, IBaseService> categoryServices;
    private final CategoryV2Service categoryService;
    private final CategoryMapper categoryMapper;


    @Cacheable(value = RedisKeyConstants.DICT_DATA_CACHE_NAME_CATEGORY_DATA, key = "#root.methodName+'-'+#req", unless = "#result==null or #result.isEmpty()")
    public Object fixedDictPro(CategoryDynamicReq req) {
        return fixedDictProPageable(req).getList();
    }

    public Object fixedDictProNonCache(CategoryDynamicReq req) {
        return fixedDictProPageable(req).getList();
    }

    public PageResult<Map<String, Object>> fixedDictProPageable(CategoryDynamicReq req) {
        CategoryDO categoryDO = categoryService.getCategoryCacheable(req.getCategoryCode());
        String tableName = categoryDO.getStoreTableName();
        Integer isVersionControl = categoryDO.getIsVersionControl();
        Map<String, Object> requestBody = req.getRequestBody();
        Map<String, Object> queryParams = new HashMap<>(16);
        AtomicBoolean nonAllStatus = new AtomicBoolean(true);
        this.checkSqlInject(requestBody);
        if (CollectionUtil.isNotEmpty(requestBody)) {
            requestBody.computeIfPresent(CategoryCommonConstants.STATUS, (key, value) -> StatusEnum.getByName(String.valueOf(value)).getCode());
            for (Map.Entry<String, Object> entry : requestBody.entrySet()) {
                if (CategoryCommonConstants.STATUS.equals(entry.getKey()) && entry.getValue() == StatusEnum.ALL.getCode()) {
                    nonAllStatus.set(false);
                    continue;
                }
                queryParams.put(MapKeyConvert.humpToUnderline(entry.getKey()), entry.getValue());
            }
        }
        if (nonAllStatus.get()) {
            queryParams.computeIfAbsent(CategoryCommonConstants.STATUS, key -> StatusEnum.ACTIVE.getCode());
        }
        PageParam pageParam = req.getPageParam();
        if (req.getPageParam() == null) {
            pageParam = new PageParam();
            pageParam.setPageSize(-1);
        }
        IPage page = MyBatisUtils.buildPage(pageParam);
        IPage<Map<String, Object>> result = categoryMapper.selectPageCategoryDataDynamic(page, tableName, isVersionControl, queryParams);
        convertDate(result.getRecords());
        return new PageResult<>(result.getRecords(), result.getTotal());
    }

    @Cacheable(value = RedisKeyConstants.DICT_DATA_CACHE_NAME_CATEGORY_DATA, key = "#root.methodName+'-'+#req", unless = "#result==null or #result.isEmpty()")
    public Object strategyDictPro(HttpServletRequest request, HttpServletResponse response, CategoryDynamicReq req) throws Exception {
        IBaseService iBaseService = categoryServices.get(req.getCategoryCode() + IBaseService.BASE_SERVICE_NAME);
        Class<? extends IBaseService> clazz = iBaseService.getClass();
        Method declaredMethod = clazz.getDeclaredMethod("get" + req.getFunctionType(), HttpServletRequest.class, HttpServletResponse.class, CategoryDynamicReq.class);
        Object result = declaredMethod.invoke(iBaseService, request, response, req);
        this.convertDate(result);
        return result;
    }

    @SuppressWarnings("unchecked")
    private void convertDate(Object result) {
        if (result != null) {
            if (result instanceof List) {
                List<Map<String, Object>> mapList = null;
                try {
                    mapList = (List<Map<String, Object>>) result;
                } catch (Exception ignored) {
                    return;
                }
                if (CollectionUtil.isEmpty(mapList) || !(mapList.get(0) instanceof Map)) {
                    return;
                }
                for (Map<String, Object> map : mapList) {
                    this.convertMapDate(map, formatFields());
                }
            } else if (result instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) result;
                this.convertMapDate(map, formatFields());
            }
        }
    }

    private void convertMapDate(Map<String, Object> map, String... formatFields) {
        if (CollUtil.isNotEmpty(Arrays.asList(formatFields))) {
            for (String formatField : formatFields) {
                map.computeIfPresent(formatField, (key, value) ->
                        DateUtil.format((LocalDateTime) value, "yyyy-MM-dd HH:mm:ss"));
            }
        }
    }

    public static String[] formatFields() {
        String[] formatFields = new String[]{"day_date", "begin_date", "start_time", "end_time", "effective_date", "effective_start_date", "effective_end_date", "update_time", "create_time"};
        String[] humpFields = Arrays.stream(formatFields.clone()).map(MapKeyConvert::underlineToHump).toArray(String[]::new);
        return ArrayUtils.addAll(formatFields, humpFields);
    }

    public List<String> canJoinCategoryCodeList() {
        return categoryService.getAllCategoryCodeList().stream().filter(entity -> entity.getStoreType() != null && YesOrNoStatusEnum.YES.getCode() == entity.getStoreType())
                .map(CategoryDO::getCode).collect(Collectors.toList());
    }

    private void checkSqlInject(Map<String, Object> params) {
        if (params != null && !params.isEmpty()) {
            for (Object value : params.values()) {
                SQLFilterUtil.sqlInject(String.valueOf(value));
            }
        }
    }
}
