/**
 * All rights Reserved, Designed By www.freemud.cn
 *
 * @Title: CouponServiceImpl
 * @Package cn.freemud.service.impl
 * @Description:
 * @author: song.cai
 * @date: 2018/12/24 18:40
 * @version V1.0
 * @Copyright: 2018 www.freemud.cn Inc. All rights reserved.
 * 注意：本内容仅限于上海非码科技内部传阅，禁止外泄以及用于其他的商业目
 */

package cn.freemud.service.impl;

import cn.freemud.adapter.CouponAdapter;
import cn.freemud.base.util.DateUtil;
import cn.freemud.constant.ResponseCodeConstant;
import cn.freemud.entities.dto.*;
import cn.freemud.entities.dto.promotion.ShoppingCartGoodsResponse;
import cn.freemud.entities.vo.*;
import cn.freemud.enums.*;
import cn.freemud.redis.RedisCache;
import cn.freemud.service.CouponService;
import cn.freemud.service.thirdparty.CardBinClient;
import cn.freemud.service.thirdparty.CouponOnlineClient;
import cn.freemud.service.thirdparty.CustomerExtendClient;
import cn.freemud.utils.BarcodeUtil;
import cn.freemud.utils.LogUtil;
import cn.freemud.utils.PropertyConvertUtil;
import cn.freemud.utils.RedisUtil;
import com.alibaba.fastjson.JSON;
import com.freemud.card.sdk.comm.Finals;
import com.freemud.card.sdk.comm.SignUtil;
import com.freemud.card.sdk.service.CouponAvailableService;
import com.freemud.card.sdk.vo.coupon.CouponStateVo;
import com.freemud.card.sdk.vo.coupon.Product;
import com.freemud.card.sdk.vo.coupon.request.CouponAvailableReqVo;
import com.freemud.card.sdk.vo.coupon.response.CouponAvailableRespVo;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CouponServiceImpl implements CouponService {
    @Autowired
    private CouponOnlineClient couponOnlineClient;

    @Value("${coupon.app.id}")
    private String appid;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private CardBinClient cardBinClient;
    @Autowired
    private CustomerExtendClient customerExtendClient;
    @Autowired
    private CouponAvailableService couponAvailableService;
    @Autowired
    private CouponAdapter couponAdapter;

    @Override
    public GetCouponDetailResponseDto getMemberCoupon(GetMemberCouponRequestVo requestVo) {
        //TODO 券详情查券服务
        Map<String, String> map = new TreeMap<String, String>();
        if (StringUtils.isBlank(requestVo.getPartnerId()) || StringUtils.isBlank(requestVo.getCouponCode())) {
            return null;
        }
        map.put(Finals.CODES, requestVo.getCouponCode());
        map.put(Finals.PARTNER_ID, requestVo.getPartnerId());
        map.put(Finals.MERCHANT_ID, requestVo.getPartnerId());
        map.put(Finals.ACTION, "code.allDetails");
        String appSecret = getAppSecret(requestVo.getPartnerId());
        String sign = SignUtil.createMD5Sign(map, appSecret);
        map.put(Finals.SIGN, sign);
        GetCouponDetailResponseDto responseDto = couponOnlineClient.getCouponDetails(map);
        if (ResponseCodeConstant.RESPONSE_SUCCESS_1.equals(responseDto.getResult())) {
            return responseDto;
        }
        return null;
    }



    @Override
    public String getAppSecret(String partnerId) {
        // 获取券服务的密钥
        String couponAppSecret = RedisUtil.getCouponAppSecret(partnerId);
        String appSecret = redisCache.getValue(couponAppSecret);
        if (StringUtils.isBlank(appSecret)) {
            GetAppKeyResponseDto getAppKeyResponseDto = cardBinClient.getAppKey(GetAppKeyRequestDto.builder().partnerId(partnerId).appId(appid).build());
            if (Objects.equals(String.valueOf(getAppKeyResponseDto.getStatusCode()), ResponseResult.SUCCESS.getCode())
                    && getAppKeyResponseDto.getData() != null) {
                redisCache.save(couponAppSecret, getAppKeyResponseDto.getData().getAppKey());
                appSecret = getAppKeyResponseDto.getData().getAppKey();
            }
        }
        return appSecret;
    }

    @Override
    public ActivityClassifyCouponBean availableCoupon(List<ShoppingCartGoodsResponse.CartGoodsDetailDto> cartGoods, String partnerId, String userId
            , String storeId, String couponCode, boolean hasGoodsCoupon, Integer orderType) {
        GetMemberCouponListRequestDto getMemberCouponListRequestDto = new GetMemberCouponListRequestDto(partnerId, userId);
        getMemberCouponListRequestDto.setStatusFlags(Arrays.asList(CouponStatus.STATUS_0.getCode()));
        getMemberCouponListRequestDto.setPageNum(1);
        getMemberCouponListRequestDto.setPageSize(Integer.MAX_VALUE);
        GetMemberCouponListResponseDto.Result result = getMemberCouponList(getMemberCouponListRequestDto);
        LogUtil.debug("CouponServiceImpl_getMemberCouponList", JSON.toJSONString(getMemberCouponListRequestDto), JSON.toJSONString(result));
        List<GetMemberCouponListResponseDto.Result.MemberCoupon> memberCoupons = Lists.newArrayList();
        // 过滤优惠券得点餐方式
        if (!Objects.isNull(result) && CollectionUtils.isNotEmpty(result.getMemberCoupons())) {
            List<String> activityCodes = result.getMemberCoupons().stream().map(GetMemberCouponListResponseDto.Result.MemberCoupon::getActivityCode).collect(Collectors.toList());
            Map<String, Boolean> activityCodesMap = couponOrderWay(partnerId, activityCodes, orderType);
            result.getMemberCoupons().forEach(memberCoupon -> {
                if (activityCodesMap.get(memberCoupon.getActivityCode()) != null && activityCodesMap.get(memberCoupon.getActivityCode())) {
                    memberCoupons.add(memberCoupon);
                }
            });
        }
        ActivityClassifyCouponBean availableCouponResponseVo = new ActivityClassifyCouponBean();
        // 可用优惠券信息
        List<ActivityCouponBean> usableCoupons = Lists.newArrayList();
        // 不可用优惠券信息
        List<ActivityCouponBean> disableCoupons = Lists.newArrayList();
        if (result == null || CollectionUtils.isEmpty(memberCoupons)) {
            return returnEmptyAvailableCoupon(availableCouponResponseVo, usableCoupons, disableCoupons);
        }
        // 对象转map
        Map<String, GetMemberCouponListResponseDto.Result.MemberCoupon> memberCouponMap = memberCoupons.stream().collect(
                Collectors.toMap(GetMemberCouponListResponseDto.Result.MemberCoupon::getCouponCode, a -> a, (k1, k2) -> k1));
        List<String> couponCodes = Lists.newArrayList();
        memberCoupons.forEach(memberCoupon -> {
            if (Objects.equals(memberCoupon.getCouponType(), CouponTypeEnum.TYPE_1.getCode())
                    || Objects.equals(memberCoupon.getCouponType(), CouponTypeEnum.TYPE_3.getCode())) {
                // 只筛选小于结束时间得券
                if (System.currentTimeMillis() < DateUtil.convert2Date(memberCoupon.getEndTime(), DateUtil.FORMAT_YYYY_MM_DD_HHMMSS).getTime()) {
                    couponCodes.add(memberCoupon.getCouponCode());
                }
            }
        });
        List<CouponStateVo> couponStateList = buildAvailiableCoupons(cartGoods, partnerId, storeId, couponCodes);
        if (CollectionUtils.isNotEmpty(couponStateList)) {
            return returnSuccessAvailiableCoupons(couponCode, hasGoodsCoupon, availableCouponResponseVo, usableCoupons, disableCoupons, memberCouponMap, couponStateList);
        }
        return null;
    }

    /**
     * 获取会员服务的优惠券列表
     *
     * @param getMemberCouponListRequestDto
     * @return
     */
    public GetMemberCouponListResponseDto.Result getMemberCouponList(GetMemberCouponListRequestDto getMemberCouponListRequestDto) {
        GetMemberCouponListResponseDto getMemberCouponListResponseDto = null;
        try {
            getMemberCouponListResponseDto = customerExtendClient.getMemberCouponListRequestDto(getMemberCouponListRequestDto);
        } catch (Exception ex) {
            log.error("会员服务优惠券接口调用失败", ex);
        }
        if (getMemberCouponListResponseDto != null && Objects.equals(getMemberCouponListResponseDto.getCode(), ResponseResult.SUCCESS.getCode())) {
            return getMemberCouponListResponseDto.getData();
        }
        return null;
    }

    /**
     * key = 活动code , value = 返回是否匹配点餐方式
     */
    public Map<String, Boolean> couponOrderWay(String partnerId, List<String> activityCodes, Integer orderTye) {
        // 去重
        List<String> activeCodes = new ArrayList<>(new HashSet(activityCodes));
        String appSecret = this.getAppSecret(partnerId);
        // 最大15一提交
        int maxNum = 15;
        int count = activeCodes.size() / maxNum;
        ActivityChannelEnum activityChannelEnum = PropertyConvertUtil.orderTypeConvert2ActivityChannel(orderTye);
        if (activityChannelEnum == null) {
            activityChannelEnum = ActivityChannelEnum.pickup;
        }
        Map<String, Boolean> result = new HashMap<>();
        for (int j = 0; j <= count; j++) {
            List calActiveCodes = null;
            if (j == count) {
                calActiveCodes = activeCodes.subList(maxNum * j, activeCodes.size());
            } else {
                calActiveCodes = activeCodes.subList(maxNum * j, maxNum * j + maxNum);
            }
            BatchQueryActivityInfoRequestDto requestDto = new BatchQueryActivityInfoRequestDto();
            requestDto.setActiveCode(calActiveCodes);
            // 书写逻辑
            requestDto.setPartnerId(Integer.valueOf(partnerId));
            String sign = SignUtil.createMD5Sign(requestDto, appSecret);
            requestDto.setSign(sign);
            BatchQueryActivityInfoResponseDto batchQueryActivityInfoResponseDto = cardBinClient.batchQueryActivityInfo(requestDto);
            if (Objects.equals(batchQueryActivityInfoResponseDto.getStatusCode(), ResponseCodeConstant.RESPONSE_SUCCESS_0_STR)
                    && CollectionUtils.isNotEmpty(batchQueryActivityInfoResponseDto.getActivities())) {
                ActivityChannelEnum finalActivityChannelEnum = activityChannelEnum;
                batchQueryActivityInfoResponseDto.getActivities().forEach(active -> {
                    // 兼容历史数据
                    if (Objects.isNull(active.getRedeemChannel())) {
                        result.put(active.getActiveCode(), true);
                    } else {
                        result.put(active.getActiveCode(), Arrays.stream(active.getRedeemChannel().split(",")).anyMatch(Predicate.isEqual(finalActivityChannelEnum.getCode())));
                    }
                });
            }
        }
        return result;
    }

    private ActivityClassifyCouponBean returnEmptyAvailableCoupon(ActivityClassifyCouponBean availableCouponResponseVo, List<ActivityCouponBean> usableCoupons, List<ActivityCouponBean> disableCoupons) {
        availableCouponResponseVo.setCouponNum(0);
        availableCouponResponseVo.setDisableCouponNum(0);
        availableCouponResponseVo.setUsableCouponNum(0);
        availableCouponResponseVo.setDisableCoupons(disableCoupons);
        availableCouponResponseVo.setUsableCoupons(usableCoupons);
        return availableCouponResponseVo;
    }

    private List<CouponStateVo> buildAvailiableCoupons(List<ShoppingCartGoodsResponse.CartGoodsDetailDto> cartGoods, String partnerId, String storeId, List<String> couponCodes) {
        String appSecret = getAppSecret(partnerId);
        // 最大15一提交
        int maxNum = 15;
        int count = couponCodes.size() / maxNum;
        // 计算可用不可用券
        CouponAvailableReqVo couponAvailableReqVo = new CouponAvailableReqVo();
        couponAvailableReqVo.setPartnerId(partnerId);
        couponAvailableReqVo.setStoreId(storeId);
        List<Product> productList = Lists.newArrayList();
        // 优惠券优先级最高，根据商品原价做计算
        // 订单金额
        Long totalAmount = createProductRequest(cartGoods, productList);
        couponAvailableReqVo.setTotalAmount(Integer.valueOf(totalAmount + ""));
        couponAvailableReqVo.setProductList(productList);
        // 构建计算SDK
        List<CouponStateVo> couponStateList = Lists.newArrayList();
        for (int j = 0; j <= count; j++) {
            List calCouponCodes = null;
            if (j == count) {
                calCouponCodes = couponCodes.subList(maxNum * j, couponCodes.size());
            } else {
                calCouponCodes = couponCodes.subList(maxNum * j, maxNum * j + maxNum);
            }
            // 书写逻辑
            couponAvailableReqVo.setCouponCodes(calCouponCodes);
            CouponAvailableRespVo couponsAvailable = couponAvailableService.getCouponsAvailable(couponAvailableReqVo, appSecret);
            LogUtil.info("availableCoupon_couponAvailableService.getCouponsAvailable", JSON.toJSONString(couponAvailableReqVo), JSON.toJSONString(couponsAvailable));
            if (Objects.equals(String.valueOf(couponsAvailable.getResult()), ResponseResult.SUCCESS.getCode())
                    && CollectionUtils.isNotEmpty(couponsAvailable.getCouponStateList())) {
                couponStateList.addAll(couponsAvailable.getCouponStateList());
            }
        }
        return couponStateList;
    }

    private Long createProductRequest(List<ShoppingCartGoodsResponse.CartGoodsDetailDto> cartGoods, List<Product> productList) {
        Long totalAmount = 0L;
        for (ShoppingCartGoodsResponse.CartGoodsDetailDto cartGood : cartGoods) {
            Product product = new Product();
            product.setProductId(cartGood.getProductId());
            product.setAmount(cartGood.getSettlementPrice().intValue() / cartGood.getQty());
            // 数量
            product.setQuantity(cartGood.getQty());
            productList.add(product);
            totalAmount += cartGood.getSettlementPrice();
        }
        return totalAmount;
    }

    private ActivityClassifyCouponBean returnSuccessAvailiableCoupons(String couponCode, boolean hasGoodsCoupon, ActivityClassifyCouponBean availableCouponResponseVo, List<ActivityCouponBean> usableCoupons, List<ActivityCouponBean> disableCoupons, Map<String, GetMemberCouponListResponseDto.Result.MemberCoupon> memberCouponMap, List<CouponStateVo> couponStateList) {
        int disableCouponNum = 0;
        int usableCouponNum = 0;
        for (CouponStateVo couponStateVo : couponStateList) {
            // 过滤0得代金券
            if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_1.getCode())
                    && (couponStateVo.getOriginalPrice() == null
                    || couponStateVo.getOriginalPrice() == 0)) {
                continue;
            }

            // 过滤折扣券为0折得
            if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_3.getCode())
                    && (couponStateVo.getDiscount() == null
                    || couponStateVo.getDiscount() == 0)) {
                continue;
            }
            ActivityCouponBean activityCouponBean = new ActivityCouponBean();
            activityCouponBean.setCouponCode(couponStateVo.getCouponCode());
            GetMemberCouponListResponseDto.Result.MemberCoupon memberCoupon = memberCouponMap.get(couponStateVo.getCouponCode());
            if (memberCoupon != null) {
                activityCouponBean.setCouponName(memberCoupon.getCouponName());
                activityCouponBean.setEndTime(memberCoupon.getEndTime());
                if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_1.getCode())) {
                    activityCouponBean.setDiscountAmount(couponStateVo.getOriginalPrice() + "");
                } else if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_3.getCode())) {
                    // TODO 折扣券 百分值10 乘以10   前端统一除以100作为操作依据
                    activityCouponBean.setDiscountAmount(couponStateVo.getDiscount() != null ? couponStateVo.getDiscount() * 10 + "" : "0");
                }
                activityCouponBean.setThresholdDesc("满" + formatAmount((couponStateVo.getMinAmount() == null ? 0 : couponStateVo.getMinAmount()) * 1.00 / 100) + "元可用");
                activityCouponBean.setActivityDesc("不与其他活动优惠同时享受。");
                activityCouponBean.setUnusedReason(couponStateVo.getMessage());
                // 详情
                GetMemberCouponListResponseVo.MemberCouponDetail detail = new GetMemberCouponListResponseVo.MemberCouponDetail();
                detail.setActivityName(memberCoupon.getCouponName());
                String startDate = DateUtil.convert2String(DateUtil.convert2Date(memberCoupon.getStartTime(), DateUtil.FORMAT_yyyyMMdd_date), DateUtil.FORMAT_yyyyMMdd_date).replace("-", ".");
                String endDate = DateUtil.convert2String(DateUtil.convert2Date(memberCoupon.getEndTime(), DateUtil.FORMAT_yyyyMMdd_date), DateUtil.FORMAT_yyyyMMdd_date).replace("-", ".");
                detail.setEndTime(endDate);
                detail.setStartTime(startDate);
                String remark = memberCoupon.getActiveDesc();
                detail.setRemark(couponAdapter.getDescribeText(remark));
                if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_1.getCode())) {
                    detail.setAmountDesc("满" + formatAmount((couponStateVo.getMinAmount() == null ? 0 : couponStateVo.getMinAmount()) * 1.00 / 100) + "元减" + formatAmount((couponStateVo.getOriginalPrice() == null ? 0 : couponStateVo.getOriginalPrice()) * 1.00 / 100) + "元");
                } else if (Objects.equals(couponStateVo.getType(), CouponTypeEnum.TYPE_3.getCode())) {
                    detail.setAmountDesc("满" + formatAmount((couponStateVo.getMinAmount() == null ? 0 : couponStateVo.getMinAmount()) * 1.00 / 100) + "元" + formatAmount((couponStateVo.getDiscount() == null ? 0 : couponStateVo.getDiscount()) * 1.00 / 10) + "折");
                }
                // 设置条形码
                GetCouponBarCodeResponseVo responseVo = this.getCouponBarCode(couponStateVo.getCouponCode());
                detail.setCouponBarCode(responseVo.getBase64Image());

                activityCouponBean.setDetail(detail);
                activityCouponBean.setActivityCode(memberCoupon.getActivityCode());
                activityCouponBean.setCouponType(couponStateVo.getType());
                activityCouponBean.setDateDescribe(couponAdapter.getDateDescribe(memberCoupon.getStatus(), memberCoupon.getStartTime(), memberCoupon.getEndTime()));
                // 券状态 0 可用 1 不可用 2 限制可用
                if (Objects.equals(activityCouponBean.getCouponCode(), couponCode)) {
                    activityCouponBean.setSelected(YesOrNoEnum.YES.getCode());
                } else {
                    activityCouponBean.setSelected(YesOrNoEnum.NO.getCode());
                }
                if (hasGoodsCoupon) {
                    disableCoupons.add(activityCouponBean);
                    disableCouponNum = disableCouponNum + 1;
                } else {
                    if (Objects.equals(couponStateVo.getState(), CouponStateEnum.STATE_1.getCode())) {
                        disableCoupons.add(activityCouponBean);
                        disableCouponNum = disableCouponNum + 1;
                    } else {
                        usableCoupons.add(activityCouponBean);
                        usableCouponNum = usableCouponNum + 1;
                    }
                }
            }

        }
        availableCouponResponseVo.setCouponNum(disableCouponNum + usableCouponNum);
        availableCouponResponseVo.setDisableCouponNum(disableCouponNum);
        availableCouponResponseVo.setUsableCouponNum(usableCouponNum);
        availableCouponResponseVo.setDisableCoupons(disableCoupons);
        availableCouponResponseVo.setUsableCoupons(usableCoupons);
        return availableCouponResponseVo;
    }

    private static Object formatAmount(Double amount) {
        if (amount % (amount.intValue()) == 0.0) {
            return amount.intValue();
        } else {
            return amount;
        }
    }

    public GetCouponBarCodeResponseVo getCouponBarCode(String couponCode) {
        byte[] bytes = BarcodeUtil.generateBarCode128(couponCode, 6D, null, true, false);
        String base64Image = Base64.getEncoder().encodeToString(bytes);
        String base64Prefix = "data:image/png;base64,";
        GetCouponBarCodeResponseVo getCouponBarCodeResponseVo = new GetCouponBarCodeResponseVo();
        getCouponBarCodeResponseVo.setBase64Image(base64Prefix + base64Image);
        return getCouponBarCodeResponseVo;
    }
}
