package com.storlead.sales.util;

import cn.hutool.core.util.StrUtil;
import com.storlead.framework.common.result.Result;
import com.storlead.framework.mybatis.entity.SysBaseField;
import com.storlead.sales.pojo.vo.customer.SimilarityCheckVo;
import com.storlead.sales.pojo.vo.customer.SimilarityErrorVO;
import com.storlead.sales.pojo.vo.systemrule.FieldSimilarityConfigVO;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @program: sp-sales-platform
 * @description:
 * @author: chenkq
 * @create: 2024-06-24 15:57
 */
@Log4j2
public class SystemRuleModuleUtils {

    /**
     * 字段相似度校验
     * @param target 比较目标
     * @param sources 比较数据源
     * @param rules 相似度规则
     * @return
     * @throws IllegalAccessException
     */

    public static <T extends SysBaseField>  Result<SimilarityCheckVo> checkFieldSimilarity(Object target, List<T> sources, List<FieldSimilarityConfigVO> rules,String resourceType){

        Result result = new Result();
        try {
            List<Object> similarityDatas = new ArrayList<>();
            List<SimilarityErrorVO> errorDatas =  new ArrayList<>();


            Boolean canSave = true;
            if (CollectionUtils.isEmpty(rules)) {
                return Result.result( new SimilarityCheckVo());
            }
            if (CollectionUtils.isEmpty(sources)) {
                return Result.result( new SimilarityCheckVo());
            }

            for (T source : sources) {
                for(FieldSimilarityConfigVO rule : rules) {
                    Field targetField = FieldUtils.getField(target.getClass(), rule.getFieldCode(), true);
//                    if (Objects.isNull(targetField)) {
//                        SimilarityErrorVO errorData = new SimilarityErrorVO();
//                        errorData.setCanSave(rule.getCanSave());
//                        errorData.setErrorMsg("字段" + rule.getFieldName() + "不存在");
//                        errorData.setObject(source);
//                        errorDatas.add(errorData);
//                    }
                    Field sourceField = FieldUtils.getField(source.getClass(), rule.getFieldCode(), true);
//                    if (Objects.isNull(sourceField)) {
//                        SimilarityErrorVO errorData = new SimilarityErrorVO();
//                        errorData.setCanSave(rule.getCanSave());
//                        errorData.setErrorMsg("字段" + rule.getFieldName() + "不存在");
//                        errorData.setObject(source);
//                        errorDatas.add(errorData);
//                    }
                    if (Objects.isNull(sourceField) || Objects.isNull(targetField)) {
                        continue;
                    }
                    if(ObjectUtils.isEmpty(FieldUtils.readField(targetField, target)) || ObjectUtils.isEmpty(FieldUtils.readField(sourceField, source))){
                        continue;
                    }

                    String targetValue = String.valueOf(FieldUtils.readField(targetField, target));
                    String sourceValue = String.valueOf(FieldUtils.readField(sourceField, source));
                    if (ObjectUtils.isEmpty(targetValue) || ObjectUtils.isEmpty(sourceValue)){
                        continue;
                    }
                        BigDecimal similarity = computeSequentialSimilarity(targetValue, sourceValue);
                        if (similarity.compareTo(rule.similarityValue) >= 0) {
                            if (!rule.canSave) {
                                canSave = false;
                            }
                            similarityDatas.add(source);
                            SimilarityErrorVO errorData = new SimilarityErrorVO();
    //                        try {
    //                            String fieldValue = String.valueOf(FieldUtils.readField(target,"id", true));
    //                        }catch (IllegalAccessException e){
    //                            log.error("获取id失败",e);
    //                        }
                            errorData.setCanSave(rule.getCanSave());
    //                        errorData.setErrorMsg("字段" + rule.getFieldName() + "相似度" + similarity.setScale(2, RoundingMode.HALF_UP)+"大于设定值"+rule.similarityValue);
                            errorData.setErrorMsg(rule.getFieldName());
                            errorData.setObject(source);
                            //添加source的id到errorId
                            Field idField = source.getClass().getDeclaredField("id");
                            idField.setAccessible(true); // 如果id字段是私有的，需要设置可访问
                            Object id = idField.get(source);
                            errorData.setErrorId((Long) id);

                            errorData.setResourceType(resourceType);
                            errorDatas.add(errorData);
    //                    return "字段" + rule.getFieldName() + "相似度" + similarity.setScale(2, RoundingMode.HALF_UP)+"大于设定值"+rule.similarityValue;
                        }


                }
            }
            SimilarityCheckVo vo = new SimilarityCheckVo();
            vo.setSimilarityErrors(errorDatas);
            vo.setSourcesData(similarityDatas);
            result.setResult(vo);
            result.setSuccess(canSave);
            return result;
        }catch (Exception e) {
            log.error("相似度校验失败",e);

            return Result.error( new SimilarityCheckVo(), "相似度校验失败");

        }
    }

    public static BigDecimal jaccardSimilarity(String str1, String str2) {
        // 转换为字符集合
        Set<Character> set1 = new HashSet<>();
        for (char c : str1.toCharArray()) {
            set1.add(c);
        }

        Set<Character> set2 = new HashSet<>();
        for (char c : str2.toCharArray()) {
            set2.add(c);
        }

        // 找出两个集合的交集和并集
        Set<Character> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);

        Set<Character> union = new HashSet<>(set1);
        union.addAll(set2);

        // 计算Jaccard相似度
        if (union.isEmpty()) return BigDecimal.ZERO;
        double similarity = (double) intersection.size() / union.size();
//        System.out.println("Similarity: " + similarity);
        return  new BigDecimal(String.valueOf(similarity)).setScale(2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100l));

    }

    public static BigDecimal computeSequentialSimilarity(String str1Old, String str2Old) {
        if (StrUtil.isBlank(str1Old) || StrUtil.isBlank(str2Old) ) {
            return BigDecimal.ZERO;
        }
        int length1 = str1Old.length();
        int length2 = str2Old.length();
        int maxLength = Math.max(length1, length2);

        String str1 = str1Old.toLowerCase();// 统一转为小写
        String str2 = str2Old.toLowerCase();// 统一转为小写

        // 初始化匹配计数
        int matchCount = 0;
        double positionPenalty = 0.0;

        // 遍历两个字符串，逐字符比较
        for (int i = 0; i < Math.min(length1, length2); i++) {
            char char1 = str1.charAt(i);
            char char2 = str2.charAt(i);
            if (char1 == char2) {
                matchCount++;  // 完全匹配
            } else {
                // 检查是否在字符串的其他地方出现
                int posInStr2 = str2.indexOf(char1);
                if (posInStr2 != -1) {
                    matchCount++;  // 字符匹配
                    positionPenalty += Math.abs(i - posInStr2);  // 计算位置偏差惩罚
                }
            }
        }

        // 计算相似度得分，考虑位置惩罚
        double similarityScore = (double) matchCount / maxLength;
        double penaltyFactor = positionPenalty / (double) maxLength;

        // 归一化相似度得分（惩罚后），保证在0到1之间
        double similarity = Math.max(0, similarityScore - penaltyFactor);
        String result = String.format("%.4f", similarity);
        return  new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100l));

    }
    public static void main(String[] args) {
        String str1 = "321";
        String str2 = "123";

        BigDecimal similarity = computeSequentialSimilarity(str1, str2);
        System.out.println("Similarity: " + similarity);
    }
}
