package com.cpop.oam.business.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.cpop.api.tencent.wxWork.handler.WebHookSendHandler;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.SourceType;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.handler.TencentCosHandler;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.file.FileUploadUtils;
import com.cpop.core.utils.file.FileUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.core.utils.uuid.IdUtils;
import com.cpop.jambox.business.entity.StoreExtend;
import com.cpop.jambox.business.service.StoreExtendService;
import com.cpop.jambox.framework.constant.JamboxCloudUrl;
import com.cpop.oam.business.bo.DataImportBo;
import com.cpop.oam.business.bo.DataImportPageBo;
import com.cpop.oam.business.dto.DataImportDto;
import com.cpop.oam.business.dto.DataImportParamsDto;
import com.cpop.oam.business.vo.DataImportPageVo;
import com.cpop.oam.framework.constant.OamRedisConstant;
import com.cpop.oam.framework.constant.WebHookKeyConstant;
import com.cpop.system.business.vo.SysFileVo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cpop.oam.business.entity.DataImport;
import com.cpop.oam.business.mapper.DataImportMapper;
import com.cpop.oam.business.service.DataImportService;
import com.qcloud.cos.model.UploadResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ConnectTimeoutException;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;

import static com.alibaba.excel.cache.Ehcache.BATCH_COUNT;
import static com.cpop.jambox.business.entity.table.StoreExtendTableDef.STORE_EXTEND;
import static com.cpop.oam.business.entity.table.DataImportTableDef.DATA_IMPORT;
import static com.cpop.system.business.entity.table.BrandTableDef.BRAND;
import static com.cpop.system.business.entity.table.StoreTableDef.STORE;

/**
 * oam-数据导入表 服务层实现。
 *
 * @author DB
 * @since 2023-12-10
 */
@Service("dataImportService")
@Slf4j
public class DataImportServiceImpl extends ServiceImpl<DataImportMapper, DataImport> implements DataImportService {

    /**
     * 数据导入分页查询
     * @author DB
     * @since 2023/12/10
     * @param bo 请求参数
     * @return R<Page<DataImportPageVo>>
     */
    @Override
    public Page<DataImportPageVo> getDataImportPage(DataImportPageBo bo) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.pageAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()),
                QueryWrapper.create().select(DATA_IMPORT.ID, DATA_IMPORT.FILE_URL, DATA_IMPORT.IS_CLEAR, DATA_IMPORT.IMPORT_STATUS, DATA_IMPORT.CREATE_TIME)
                        .select(BRAND.BRAND_NAME)
                        .select(STORE.STORE_NAME)
                        .leftJoin(BRAND).on(BRAND.ID.eq(DATA_IMPORT.BRAND_ID))
                        .leftJoin(STORE).on(STORE.ID.eq(DATA_IMPORT.STORE_ID))
                        .where(DATA_IMPORT.IMPORT_STATUS.eq(bo.getImportStatus()))
                        .and(STORE.STORE_NAME.like(bo.getStoreName()))
                        .orderBy(DATA_IMPORT.CREATE_TIME.desc()),
                DataImportPageVo.class);
    }
    /**
     * 新增数据导入
     * @author DB
     * @since 2023/12/10
     * @param bo 请求参数
     */
    @Override
    public void insertDataImport(DataImportBo bo) {
        DataImport dataImport = BeanUtils.mapToClass(bo, DataImport.class);
        dataImport.setImportStatus(false).setSourceType(SourceType.JAMBOX.toString());
        this.save(dataImport);
    }

    /**
     * 立即导入
     * @author DB
     * @since 2023/12/10
     */
    @Override
    public void importNow(String id) {
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(OamRedisConstant.DATA_IMPORT_LOCK + id);
        if (userIdLock.tryLock()) {
            try {
                //读取数据
                DataImport dataImport = this.getById(id);
                if (dataImport.getImportStatus()) {
                    throw new ServiceException("数据已导入,请刷新页面");
                }
                importNow(dataImport);
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复导入");
        }
    }

    /**
     * 现在导入
     * @author DB
     * @since 2024/1/15
     * @param dataImport 数据导入
     */
    @Override
    public void importNow(DataImport dataImport) {
        //读取数据
        DataImportParamsDto dataImportParamsDto = new DataImportParamsDto();
        dataImportParamsDto.setFileUrl(dataImport.getFileUrl()).setEmptyTag(dataImport.getIsClear() ? 1 : 0).setUploadId(dataImport.getId());
        if (SourceType.valueOf(dataImport.getSourceType()) == SourceType.JAMBOX) {
            StoreExtend storeExtend = SpringUtils.getBean(StoreExtendService.class).queryChain().where(STORE_EXTEND.STORE_ID.eq(dataImport.getStoreId())).one();
            dataImportParamsDto.setStoreId(storeExtend.getStoreCloudId());
        } else {
            dataImportParamsDto.setStoreId(dataImport.getStoreId());
        }
        this.updateChain().set(DATA_IMPORT.IMPORT_STATUS, true).where(DATA_IMPORT.ID.eq(dataImport.getId())).update();
        try {
            SpringUtils.getBean(RestTemplate.class).postForObject(JamboxCloudUrl.DATA_IMPORT, dataImportParamsDto, String.class);
        } catch (Exception e) {
            log.info("数据导入超时,导入id为：{}", dataImport.getId());
        }
    }

    /**
     * 检查上传的文件
     * @author DB
     * @since 2023/12/10
     * @param file 文件
     */
    @Override
    public SysFileVo checkImportData(MultipartFile file) {
        //临时存储
        List<DataImportDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        //文件检查
        File tempFile = null;
        String fileName = file.getOriginalFilename();
        try {
            EasyExcel.read(file.getInputStream(), DataImportDto.class, new ReadListener<DataImportDto>() {
                @Override
                public void invoke(DataImportDto dataImportDto, AnalysisContext analysisContext) {
                    //检查手机号
                    if (StringUtils.isBlank(dataImportDto.getName())) {
                        throw new ServiceException(String.format("第%s行姓名为空，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                    }
                    //检查手机号
                    if (StringUtils.isBlank(dataImportDto.getPhone())) {
                        dataImportDto.setPhone(IdUtil.getSnowflakeNextIdStr());
                    }
                    //课卡类型
                    if (StringUtils.isBlank(dataImportDto.getCardType())) {
                        //计费类型
                        if (!StringUtils.equals(dataImportDto.getBillingType(), "储值卡")) {
                            throw new ServiceException(String.format("第%s行计费类型不为储值卡时课卡类型不能为空，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                        }
                    } else {
                        //计费类型
                        if (!StringUtils.equals(dataImportDto.getBillingType(), "暂无")) {
                            //计费类型
                            if (StringUtils.isBlank(dataImportDto.getBillingType())) {
                                throw new ServiceException(String.format("第%s行课卡类型不为暂无时计费类型不能为空，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                            } else {
                                if (StringUtils.equals(dataImportDto.getBillingType(), "限时卡")){
                                    dataImportDto.setBillingType("时限卡");
                                }
                                if (!StringUtils.equals(dataImportDto.getBillingType(), "时限卡")){
                                    //首充课时
                                    if (dataImportDto.getFirstChargePeriod() == null || dataImportDto.getFirstChargePeriod() <= 0) {
                                        throw new ServiceException(String.format("第%s行计费类型不为时限卡时首充课时不能为空或0，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                                    }
                                }
                            }
                            //剩余课时
                            if (dataImportDto.getRemainingClassHours() == null) {
                                dataImportDto.setFirstChargePeriod(0);
                            }
                            //消费金额
                            if (dataImportDto.getAmountSpent() == null) {
                                dataImportDto.setAmountSpent(0);
                            }
                            //验证日期是否标准并且办卡日期小于有效期
                            if (StringUtils.isBlank(dataImportDto.getExpiryDate()) || StringUtils.isBlank(dataImportDto.getCreateCardDate())) {
                                throw new ServiceException(String.format("第%s行办卡日期或到期日期不能为空，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                            } else {
                                LocalDate expiryDate = LocalDate.parse(dataImportDto.getExpiryDate(), DateTimeFormatter.ofPattern("yyyy/M/d"));
                                LocalDate createCardDate = LocalDate.parse(dataImportDto.getExpiryDate(), DateTimeFormatter.ofPattern("yyyy/M/d"));
                                if (expiryDate.isBefore(createCardDate)) {
                                    throw new ServiceException(String.format("第%s行办卡日期不能大于到期日期，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                                }
                            }
                            //课程顾问
                            if (StringUtils.isBlank(dataImportDto.getBillingType())) {
                                dataImportDto.setBillingType("校长");
                            }
                            //性别
                            if (StringUtils.isBlank(dataImportDto.getSex())) {
                                dataImportDto.setSex("女");
                            }
                            //课卡名称
                            if (StringUtils.isBlank(dataImportDto.getCardName())) {
                                throw new ServiceException(String.format("第%s行课卡名称为空，请核实", analysisContext.readRowHolder().getRowIndex() + 1));
                            }
                            //停卡时剩余天数
                            if (dataImportDto.getStopCardRemainingDays() == null) {
                                dataImportDto.setStopCardRemainingDays(0);
                                dataImportDto.setCardStatus("使用中");
                            } else {
                                dataImportDto.setCardStatus("停卡");
                            }
                            //停卡缓冲天数
                            if (dataImportDto.getStopCardBufferDays() == null) {
                                dataImportDto.setStopCardBufferDays(0);
                            }
                            if (dataImportDto.getStopCardBufferDays() != 0 && StringUtils.equals("使用中", dataImportDto.getCardStatus())) {
                                dataImportDto.setCardStatus("未激活");
                            } else if (dataImportDto.getStopCardBufferDays() != 0 && StringUtils.equals("停卡", dataImportDto.getCardStatus())) {
                                dataImportDto.setCardStatus("停卡未激活");
                            }
                        }
                    }
                    cachedDataList.add(dataImportDto);
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext analysisContext) {

                }

                /**
                 * 这里会一行行的返回头
                 */
                @Override
                public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                    if (!StringUtils.equals(headMap.get(15).getStringValue(), "停卡缓冲天数")) {
                        throw new ServiceException("模板错误，请使用Oam导入模板");
                    }
                }

                /**
                 * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
                 *
                 * @param exception 异常
                 * @param context   内容
                 */
                @Override
                public void onException(Exception exception, AnalysisContext context) {
                    // 如果是某一个单元格的转换异常 能获取到具体行号  如果要获取头的信息 配合invokeHeadMap使用
                    if (exception instanceof ExcelDataConvertException) {
                        ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
                        throw new ServiceException("第" + (excelDataConvertException.getRowIndex() + 1) + "行，第" + (excelDataConvertException.getColumnIndex() + 1) + "列解析异常，数据为:" + excelDataConvertException.getCellData());
                    } else {
                        throw new ServiceException(exception.getMessage());
                    }
                }
            }).sheet().doRead();
            if (cachedDataList.isEmpty()){
                throw new ServiceException("导入数据为空,请检查文件内容,按照模板格式导入");
            }
            tempFile = File.createTempFile(fileName, ".xlsx");
            log.info("文件名：{};临时文件地址: {}", fileName, tempFile.getPath());
            EasyExcel.write(tempFile.getPath(), DataImportDto.class)
                    .sheet("模板")
                    .doWrite(() -> {
                        return cachedDataList;
                    });
            MultipartFile multipartFile = FileUtils.getInstance().getMultipartFile(tempFile);
            //文件上传
            TencentCosHandler tencentCosHandler = SpringUtils.getBean(TencentCosHandler.class);
            UploadResult uploadResult = tencentCosHandler.cdnUpload(multipartFile);
            String filename = FileUploadUtils.getInstance().extractFilename(multipartFile);
            SysFileVo sysFileVo = new SysFileVo();
            sysFileVo.setUrl("https://" + uploadResult.getBucketName() + tencentCosHandler.getCdnUrl() + uploadResult.getKey())
                    .setFileName(filename)
                    .setNewFileName(FileUtils.getInstance().getName(filename))
                    .setOriginalFilename(file.getOriginalFilename());
            return sysFileVo;
        } catch (IOException e) {
            throw new ServiceException("文件上传失败!");
        } finally {
            assert tempFile != null;
            //tempFile.delete();
        }
    }
}
