package com.yc.ship.module.trade.service.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.util.cache.CacheUtils;
import com.yc.ship.framework.common.util.date.DateUtils;
import com.yc.ship.framework.common.util.object.ObjectUtils;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.product.enums.SpecTypeEnum;
import com.yc.ship.module.system.api.dict.DictDataApi;
import com.yc.ship.module.system.api.dict.dto.DictDataRespDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderCreateReqDTO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeDetailDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeVisitorDO;
import com.yc.ship.module.trade.enums.CredentialTypeEnum;
import org.apache.commons.lang3.StringUtils;

import java.time.Duration;
import java.util.Date;
import java.util.List;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MINUTE_FORMAT;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.YES;

/**
 * &#064;Description ：交易规则验证
 * &#064;Author ：qsl
 * &#064;Date ：2025/8/30 16:58
 */
public class TradeOrderRuleValidate {


    public static void checkSaleRule(TradeOrderCreateReqDTO.OrderItem orderItem, ProductSpuRespDTO productSpuRespDTO){
        //1.销售开始时间、结束时间
        Date now = new Date();
        Date today = DateUtil.beginOfDay(now);
        if(productSpuRespDTO.getProductBasicRule().getSaleStartDate()!=null){
            Date startDate = DateUtils.of(productSpuRespDTO.getProductBasicRule().getSaleStartDate().atStartOfDay());
            Asserts.isTrue(today.compareTo(startDate)>=0,"当前时间小于销售开始日期{}",DateUtil.formatDateTime(startDate));
        }
        if(StrUtil.isNotBlank(productSpuRespDTO.getProductBasicRule().getSaleStartTime())){
            String saleStartDay = DateUtil.today();
            if(productSpuRespDTO.getProductBasicRule().getSaleStartDate()!=null){
                //指定日期的时间
                saleStartDay = DateUtil.formatDate(DateUtils.of(productSpuRespDTO.getProductBasicRule().getSaleStartDate().atStartOfDay()));
            }
            String saleStartTime = saleStartDay+" "+productSpuRespDTO.getProductBasicRule().getSaleStartTime();
            Asserts.isTrue(now.compareTo(DateUtil.parse(saleStartTime,NORM_DATETIME_MINUTE_FORMAT))>=0,"当前时间小于销售开始时间{}",saleStartTime);
        }

        if(productSpuRespDTO.getProductBasicRule().getSaleEndDate()!=null){
            Date endDate = DateUtils.of(productSpuRespDTO.getProductBasicRule().getSaleEndDate().atStartOfDay());
            Asserts.isTrue(today.compareTo(endDate)<=0,"当前时间大于销售结束日期{}",DateUtil.formatDateTime(endDate));
        }
        if(StrUtil.isNotBlank(productSpuRespDTO.getProductBasicRule().getSaleEndTime())){
            String saleEndDay = DateUtil.today();
            if(productSpuRespDTO.getProductBasicRule().getSaleEndDate()!=null){
                //指定日期的时间
                saleEndDay = DateUtil.formatDate(DateUtils.of(productSpuRespDTO.getProductBasicRule().getSaleEndDate().atStartOfDay()));
            }
            String saleEndTime = saleEndDay+" "+productSpuRespDTO.getProductBasicRule().getSaleEndTime();
            Asserts.isTrue(now.compareTo(DateUtil.parse(saleEndTime,NORM_DATETIME_MINUTE_FORMAT))<=0,"当前时间大于销售结束时间{}",saleEndTime);
        }

        //2.提前预定 指预约的游玩日期与当前下单时间的最小相距天数
        if((productSpuRespDTO.getProductSaleRule().getPrebuyLimitDay()!=null || productSpuRespDTO.getProductSaleRule().getPrebuyLimitHour()!=null) && orderItem.getUseDate()!=null){
            int dayhours = productSpuRespDTO.getProductSaleRule().getPrebuyLimitDay()!=null?productSpuRespDTO.getProductSaleRule().getPrebuyLimitDay()*24:0;
            int hours = productSpuRespDTO.getProductSaleRule().getPrebuyLimitHour()!=null?productSpuRespDTO.getProductSaleRule().getPrebuyLimitHour():0;
            if(dayhours+hours!=0){
                Date minDate = DateUtil.offsetHour(orderItem.getUseDate(),-(dayhours+hours));
                Asserts.isTrue(now.compareTo(minDate)<=0,"当前时间大于提前预约日期{}",DateUtil.formatDateTime(minDate));
            }
        }

        //3.一票多人 最少1人，此处不验证，后面组装游客数据的时候验证了不能超过最大数量
        //4.订单下单票数,最少数量和最大数量
        int detailCounts = orderItem.getDetailList().stream().map(TradeOrderCreateReqDTO.OrderDetail::getCount).reduce(0, Integer::sum);
        if(productSpuRespDTO.getProductSaleRule().getMinNum()!=null){
            Asserts.isTrue(detailCounts>=productSpuRespDTO.getProductSaleRule().getMinNum(),"当前购买数量小于最小下单数量{}",productSpuRespDTO.getProductSaleRule().getMinNum());
        }
        if(productSpuRespDTO.getProductSaleRule().getMaxNum()!=null){
            Asserts.isTrue(detailCounts<=productSpuRespDTO.getProductSaleRule().getMaxNum(),"当前购买数量大于最大下单数量{}",productSpuRespDTO.getProductSaleRule().getMaxNum());
        }
        //5.实名制，是否需要游客姓名,是否需要游客手机号,性别限制,允许购买区域,限制购买区域,年龄限制,此处不验证，在构建游客信息时验证
        //6.目前规格内的产品只能是同规格或者通用
        orderItem.getDetailList().forEach(detail->{
            detail.getDetailBaseList().forEach(detailBase->{
                Asserts.isTrue(ObjectUtils.equalsAny(detailBase.getSpecType(),detail.getSpecType(), SpecTypeEnum.COMMON.getType()),"规格{}不能包含{}规格的基础产品,{}",detail.getSpecType(),detailBase.getSpecType(),detailBase.getProductBaseId());
            });
        });
    }

    public static void checkVisitor(TradeVisitorDO tradeVisitorDO,ProductSpuRespDTO productSpuRespDTO, TradeOrderCreateReqDTO.Visitor visitor, TradeDetailDO tradeDetailDO){
        //特殊处理，规格为other不验证游客信息
        if(ObjectUtil.equal(tradeDetailDO.getSpecType(),SpecTypeEnum.OTHER.getType())){
            return;
        }
        //1.验证实名方式
        if(productSpuRespDTO.getProductSaleRule().getIsLimitType() == YES){

            if(productSpuRespDTO.getProductSaleRule().getLimitCertificate()!=null){
                Asserts.isTrue(productSpuRespDTO.getProductSaleRule().getLimitCertificate().contains(String.valueOf(tradeVisitorDO.getCredentialType())),"当前证件类型未在实名制方式内");
                //只验证身份证格式是否正确
                if(ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType())){
                    Asserts.isTrue( IdcardUtil.isValidCard(tradeVisitorDO.getCredentialNo()),"身份证格式不正确{}",tradeVisitorDO.getCredentialNo());
                    tradeVisitorDO.setGender(IdcardUtil.getGenderByIdCard(tradeVisitorDO.getCredentialNo()));
                }
            }

            //2.是否需要游客姓名
            if(ObjectUtil.equal(productSpuRespDTO.getProductSaleRule().getNeedVisitorName(),YES)){
                Asserts.isTrue( StrUtil.isNotBlank(tradeVisitorDO.getName()),"此产品要求游客姓名不能为空");
            }
            //3.是否需要游客手机号
            if(ObjectUtil.equal(productSpuRespDTO.getProductSaleRule().getNeedVisitorPhone(),YES)){
                Asserts.isTrue( StrUtil.isNotBlank(tradeVisitorDO.getMobile()),"此产品要求游客手机号不能为空");
            }
        }else{
            //设置性别
            if(tradeVisitorDO.getCredentialNo()!=null && StringUtils.isNotBlank(tradeVisitorDO.getCredentialNo())){
                //只验证身份证格式是否正确
                if(ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType())){
                    tradeVisitorDO.setGender(IdcardUtil.getGenderByIdCard(tradeVisitorDO.getCredentialNo()));
                }
            }
        }
        //如果传了游客证件，则证件号不能为空
        if(StrUtil.isNotBlank(tradeVisitorDO.getCredentialNo())){
            Asserts.isTrue(tradeVisitorDO.getCredentialType()!=null,"有证件信息，但无证件类型，请检查");
        }

        //4.性别限制
        if(StrUtil.isNotBlank(productSpuRespDTO.getProductSaleRule().getLimitSex()) && !"-1".equals(productSpuRespDTO.getProductSaleRule().getLimitSex())){
            Asserts.isTrue( StrUtil.contains(productSpuRespDTO.getProductSaleRule().getLimitSex(),String.valueOf(tradeVisitorDO.getGender())),"游客不符合产品性别要求");
        }
        //5.允许购买区域
        if(productSpuRespDTO.getProductSaleRule().getAllowArea()!=null){
            Asserts.isTrue(ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType()),"当前产品限制了购买区域，只能通过身份证购买");
            //身份证类型才验证
            String districtCode = IdcardUtil.getDistrictCodeByIdCard(tradeVisitorDO.getCredentialNo());
            String allowArea = StrUtil.split(productSpuRespDTO.getProductSaleRule().getAllowArea(),",").stream().filter(districtCode::startsWith).findFirst().orElse(null);
            Asserts.isTrue(allowArea != null,"当前区域的身份证不能购买此产品{}",allowArea);
        }
        //6.限制购买区域
        if(productSpuRespDTO.getProductSaleRule().getLimitArea()!=null){
            if(ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType())){
                //身份证类型才验证
                String districtCode = IdcardUtil.getDistrictCodeByIdCard(tradeVisitorDO.getCredentialNo());
                String limitArea = StrUtil.split(productSpuRespDTO.getProductSaleRule().getLimitArea(),",").stream().filter(districtCode::startsWith).findFirst().orElse(null);
                Asserts.isTrue(limitArea == null,"当前区域的身份证不能购买此产品{}",limitArea);
            }
        }
        //7.年龄限制，只有身份证才验证
        if(StrUtil.isNotBlank(productSpuRespDTO.getProductSaleRule().getLimitAge()) && ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType())){
            JSONArray jsonArray = JSONUtil.parseArray(productSpuRespDTO.getProductSaleRule().getLimitAge());
            int age = IdcardUtil.getAgeByIdCard(tradeVisitorDO.getCredentialNo());
            JSONObject ageLimitJson = null;
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String specType = jsonObject.getStr("specType");
                if(StrUtil.equals(specType,tradeDetailDO.getSpecType())){
                    ageLimitJson = jsonObject;
                    break;
                }
            }
            if(ageLimitJson != null){
                boolean flag = false;
                String ageBegin = ageLimitJson.getStr("ageBegin");
                String ageEnd = ageLimitJson.getStr("ageEnd");
                String before = ageLimitJson.getStr("before");
                String after = ageLimitJson.getStr("after");

                boolean flagStart = true;
                boolean flagEnd = true;
                boolean isAnd = false;
                //[{"specType":"full","ageBegin":"12","ageEnd":"55","before":5,"after":1,"after":"成人票不打折"}]
                if(StringUtils.isNotBlank(before)&&StringUtils.isNotBlank(ageBegin)){
                    flagStart = checkLimitAge(age,Integer.parseInt(ageBegin),before);
                    isAnd = ObjectUtils.equalsAny(before,4,5);
                }
                if(StringUtils.isNotBlank(after)&& StringUtils.isNotBlank(ageEnd)){
                    flagEnd = checkLimitAge(age,Integer.parseInt(ageEnd),after);
                    if(isAnd){
                        isAnd = ObjectUtils.equalsAny(after,1,2);
                    }
                }
                if(isAnd){
                    if(flagStart && flagEnd){
                        flag = true;
                    }
                }else{
                    if(flagStart || flagEnd){
                        flag = true;
                    }
                }
                Asserts.isTrue(flag,"当前产品限制了年龄，游客年龄不符合规则{}",age);
            }

        }
        //8.规格证件号验证，目前只验证身份证年龄
        if(ObjectUtil.equal(tradeVisitorDO.getCredentialType(), CredentialTypeEnum.ID_CARD.getType()) && StrUtil.isNotBlank(tradeVisitorDO.getCredentialNo())){
            String rule = getSpecCheckRule(tradeDetailDO.getSpecType());
            String age = String.valueOf(IdcardUtil.getAgeByIdCard(tradeVisitorDO.getCredentialNo()));
            Asserts.isTrue(ReUtil.isMatch(rule,age),"证件号{}不在规格{}要求的年龄区间内",tradeVisitorDO.getCredentialNo(),tradeDetailDO.getSpecType());
        }

    }

    private static String getSpecCheckRule(String specType){
        try {
            DictDataRespDTO dictDataRespDTO = specTypeDictCache.get(specType);
            if(dictDataRespDTO!=null && StrUtil.isNotBlank(dictDataRespDTO.getRemark())){
                String regex = "/\\*\\*(.*?)\\*\\*/";
                String remark = dictDataRespDTO.getRemark();
                List<String> matchList = ReUtil.findAll(regex,remark,1);
                if(CollUtil.isNotEmpty(matchList)){
                    return matchList.get(0);
                }
            }
        } catch (Exception e) {
        }
        return null;
    }

    public static boolean checkLimitAge(int age ,int limitAge,String type){
        boolean flag = false;
        if("1".equals(type)){
            //小于
            flag = limitAge > age;
        }else if("2".equals(type)){
            //小于等于
            flag = limitAge >= age;
        }else if("3".equals(type)){
            //等于
            flag = limitAge == age;
        }else if("4".equals(type)){
            //大于等于
            flag = limitAge <= age;
        }else if("5".equals(type)){
            //大于
            flag = limitAge < age;
        }
        return flag;
    }


    private static final LoadingCache<String, DictDataRespDTO> specTypeDictCache = CacheUtils.buildAsyncReloadingCache(
        // 过期时间 10 分钟
        Duration.ofMinutes(10L),
        new CacheLoader<String, DictDataRespDTO>() {

            @Override
            public DictDataRespDTO load(String key) {
                DictDataApi dictDataApi = SpringUtil.getBean(DictDataApi.class);
                List<DictDataRespDTO> dictDataRespDTOList = dictDataApi.getDictDataList("project_specification_type");
                if(CollUtil.isNotEmpty(dictDataRespDTOList)){
                    return dictDataRespDTOList.stream().filter(dict->ObjectUtil.equal(dict.getValue(),key)).findFirst().orElse(null);
                }
                return null;
            }

        });
}
