package com.ningdatech.carapi.common.util;

import cn.hutool.core.collection.CollUtil;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.carapi.car.datascreen.model.po.OnlineVehicleRegionsPO;
import com.ningdatech.carapi.common.model.MapDataVO;
import com.ningdatech.carapi.sys.contants.RegionContant;
import com.ningdatech.carapi.sys.entity.dto.RegionDTO;
import com.ningdatech.carapi.sys.helper.CompaniesCacheHelper;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @Classname CodeUtil
 * @Description
 * @Date 2022/11/22 16:21
 * @Created by PoffyZhang
 */
@RequiredArgsConstructor
@Slf4j
@Component
public class CodeUtil {

    @Autowired
    private RegionsCacheHelper regionsCacheHelper;
    @Autowired
    private CompaniesCacheHelper companiesCacheHelper;
    private static CodeUtil codeUtil;

    @PostConstruct
    public void init() {
        codeUtil = this;
        codeUtil.regionsCacheHelper = this.regionsCacheHelper;
        codeUtil.companiesCacheHelper = this.companiesCacheHelper;
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

    public static <T> void searchCompanyName(T obj) {
        try {
            Class<?> clazz = obj.getClass();
            Field companyNameField = clazz.getDeclaredField("companyName");
            Field companyIdField = clazz.getDeclaredField("companyId");
            companyNameField.setAccessible(Boolean.TRUE);
            companyIdField.setAccessible(Boolean.TRUE);
            Long companyId = Objects.nonNull(companyIdField.get(obj)) ? (Long)companyIdField.get(obj) : null;
            if (Objects.isNull(companyId)) {
                return;
            }
            String companyName = Objects.nonNull(companyNameField.get(obj)) ? (String)companyNameField.get(obj) : null;
            if (Objects.isNull(codeUtil)){
                log.error("codeUtil is null");
                return;
            }
            companyNameField.set(obj, codeUtil.companiesCacheHelper.getNameById(companyId, companyName));
        } catch (Exception e) {
            log.error("searchCompanyName err :",e);
        }
    }

    public static <T> void searchCompanyNames(List<T> records) {
        if (CollUtil.isEmpty(records)) {
            return;
        }
        for (Object obj : records) {
            searchCompanyName(obj);
        }
    }

    public static <T> void searchRegionName(T record) {
        try {
            Class<?> clazz = record.getClass();
            Field regionNameField = clazz.getDeclaredField("regionName");
            Field regionIdField = clazz.getDeclaredField("regionId");
            regionNameField.setAccessible(Boolean.TRUE);
            regionIdField.setAccessible(Boolean.TRUE);
            Long regionId = Objects.nonNull(regionIdField.get(record)) ? (Long)regionIdField.get(record) : null;
            if (Objects.isNull(regionId)) {
                return;
            }
            if (Objects.isNull(codeUtil)){
                log.error("codeUtil is null");
                return;
            }
            regionNameField.set(record, codeUtil.regionsCacheHelper.getDisplayName(regionId));
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    public static <T> void searchRegionNames(List<T> records) {
        if (CollUtil.isEmpty(records)) {
            return;
        }
        for (Object obj : records) {
            searchRegionName(obj);
        }
    }

    public static <T> void searchCompanyNameAndRegionName(T record) {
        searchCompanyName(record);
        searchRegionName(record);
    }

    public static <T> void searchCompanyNamesAndRegionNames(List<T> records) {
        if (CollUtil.isEmpty(records)) {
            return;
        }
        for (Object obj : records) {
            searchCompanyNameAndRegionName(obj);
        }
    }

    /**
     * 字符串是否是json类型
     * 
     * @param str
     * @return
     */
    public static boolean getJSONType(String str) {
        boolean result = false;
        if (StringUtils.isNotBlank(str)) {
            str = str.trim();
            if (str.startsWith("{") && str.endsWith("}")) {
                result = true;
            } else if (str.startsWith("[") && str.endsWith("]")) {
                result = true;
            }
        }
        return result;
    }

    /**
     * 获取 区域等级
     * 
     * @param record
     */
    public static <T> void buildRegionLevel(T record) {
        try {
            Class<?> clazz = record.getClass();
            Field regionLevelField = clazz.getDeclaredField("regionLevel");
            Field regionIdField = clazz.getDeclaredField("regionId");
            regionLevelField.setAccessible(Boolean.TRUE);
            regionIdField.setAccessible(Boolean.TRUE);
            Long regionId = Objects.nonNull(regionIdField.get(record)) ? (Long)regionIdField.get(record) : null;
            if (Objects.isNull(regionId)) {
                regionId = RegionContant.ZJ_REGION_ID;
            }
            if (Objects.isNull(codeUtil)){
                log.error("codeUtil is null");
                return;
            }
            RegionDTO regionDto = codeUtil.regionsCacheHelper.getByRegionId(regionId);
            if (Objects.nonNull(regionDto)) {
                regionLevelField.set(record, regionDto.getRegionLevel());
            }
        } catch (Exception e) {
            log.error("buildRegionLevel err :",e);
        }
    }

    /**
     * 根据月份 和type 来build 这一旬的起始结束日期
     * 
     * @param record
     * @param <T>
     */
    public static <T> void buildCurrentTendaysStartEnd(T record) {
        try {
            Class<?> clazz = record.getClass();
            Field monthField = clazz.getDeclaredField("month");
            Field typeField = clazz.getDeclaredField("type");
            monthField.setAccessible(Boolean.TRUE);
            typeField.setAccessible(Boolean.TRUE);
            String month = Objects.nonNull(monthField.get(record)) ? (String)monthField.get(record) : null;
            Integer type = Objects.nonNull(typeField.get(record)) ? (Integer)typeField.get(record) : null;
            if (StringUtils.isBlank(month) || Objects.isNull(type)) {
                return;
            }
            LocalDateTime date =
                LocalDateTime.parse(month + "-01 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            LocalDateTime startTime = date.with(TemporalAdjusters.firstDayOfMonth());
            LocalDateTime endTime = date.plusMonths(1).minusSeconds(1);
            // 获取当前月的第一天
            if (type.equals(1)) {
                endTime = date.plusDays(10).minusSeconds(1);
            } else if (type.equals(2)) {
                startTime = startTime.plusDays(10);
                endTime = date.plusDays(20).minusSeconds(1);
            } else if (type.equals(3)) {
                startTime = startTime.plusDays(20);
            }
            Field startTimeField = clazz.getDeclaredField("startTime");
            Field endTimeField = clazz.getDeclaredField("endTime");
            startTimeField.setAccessible(Boolean.TRUE);
            endTimeField.setAccessible(Boolean.TRUE);
            startTimeField.set(record, startTime);
            endTimeField.set(record, endTime);
        } catch (Exception e) {
            log.error("buildCurrentTendaysStartEnd err :",e);
        }
    }

    /**
     * 根据月份 和type 来build 上一旬的起始结束日期
     * 
     * @param record
     * @param <T>
     */
    public static <T> void buildLastTendaysStartEnd(T record) {
        try {
            Class<?> clazz = record.getClass();
            Field monthField = clazz.getDeclaredField("month");
            Field typeField = clazz.getDeclaredField("type");
            monthField.setAccessible(Boolean.TRUE);
            typeField.setAccessible(Boolean.TRUE);
            String month = Objects.nonNull(monthField.get(record)) ? (String)monthField.get(record) : null;
            Integer type = Objects.nonNull(typeField.get(record)) ? (Integer)typeField.get(record) : null;
            if (StringUtils.isBlank(month) || Objects.isNull(type)) {
                return;
            }
            LocalDateTime date =
                LocalDateTime.parse(month + "-01 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            // 上一旬 用type来判断 如果不是1 就减一
            if (!type.equals(1)) {
                type = type - 1;
            } else if (type.equals(1)) {
                // 否则就是上一个月的 下旬
                type = 3;
                date = date.minusMonths(1);
            }

            LocalDateTime startTime = date.with(TemporalAdjusters.firstDayOfMonth());
            LocalDateTime endTime = date.plusMonths(1).minusSeconds(1);
            // 获取当前月的第一天
            if (type.equals(1)) {
                endTime = date.plusDays(10).minusSeconds(1);
            } else if (type.equals(2)) {
                startTime = startTime.plusDays(10);
                endTime = date.plusDays(20).minusSeconds(1);
            } else if (type.equals(3)) {
                startTime = startTime.plusDays(20);
            }
            Field lastStartTimeField = clazz.getDeclaredField("lastStartTime");
            Field lastEndTimeField = clazz.getDeclaredField("lastEndTime");
            lastStartTimeField.setAccessible(Boolean.TRUE);
            lastEndTimeField.setAccessible(Boolean.TRUE);
            lastStartTimeField.set(record, startTime);
            lastEndTimeField.set(record, endTime);
        } catch (Exception e) {
            log.error("buildLastTendaysStartEnd err :",e);
        }
    }

    /**
     * Returns the 转换regionCode 成 id
     * 
     * @param record
     */
    public static <T> void convertRegionCodeToId(T record) {
        try {
            Class<?> clazz = record.getClass();
            Field regionIdField = clazz.getDeclaredField("regionId");
            regionIdField.setAccessible(Boolean.TRUE);
            String regionCode =
                Objects.nonNull(regionIdField.get(record)) ? String.valueOf(regionIdField.get(record)) : null;
            Long regionId = codeUtil.regionsCacheHelper.getIdByCode(regionCode);
            if (Objects.nonNull(regionId) && !regionId.equals(0L)) {
                // 能查得到 说明传入的code 直接转换为id
                regionIdField.set(record, regionId);
            }
        } catch (Exception e) {
            log.error("convertRegionCodeToId err :",e);
        }
    }

    public static Long convertRegionCodeToId(Long regionId) {
        try {
            String regionCode = String.valueOf(regionId);
            if (Objects.isNull(codeUtil)) {
                log.error("codeUtil is null");
                return null;
            }
            Long newRegionId = codeUtil.regionsCacheHelper.getIdByCode(regionCode);
            if (Objects.nonNull(newRegionId) && !newRegionId.equals(0L)) {
                // 能查得到 说明传入的code 直接转换为id
                return newRegionId;
            }
        } catch (Exception e) {
            log.error("convertRegionCodeToId err :",e);
        }
        return regionId;
    }

    /**
     * 求一个十进制数的 某个二进制位数上的值
     * 
     * @param number
     * @param index
     * @return
     */
    public static Integer getBinaryStringIndexValue(Long number, int index) {
        char[] chars = Long.toBinaryString(number).toCharArray();
        if (chars.length >= index + 1) {
            return Integer.valueOf(String.valueOf(chars[index]), 2);
        }
        return 0;
    }

    public static void complementaryRegion(List<MapDataVO> list, Long regionId) {
        Map<Long, MapDataVO> dataMap = CollUtils.listToMap(list, m -> m.getRegionId());
        if (Objects.isNull(codeUtil)){
            log.error("codeUtil is null");
            return;
        }
        List<RegionDTO> regions = codeUtil.regionsCacheHelper.listChildOnly(regionId);
        for (RegionDTO region : regions) {
            if (!dataMap.containsKey(region.getId())) {
                MapDataVO mapDataVo = new MapDataVO();
                mapDataVo.setRegionId(region.getId());
                mapDataVo.setRegionName(region.getRegionName());
                mapDataVo.setRegionCode(region.getRegionCode());
                mapDataVo.setValue(0);
                mapDataVo.setPercent("0%");
                list.add(mapDataVo);
            }
        }
    }

    public static void complementaryRegions(List<OnlineVehicleRegionsPO> list, Long regionId) {
        Map<Long, OnlineVehicleRegionsPO> dataMap = CollUtils.listToMap(list, m -> m.getRegionId());
        if (Objects.isNull(codeUtil)){
            log.error("codeUtil is null");
            return;
        }
        List<RegionDTO> regions = codeUtil.regionsCacheHelper.listChildOnly(regionId);
        for (RegionDTO region : regions) {
            if (!dataMap.containsKey(region.getId())) {
                OnlineVehicleRegionsPO onlineVehicleRegions = new OnlineVehicleRegionsPO();
                onlineVehicleRegions.setRegionId(region.getId());
                onlineVehicleRegions.setRegionName(region.getRegionName());
                onlineVehicleRegions.setRegionCode(region.getRegionCode());
                onlineVehicleRegions.setOnlineNum(0);
                onlineVehicleRegions.setVehicleNum(0);
                onlineVehicleRegions.setRate(BigDecimal.ZERO);
                list.add(onlineVehicleRegions);
            }
        }
    }

    // 生成验证码
    public static String codeFen(Integer length) {
        String str = "0123456789";
        // 将字符串转换为一个新的字符数组。
        char[] VerificationCodeArray = str.toCharArray();
        Random random = new Random();
        int count = 0;// 计数器
        StringBuilder stringBuilder = new StringBuilder();
        while (true) {
            // 随机生成一个随机数
            int index = random.nextInt(VerificationCodeArray.length);
            char c = VerificationCodeArray[index];
            // 限制四位不重复数字
            if (stringBuilder.indexOf(c + "") == -1) {
                stringBuilder.append(c);
                // 计数器加1
                count++;
            }
            // 当count等于4时结束，随机生成四位数的验证码
            if (count == length) {
                break;
            }
        }
        return stringBuilder.toString();
    }
}
