package com.liquidnet.service.platform.service.impl.goblin;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.GoblinNftExCodeNumDto;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.*;
import com.liquidnet.service.goblin.mapper.*;
import com.liquidnet.service.goblin.param.GoblinNftExActivityDelayParam;
import com.liquidnet.service.goblin.param.GoblinNftExActivityParam;
import com.liquidnet.service.goblin.service.IGoblinNftExActivityService;
import com.liquidnet.service.platform.utils.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: wll
 * @Description: nft兑换活动服务实现
 * @Date:Create：in 2022/4/19 4:35 下午
 */


@Service
@Slf4j
public class GoblinNftExActivityServiceImpl implements IGoblinNftExActivityService {

    @Autowired
    private GoblinNftExActivityMapper goblinNftExActivityMapper;
    @Autowired
    private GoblinNftExCodeMapper goblinNftExCodeMapper;
    @Autowired
    private GoblinNftExSkuMapper goblinNftExSkuMapper;
    @Autowired
    private GoblinGoodsSkuMapper goblinGoodsSkuMapper;
    @Autowired
    GoblinNftExCodeTaskMapper goblinNftExCodeTaskMapper;
    @Autowired
    private GoblinNftExCodeTaskRelationMapper goblinNftExCodeTaskRelationMapper;

    @Override
    public ResponseDto<Object> add(GoblinNftExActivityParam goblinNftExActivityParam) {

        // 根据名称查询
        String title = goblinNftExActivityParam.getTitle();
        GoblinNftExActivity goblinNftExActivityShow = goblinNftExActivityMapper.selectByTitle(title, goblinNftExActivityParam.getUid());
        if (goblinNftExActivityShow != null) {
            return ResponseDto.failure("活动名称已存在!");
        }

        LocalDateTime now = LocalDateTime.now();
        GoblinNftExActivity goblinNftExActivity = GoblinNftExActivity.getNew();
        BeanUtils.copyProperties(goblinNftExActivityParam, goblinNftExActivity);
        goblinNftExActivity.setActivityId(IDGenerator.nextSnowId());
        goblinNftExActivity.setCreatedAt(now);
        goblinNftExActivity.setUid(goblinNftExActivityParam.getUid());
        // mysql数据
        long s = System.currentTimeMillis();
        goblinNftExActivityMapper.addGoblinNftExActivity(goblinNftExActivity);
        log.debug("#MYS耗时:{}ms", System.currentTimeMillis() - s);
        return ResponseDto.success(goblinNftExActivity.getActivityId());
    }

    @Override
    public PageInfoVo pageList(GoblinNftExActivityParam goblinNftExActivityParam) {

        PageHelper.startPage(goblinNftExActivityParam.getPageNum(), 20, true);

        /*
         * 获取分页nft兑换活动数据
         */
        long s = System.currentTimeMillis();
        List<GoblinNftExActivity> goblinNftExActivities = goblinNftExActivityMapper.selectPageList(goblinNftExActivityParam.getTitle(), goblinNftExActivityParam.getStartTime(),
                goblinNftExActivityParam.getEndTime(), goblinNftExActivityParam.getUid());

        // 获取活动数量
        int count = goblinNftExActivityMapper.selectActivityCount(goblinNftExActivityParam.getTitle(), goblinNftExActivityParam.getStartTime(),
                goblinNftExActivityParam.getEndTime(), goblinNftExActivityParam.getUid());

        log.debug("#MYS耗时:{}ms", System.currentTimeMillis() - s);

        StringBuffer activityIds = new StringBuffer();
        for (GoblinNftExActivity GoblinNftExActivity : goblinNftExActivities) {
            activityIds.append(GoblinNftExActivity.getActivityId()).append(",");
        }


        ArrayList<GoblinNftExActivityVo> goblinNftExActivityArrayList = ObjectUtil.getGoblinNftExActivityVoArrayList();
        if (StringUtil.isNotBlank(activityIds)) {
            activityIds.deleteCharAt(activityIds.length() - 1);

            // 根据活动ids查询正在生成的兑换码
            List<GoblinNftExCodeTask> goblinNftExCodeTasks = goblinNftExCodeTaskMapper.selectByActivityIds(activityIds.toString());

            long k = System.currentTimeMillis();
            List<GoblinNftExCode> goblinNftExCodes = goblinNftExCodeMapper.selectByActivityIds(activityIds.toString());
            log.debug("#MYS耗时:{}ms", System.currentTimeMillis() - k);

            for (GoblinNftExActivity goblinNftExActivity : goblinNftExActivities) {
                for (GoblinNftExCodeTask goblinNftExCodeTask : goblinNftExCodeTasks) {
                    if (goblinNftExActivity.getActivityId().equals(goblinNftExCodeTask.getActivityId())) {
                        goblinNftExActivity.setIsDisplay(1);
                        break;
                    }
                }

                Integer countNumber = 0;
                Integer useNumber = 0;
                Integer unUseNumber = 0;
                Integer invalidNumber = 0; // 失效数量 包含未兑换和已失效(手动失效)的
                for (GoblinNftExCode goblinNftExCode : goblinNftExCodes) {
                    if (goblinNftExActivity.getActivityId().equals(goblinNftExCode.getActivityId())) {
                        countNumber++;
                        switch (goblinNftExCode.getState()) {
                            case 1:
                            case 3:
                                unUseNumber++;
                                invalidNumber++;
                                break;
                            case 2:
                                useNumber++;
                                break;
                            default:
                                unUseNumber++;
                        }
                    }
                }

                goblinNftExActivity.setCountNumber(countNumber);
                goblinNftExActivity.setUseNumber(useNumber);
                goblinNftExActivity.setUnUseNumber(unUseNumber);

                GoblinNftExActivityVo goblinNftExActivityVo = GoblinNftExActivityVo.getNew().copy(goblinNftExActivity);

                // 获取该活动最大的失效时间
                goblinNftExActivityVo.setIsDelay(0);
                if (goblinNftExActivity.getNewActivityId().isEmpty()) {
                    List<GoblinNftExSku> goblinNftExSkus = goblinNftExSkuMapper.selectList(
                            Wrappers.lambdaQuery(GoblinNftExSku.class)
                                    .eq(GoblinNftExSku::getActivityId, goblinNftExActivity.getActivityId())
                                    .orderByDesc(GoblinNftExSku::getExStopTime)
                    );
                    if (!CollectionUtil.isEmpty(goblinNftExSkus)) {
                        LocalDateTime exStopTime = goblinNftExSkus.get(0).getExStopTime();
                        if (LocalDateTime.now().isAfter(exStopTime) && invalidNumber > 0) {
                            goblinNftExActivityVo.setIsDelay(1);
                        }
                    }
                }

                goblinNftExActivityArrayList.add(goblinNftExActivityVo);
            }
        }


        PageInfoVo pageInfoVo = PageInfoVo.getNew();
        pageInfoVo.setList(goblinNftExActivityArrayList);
        pageInfoVo.setTotal(count);
        // PageInfoVo pageInfo = new PageInfoVo(goblinNftExActivityArrayList, count, 0);
        return pageInfoVo;
    }

    @Override
    public List<GoblinNftSkuVo> selectNftSkuByActivityId(String activityId) {

        ArrayList<GoblinNftSkuVo> goblinNftSkuVoArrayList = ObjectUtil.getGoblinNftSkuVoArrayList();

        // 获取兑换活动
        GoblinNftExActivity goblinNftExActivity = goblinNftExActivityMapper.selectByActivityId(activityId);
        if (goblinNftExActivity == null) {
            return goblinNftSkuVoArrayList;
        }

        // 获取兑换活动和sku的关联
        List<GoblinNftExSku> goblinNftExSkus = goblinNftExSkuMapper.selectGoblinNftExSkuByActivityIds(activityId);
        if (goblinNftExSkus.size() <= 0) {
            return goblinNftSkuVoArrayList;
        }

        StringBuffer skuIds = new StringBuffer();
        for (GoblinNftExSku goblinNftExSku : goblinNftExSkus) {
            skuIds.append(goblinNftExSku.getSkuId()).append(",");
        }


        List<GoblinGoodsSku> goblinGoodsSkus = goblinGoodsSkuMapper.selectBySkuIds(skuIds.toString());


        // 根据活动id获取code码
        List<GoblinNftExCode> goblinNftExCodes = goblinNftExCodeMapper.selectByActivityIds(activityId);

        for (GoblinGoodsSku goblinGoodsSku : goblinGoodsSkus) {

            GoblinNftSkuVo goblinNftSkuVo = GoblinNftSkuVo.getNew().copy(goblinGoodsSku);

            GoblinNftExSku nftExSku = GoblinNftExSku.getNew();
            for (GoblinNftExSku goblinNftExSku : goblinNftExSkus) {
                if (goblinGoodsSku.getSkuId().equals(goblinNftExSku.getSkuId())) {
                    nftExSku = goblinNftExSku;
                    break;
                }
            }

            GoblinNftExSkuVo goblinNftExSkuVo = GoblinNftExSkuVo.getNew().copy(nftExSku);

            Integer countNumber = 0;
            Integer useNumber = 0;
            Integer unUseNumber = 0;
            ArrayList<GoblinNftExCode> gCodes = ObjectUtil.getGoblinNftExCodeArrayList();
            for (GoblinNftExCode goblinNftExCode : goblinNftExCodes) {
                /* if (StringUtil.isNotBlank(goblinNftExCode.getBoxSkuId()) && goblinNftExCode.getBoxSkuId().equals(goblinGoodsSku.getSkuId())) {
                    gCodes.add(goblinNftExCode);
                }
                if (StringUtil.isBlank(goblinNftExCode.getBoxSkuId()) && goblinNftExCode.getSkuId().equals(goblinGoodsSku.getSkuId())) {
                    gCodes.add(goblinNftExCode);
                }*/

                if (goblinNftExCode.getSkuId().equals(goblinGoodsSku.getSkuId())) {
                    gCodes.add(goblinNftExCode);
                }
            }

            if (gCodes == null || gCodes.size() <= 0) {
                continue;
            }
            for (GoblinNftExCode goblinNftExCode : gCodes) {
                countNumber++;
                switch (goblinNftExCode.getState()) {
                    case 2:
                        useNumber++;
                        break;
                    default:
                        unUseNumber++;
                }
            }
            goblinNftSkuVo.setCountNumber(countNumber);
            goblinNftSkuVo.setUseNumber(useNumber);
            goblinNftSkuVo.setUnUseNumber(unUseNumber);
            goblinNftSkuVo.setGoblinNftExSkuVo(goblinNftExSkuVo);
            goblinNftSkuVoArrayList.add(goblinNftSkuVo);
        }


        return goblinNftSkuVoArrayList;
    }

    @Override
    public void excel(HttpServletResponse httpServletResponse, GoblinNftExActivityParam goblinNftExActivityParam) {

        /*
         * 获取分页nft兑换活动数据
         */
        long s = System.currentTimeMillis();
        // PageHelper.startPage(goblinNftExActivityParam.getPageNum(), 20, true);
        List<GoblinNftExActivity> goblinNftExActivities = goblinNftExActivityMapper.selectPageList(goblinNftExActivityParam.getTitle(), goblinNftExActivityParam.getStartTime(),
                goblinNftExActivityParam.getEndTime(), goblinNftExActivityParam.getUid());
        log.debug("#MYS耗时:{}ms", System.currentTimeMillis() - s);

        StringBuffer activityIds = new StringBuffer();
        for (GoblinNftExActivity GoblinNftExActivity : goblinNftExActivities) {
            activityIds.append(GoblinNftExActivity.getActivityId()).append(",");
        }


        ArrayList<ActivityExcelVo> activityExcelVoArrayList = ObjectUtil.getActivityExcelVoArrayList();
        if (StringUtil.isNotBlank(activityIds)) {

            activityIds.deleteCharAt(activityIds.length() - 1);

            long k = System.currentTimeMillis();
            List<GoblinNftExCode> goblinNftExCodes = goblinNftExCodeMapper.selectByActivityIds(activityIds.toString());
            log.debug("#MYS耗时:{}ms", System.currentTimeMillis() - k);

            for (GoblinNftExActivity goblinNftExActivity : goblinNftExActivities) {
                Integer countNumber = 0;
                Integer useNumber = 0;
                Integer unUseNumber = 0;
                for (GoblinNftExCode goblinNftExCode : goblinNftExCodes) {
                    if (goblinNftExActivity.getActivityId().equals(goblinNftExCode.getActivityId())) {
                        countNumber++;
                        switch (goblinNftExCode.getState()) {
                            case 2:
                                useNumber++;
                                break;
                            default:
                                unUseNumber++;
                        }
                    }
                }
                ActivityExcelVo activityExcelVo = ActivityExcelVo.getNew();
                activityExcelVo.setTitle(goblinNftExActivity.getTitle());
                activityExcelVo.setExcelTime(goblinNftExActivity.getCreatedAt().toString());
                activityExcelVo.setCountNumber(countNumber);
                activityExcelVo.setUseNumber(useNumber);
                activityExcelVo.setUnUseNumber(unUseNumber);
                activityExcelVoArrayList.add(activityExcelVo);
            }
        }

        try {
            writeExcel(httpServletResponse, activityExcelVoArrayList, "活动excel", "sheet1", ActivityExcelVo.class);
        } catch (Exception e) {
            log.error("导出excel失败！");
        }

    }


    /**
     * 导出
     *
     * @param response
     * @param data
     * @param fileName
     * @param sheetName
     * @param clazz
     * @throws Exception
     */
    public static void writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName,
                                  String sheetName, Class clazz) throws Exception {
        // 表头样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 设置表头居中对齐
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 内容样式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 设置内容靠左对齐
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).sheet(sheetName)
                .registerWriteHandler(horizontalCellStyleStrategy).doWrite(data);

    }

    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + "-" + LocalDate.now() + ".xlsx");
        return response.getOutputStream();
    }

    @Transactional
    @Override
    public ResponseDto delay(GoblinNftExActivityDelayParam delayParam) {
        // 结束时间不能大于开始时间
        if (!delayParam.getEndTime().isAfter(delayParam.getStartTime())) {
            return ResponseDto.failure("结束时间必需大于开始时间!");
        }
        // 名称不能重复
        String title = delayParam.getTitle();
        String uid = delayParam.getUid();
        GoblinNftExActivity goblinNftExActivityShow = goblinNftExActivityMapper.selectByTitle(title, uid);
        if (goblinNftExActivityShow != null) {
            return ResponseDto.failure("活动名称已存在!");
        }
        // 已经延期过
        String oldActivityId = delayParam.getActivityId();
        GoblinNftExActivity oldActivity = goblinNftExActivityMapper.selectByActivityId(oldActivityId);
        if (!oldActivity.getNewActivityId().isEmpty()) {
            return ResponseDto.failure("该活动仅支持延期一次!");
        }
        // 是否已过最大结束时间
        List<GoblinNftExSku> goblinNftExSkus = goblinNftExSkuMapper.selectList(
                Wrappers.lambdaQuery(GoblinNftExSku.class)
                        .eq(GoblinNftExSku::getActivityId, oldActivityId)
                        .orderByDesc(GoblinNftExSku::getExStopTime)
        );
        if (CollectionUtil.isEmpty(goblinNftExSkus)) {
            return ResponseDto.failure("该活动不存在或尚未配置SKU信息!");
        } else {
            // TODO: 2022/6/2 时间未到 但是全部手动设置了失效？？？？
            if (LocalDateTime.now().isBefore(goblinNftExSkus.get(0).getExStopTime())) {
                return ResponseDto.failure("该活动还有未过期的兑换码!");
            }
        }
        // TODO: 2022/6/2 是否有失效的code？？?

        // 活动入库
        LocalDateTime now = LocalDateTime.now();
        GoblinNftExActivity goblinNftExActivity = GoblinNftExActivity.getNew();
        String activityId = IDGenerator.nextSnowId();
        goblinNftExActivity.setActivityId(activityId);
        goblinNftExActivity.setUid(uid);
        goblinNftExActivity.setTitle(title);
        goblinNftExActivity.setCreatedAt(now);
        goblinNftExActivityMapper.addGoblinNftExActivity(goblinNftExActivity);
        // 更新活动标识 记录延期过
        GoblinNftExActivity oldActivityUp = GoblinNftExActivity.getNew();
        oldActivityUp.setNewActivityId(activityId);
        oldActivityUp.setUpdatedAt(now);
        goblinNftExActivityMapper.update(
                oldActivityUp,
                Wrappers.lambdaUpdate(GoblinNftExActivity.class).eq(GoblinNftExActivity::getActivityId, oldActivityId)
        );

        // 查询出所有过期的码 所有
        List<GoblinNftExCodeNumDto> exCodeNum = goblinNftExSkuMapper.selectExCodeNumAll(oldActivityId);
        // 重新生成
        List<GoblinNftExCodeTask> goblinNftExCodeTasks = ObjectUtil.getGoblinNftExCodeTaskArrayList();
        for (GoblinNftExCodeNumDto goblinNftExCodeNumDto : exCodeNum) {
            // 定时任务对象构建
            GoblinNftExCodeTask goblinNftExCodeTask = GoblinNftExCodeTask.getNew().copy(goblinNftExCodeNumDto);
            goblinNftExCodeTask.setActivityId(activityId);
            goblinNftExCodeTask.setTaskId(IDGenerator.nextSnowId());
            goblinNftExCodeTask.setExStartTime(delayParam.getStartTime());
            goblinNftExCodeTask.setExStopTime(delayParam.getEndTime());
            goblinNftExCodeTask.setCreatedAt(now);
            if (goblinNftExCodeNumDto.getUnbox().equals("1")) {
                ArrayList<GoblinNftExCodeTaskRelation> goblinNftExCodeTaskRelations = ObjectUtil.getGoblinNftExCodeTaskRelationArrayList();
                // 查询出所有过期的码 盲盒
                List<GoblinNftExCodeNumDto> exCodeNumBox = goblinNftExSkuMapper.selectExCodeNumBox(oldActivityId, goblinNftExCodeNumDto.getSkuId());
                Integer sNumber = 0;
                for (GoblinNftExCodeNumDto codeNumBox : exCodeNumBox) {
                    GoblinNftExCodeTaskRelation goblinNftExCodeTaskRelation = GoblinNftExCodeTaskRelation.getNew();
                    goblinNftExCodeTaskRelation.setTaskId(goblinNftExCodeTask.getTaskId());
                    goblinNftExCodeTaskRelation.setSkuId(codeNumBox.getBoxSkuId());
                    goblinNftExCodeTaskRelation.setStock(codeNumBox.getNum());
                    goblinNftExCodeTaskRelation.setCreatedAt(now);
                    goblinNftExCodeTaskRelations.add(goblinNftExCodeTaskRelation);
                    sNumber += codeNumBox.getNum();
                }
                goblinNftExCodeTaskRelationMapper.inserts(goblinNftExCodeTaskRelations);
                goblinNftExCodeTask.setExStock(sNumber);
            }

            goblinNftExCodeTasks.add(goblinNftExCodeTask);
        }
        if (goblinNftExCodeTasks.size() > 0) {
            goblinNftExCodeTaskMapper.addGoblinNftExCodeTasks(goblinNftExCodeTasks);
        }

        return ResponseDto.success("操作成功");
    }

}
