package com.yida.data.common.excel;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yida.data.common.core.entity.EasyExcelExportBaseDTO;
import com.yida.data.common.core.enums.FileDealStatusEnum;
import com.yida.data.common.core.file.IExportExcelClient;
import com.yida.data.common.core.file.common.export.ExportExcelData;
import com.yida.data.common.core.file.common.export.SelectExportData;
import com.yida.data.common.core.utils.FileUtil;
import com.yida.data.common.service.CommonService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 导出excel工具类
 *
 * @author ZYJ
 * @date 2023/10/19 16:49
 */
@Data
@Slf4j
@AllArgsConstructor
public abstract class DefaultExportExcelClient<T extends SelectExportData, R extends EasyExcelExportBaseDTO, O, BaseService extends IService<O>>
        implements IExportExcelClient<T> {

    /**
     * 导出的service层
     */
    protected final BaseService baseService;

    protected final RedisService redisService;
    protected final CommonService commonService;
    protected final String exportCacheKey;
    protected final String uploadUrl;

    public Class<R> getClassR() {
        Type sType = getClass().getGenericSuperclass();
        Type[] generics = ((ParameterizedType) sType).getActualTypeArguments();
        return (Class<R>) (generics[1]);
    }

    /**
     * 查询excel 导出需要的数据
     *
     * @param t 查询条件
     * @return java.util.List<O>
     * @author ZYJ
     * @date 2023/12/12 16:54
     */
    public abstract List<O> selectExportData(T t);

    /**
     * 处理单条数据
     *
     * @param o 需要处理的数据
     * @return R
     * @author ZYJ
     * @date 2023/12/13 13:58
     */
    public abstract R handleSingleData(O o);

    @Override
    public void exportExcelData(T t) {
        File file = null;
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 生成导出成功excel文件名
            String fileName = FileUtil.getLocalUploadAddress() + UUID.randomUUID() + ".xls";
            // 查询需要导出的文件信息
            List<O> dataList = this.selectExportData(t);

            // 添加处理总数量
            redisService.hset(this.exportCacheKey, t.getKey(), ExportExcelData.builder()
                    .key(t.getKey())
                    .exportStatus(FileDealStatusEnum.DEALING.getStatus())
                    .total(dataList.size())
                    .finish(0)
                    .build());

            // 处理数据
            List<R> list = new ArrayList<>();
            // 完成数量
            int finishNumber = 0;
            for (O o : dataList) {
                try {
                    R r = this.handleSingleData(o);
                    list.add(r);
                } catch (Exception e) {
                    log.error("{}导出错误: {}", t.getFunctionName(), JSONUtil.toJsonStr(o));
                } finally {
                    finishNumber += 1;
                    redisService.hset(this.exportCacheKey, t.getKey(), ExportExcelData.builder()
                            .key(t.getKey())
                            .exportStatus(FileDealStatusEnum.DEALING.getStatus())
                            .total(dataList.size())
                            .finish(finishNumber)
                            .build());
                }
            }
            // 导出数据
            file = new File(fileName);
            EasyExcel.write(file, getClassR())
                    .excelType(ExcelTypeEnum.XLS)
                    .sheet("数据导出")
                    .doWrite(list);
            // 上传文件到媒资
            String fileUrl = FileUtil.uploadFileToMediaServer(uploadUrl, file);
            // 处理完成
            ExportExcelData exportExcelData = ExportExcelData.builder()
                    .key(t.getKey())
                    .exportStatus(FileDealStatusEnum.DEAL_SUCCESS.getStatus())
                    .fileName(fileName)
                    .total(dataList.size())
                    .finish(finishNumber)
                    .filePath(fileUrl)
                    .build();
            redisService.hset(this.exportCacheKey, t.getKey(), exportExcelData);
            // 添加处理时间
            stopWatch.stop();
            log.info("{}数据总共耗时: {}", t.getFunctionName(), stopWatch.getTotalTimeSeconds() + "秒");
        } catch (Exception e) {
            log.error("{}导出失败", t.getFunctionName(), e);
            // 添加处理失败的结果
            redisService.hset(this.exportCacheKey, t.getKey(), ExportExcelData.builder()
                    .key(t.getKey())
                    .exportStatus(FileDealStatusEnum.DEAL_FAIL.getStatus())
                    .build());
        } finally {
            if (Objects.nonNull(file)) {
                cn.hutool.core.io.FileUtil.del(file);
            }
        }
    }

    @Override
    public Object selectExportInfo(String key) {
        return redisService.hget(exportCacheKey, key);
    }
}
