package com.bonus.canteen.core.common.export.service;

import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bonus.canteen.core.common.export.constant.ExportFileStateEnum;
import com.bonus.canteen.core.common.export.constant.ExportFileTypeEnum;
import com.bonus.canteen.core.common.export.dto.ExportRecordQueryDTO;
import com.bonus.canteen.core.common.export.mapper.ExportRecordMapper;
import com.bonus.canteen.core.common.export.model.ExportRecord;
import com.bonus.canteen.core.common.export.vo.ExportRecordVO;
import com.bonus.canteen.core.common.page.PageVO;
import com.bonus.canteen.core.common.redis.RedisUtil;
import com.bonus.canteen.core.customer.utils.SecureManager;
import com.bonus.canteen.core.order.utils.LeNumUtil;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.houqin.constant.LeConstants;
import com.bonus.common.houqin.i18n.I18n;
import com.bonus.common.houqin.utils.id.Id;
import com.github.pagehelper.page.PageMethod;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.lang.invoke.SerializedLambda;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ExportRecordService {
    private static final Logger log = LoggerFactory.getLogger(ExportRecordService.class);
    private static final String STOP_KEY = "yst:export:stopping:";
    private static final String OPERATING_LOCK_KEY = "yst:export:operating:";
    @Autowired
    @Lazy
    private ExportRecordMapper exportRecordMapper;
    @Autowired
    @Lazy
    private ExportUploadManager exportUploadManager;

    public ExportRecord create(String fileName, ExportFileTypeEnum type, boolean ifPrivate, LocalDateTime expiredTime) {
        ExportRecord exportRecord = new ExportRecord();
        exportRecord.setExportId(Id.next());
        exportRecord.setFileName(fileName);
        exportRecord.setExportFileState(ExportFileStateEnum.WAITING.getKey());
        exportRecord.setExportFileType(type.getKey());
        exportRecord.setExpiredTime(expiredTime);
        exportRecord.setIfPrivate(ifPrivate ? LeConstants.COMMON_YES : LeConstants.COMMON_NO);
        exportRecord.setCrby(((Long) SecureManager.getSubjectId().orElse(LeConstants.DATA_DEFAULT_LONG)).toString());
        this.insert(exportRecord);
        return exportRecord;
    }

    public ExportRecord createFinished(String fileName, ExportFileTypeEnum type, boolean ifPrivate, LocalDateTime expiredTime, String fileUrl, Long fileSize) {
        ExportRecord exportRecord = new ExportRecord();
        exportRecord.setExportId(Id.next());
        exportRecord.setFileName(fileName);
        exportRecord.setExportFileState(ExportFileStateEnum.FINISHED.getKey());
        exportRecord.setExportFileType(type.getKey());
        exportRecord.setExpiredTime(expiredTime);
        exportRecord.setIfPrivate(ifPrivate ? LeConstants.COMMON_YES : LeConstants.COMMON_NO);
        exportRecord.setCrby(((Long) SecureManager.getSubjectId().orElse(LeConstants.DATA_DEFAULT_LONG)).toString());
        exportRecord.setFinishTime(LocalDateTime.now());
        exportRecord.setProcess(100);
        exportRecord.setFileUrl(fileUrl);
        exportRecord.setFileSize(fileSize);
        this.insert(exportRecord);
        return exportRecord;
    }

    public void insert(ExportRecord entity) {
        this.exportRecordMapper.insert(entity);
    }

    public void updateCreatingProgress(Long exportId, Integer progress, String fileUrl, Long fileSize) {
        if (exportId != null && progress != null) {
            this.exportRecordMapper.update(null, Wrappers.lambdaUpdate(ExportRecord.class)
                    .set(ExportRecord::getProcess, progress)
                    .set(progress != 100, ExportRecord::getExportFileState, ExportFileStateEnum.CREATING.getKey())
                    .set(progress == 100, ExportRecord::getExportFileState, ExportFileStateEnum.FINISHED.getKey())
                    .set(progress == 100, ExportRecord::getFinishTime, LocalDateTime.now())
                    .set(CharSequenceUtil.isNotBlank(fileUrl), ExportRecord::getFileUrl, fileUrl)
                    .set(fileSize != null, ExportRecord::getFileSize, fileSize)
                    .eq(ExportRecord::getExportId, exportId)
                    .in(ExportRecord::getExportFileState, new Object[]{ExportFileStateEnum.CREATING.getKey(), ExportFileStateEnum.WAITING.getKey()}));
        } else {
            log.warn("参数错误 {} {}", exportId, progress);
        }
    }

    public void stopAndDelete(Long exportId) {
        RLock lock = RedisUtil.getLock("yst:export:operating:" + exportId);
        if (!lock.tryLock()) {
            throw new ServiceException("common.export.operating");
        } else {
            try {
                ExportRecord exportRecord = (ExportRecord) this.exportRecordMapper.selectById(exportId);
                if (exportRecord == null) {
                    throw new ServiceException("common.export.no-record");
                }

                if (ExportFileStateEnum.CREATING.getKey().equals(exportRecord.getExportFileState())) {
                    int row = this.updateState(exportId, ExportFileStateEnum.STOPPING.getKey());
                    if (row > 0) {
                        RedisUtil.setNx("yst:export:stopping:" + exportId, exportId, 3600);
                    }
                } else {
                    if (!ExportFileStateEnum.WAITING.getKey().equals(exportRecord.getExportFileState()) && !ExportFileStateEnum.FINISHED.getKey().equals(exportRecord.getExportFileState()) && !ExportFileStateEnum.EXPIRED.getKey().equals(exportRecord.getExportFileState()) && !ExportFileStateEnum.CANCELED.getKey().equals(exportRecord.getExportFileState())) {
                        throw new ServiceException("common.export.wrong-state");
                    }

                    if (CharSequenceUtil.isNotBlank(exportRecord.getFileUrl())) {
                        this.exportUploadManager.remove(exportRecord.getFileUrl());
                    }

                    this.exportRecordMapper.deleteById(exportId);
                }
            } catch (Exception var12) {
                log.error("停止或取消或删除导出任务失败", var12);
                throw new ServiceException("停止或取消或删除导出任务失败");
            } finally {
                try {
                    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                } catch (Exception var11) {
                    log.error("释放锁失败", var11);
                }

            }

        }
    }

    public void cancelAllWaitingAndCreating() {
        this.exportRecordMapper.update(null,
                Wrappers.lambdaUpdate(ExportRecord.class)
                        .set(ExportRecord::getExportFileState, ExportFileStateEnum.CANCELED.getKey())
                        .in(ExportRecord::getExportFileState, new Object[]{ExportFileStateEnum.WAITING.getKey(), ExportFileStateEnum.CREATING.getKey(), ExportFileStateEnum.STOPPING.getKey()}));
    }

    public void didStop(Long exportId) {
        int row = this.exportRecordMapper.deleteById(exportId);
        if (row > 0) {
            RedisUtil.delete("yst:export:stopping:" + exportId);
        }

    }

    public void didCanceled(Long exportId) {
        this.updateState(exportId, ExportFileStateEnum.CANCELED.getKey());
    }

    public void didExpired(Long exportId) {
        this.updateState(exportId, ExportFileStateEnum.EXPIRED.getKey());
    }

    public boolean existsWaiting() {
        return this.exportRecordMapper.exists((Wrapper) Wrappers.lambdaQuery(ExportRecord.class).in(ExportRecord::getExportFileState, new Object[]{ExportFileStateEnum.WAITING.getKey(), ExportFileStateEnum.CREATING.getKey()}));
    }

    public boolean shouldStop(Long exportId) {
        return RedisUtil.getString("yst:export:stopping:" + exportId) != null;
    }

    public List<ExportRecord> listExpired() {
        return this.exportRecordMapper.selectList(Wrappers.lambdaQuery(ExportRecord.class)
                .isNotNull(ExportRecord::getFileUrl).ne(ExportRecord::getFileUrl, "")
                .le(ExportRecord::getExpiredTime, LocalDateTime.now()));
    }

    public PageVO<ExportRecordVO> page(ExportRecordQueryDTO queryDTO) {
        queryDTO.setUserId(((Long) SecureManager.getSubjectId().orElse(LeConstants.DATA_DEFAULT_LONG)).toString());
        PageMethod.startPage(queryDTO);
        List<ExportRecord> exportRecords = this.exportRecordMapper.listByParam(queryDTO);
        List<ExportRecordVO> voList = exportRecords.stream().map(ExportRecordVO::of).collect(Collectors.toList());
        return PageVO.of(exportRecords, voList);
    }

    protected int updateState(Long exportId, Integer state) {
        return !LeNumUtil.isValidId(exportId) ? 0 : this.exportRecordMapper.update(null,
                Wrappers.lambdaUpdate(ExportRecord.class)
                        .set(ExportRecord::getExportFileState, state)
                        .eq(ExportRecord::getExportId, exportId));
    }
}
