package com.njcn.gather.aireport.reportmodel.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.aireport.reportmodel.component.ReportModelFileStorageService;
import com.njcn.gather.aireport.reportmodel.component.ReportModelUserNameResolver;
import com.njcn.gather.aireport.reportmodel.mapper.ReportModelMapper;
import com.njcn.gather.aireport.reportmodel.pojo.constant.ReportModelConst;
import com.njcn.gather.aireport.reportmodel.pojo.param.ReportModelParam;
import com.njcn.gather.aireport.reportmodel.pojo.po.ReportModelPO;
import com.njcn.gather.aireport.reportmodel.pojo.vo.ReportModelVO;
import com.njcn.gather.aireport.reportmodel.service.ReportModelService;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.RequestUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Report model management service.
 */
@Service
public class ReportModelServiceImpl extends ServiceImpl<ReportModelMapper, ReportModelPO> implements ReportModelService {

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final ReportModelFileStorageService reportModelFileStorageService;
    private final ReportModelUserNameResolver reportModelUserNameResolver;

    public ReportModelServiceImpl(ReportModelFileStorageService reportModelFileStorageService,
                                  ReportModelUserNameResolver reportModelUserNameResolver) {
        this.reportModelFileStorageService = reportModelFileStorageService;
        this.reportModelUserNameResolver = reportModelUserNameResolver;
    }

    @Override
    public Page<ReportModelVO> listReportModels(ReportModelParam.QueryParam param) {
        ReportModelParam.QueryParam query = param == null ? new ReportModelParam.QueryParam() : param;
        LambdaQueryWrapper<ReportModelPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
                .like(StrUtil.isNotBlank(query.getName()), ReportModelPO::getName, query.getName())
                .eq(StrUtil.isNotBlank(query.getState()), ReportModelPO::getState, query.getState());
        if (StrUtil.isNotBlank(query.getSearchBeginTime())) {
            wrapper.ge(ReportModelPO::getUpdateTime, parseDateTime(query.getSearchBeginTime(), "开始时间格式不正确"));
        }
        if (StrUtil.isNotBlank(query.getSearchEndTime())) {
            wrapper.le(ReportModelPO::getUpdateTime, parseDateTime(query.getSearchEndTime(), "结束时间格式不正确"));
        }
        wrapper.orderByDesc(ReportModelPO::getCreateTime);
        Page<ReportModelPO> page = this.page(new Page<>(PageFactory.getPageNum(query), PageFactory.getPageSize(query)), wrapper);
        Page<ReportModelVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        Map<String, String> userNameMap = resolveUserNameMap(page.getRecords());
        result.setRecords(page.getRecords().stream().map(model -> toVO(model, userNameMap)).collect(Collectors.toList()));
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addReportModel(ReportModelParam.AddParam param, MultipartFile templateFile) {
        String name = resolveName(param == null ? null : param.getName());
        checkNameUnique(name, null);
        ReportModelFileStorageService.StoredFile storedFile = reportModelFileStorageService.save(templateFile);
        try {
            LocalDateTime now = LocalDateTime.now();
            String userId = resolveCurrentUserId();
            ReportModelPO model = new ReportModelPO();
            model.setId(UUID.randomUUID().toString());
            model.setName(name);
            model.setState(ReportModelConst.STATE_EDITING);
            model.setPath(storedFile.getPath());
            model.setFileName(storedFile.getFileName());
            model.setStatus(ReportModelConst.STATUS_NORMAL);
            model.setCreateBy(userId);
            model.setCreateTime(now);
            model.setUpdateBy(userId);
            model.setUpdateTime(now);
            return this.save(model);
        } catch (RuntimeException exception) {
            reportModelFileStorageService.deleteQuietly(storedFile.getPath());
            throw exception;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateReportModel(ReportModelParam.UpdateParam param, MultipartFile templateFile) {
        if (param == null || StrUtil.isBlank(param.getId())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板ID不能为空");
        }
        ReportModelPO model = requireNormal(param.getId());
        if (!allowEdit(model.getState())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "仅编制或退回状态的模板允许编辑");
        }
        String name = resolveName(param.getName());
        checkNameUnique(name, model.getId());
        String oldStoredPath = model.getPath();
        String newStoredPath = null;
        if (templateFile != null && !templateFile.isEmpty()) {
            ReportModelFileStorageService.StoredFile storedFile = reportModelFileStorageService.save(templateFile);
            newStoredPath = storedFile.getPath();
            model.setPath(storedFile.getPath());
            model.setFileName(storedFile.getFileName());
        }
        try {
            model.setName(name);
            model.setUpdateBy(resolveCurrentUserId());
            model.setUpdateTime(LocalDateTime.now());
            boolean updated = this.updateById(model);
            if (updated && StrUtil.isNotBlank(newStoredPath) && StrUtil.isNotBlank(oldStoredPath)
                    && !StrUtil.equals(oldStoredPath, newStoredPath)) {
                reportModelFileStorageService.deleteQuietly(oldStoredPath);
            }
            return updated;
        } catch (RuntimeException exception) {
            reportModelFileStorageService.deleteQuietly(newStoredPath);
            throw exception;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteReportModels(List<String> ids) {
        if (ids == null || ids.isEmpty()) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板ID不能为空");
        }
        List<String> validIds = ids.stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
        if (validIds.isEmpty()) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板ID不能为空");
        }
        List<ReportModelPO> models = this.lambdaQuery()
                .in(ReportModelPO::getId, validIds)
                .eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
                .list();
        boolean deleted = this.lambdaUpdate()
                .set(ReportModelPO::getStatus, ReportModelConst.STATUS_DELETED)
                .set(ReportModelPO::getState, ReportModelConst.STATE_DELETED)
                .set(ReportModelPO::getUpdateBy, resolveCurrentUserId())
                .set(ReportModelPO::getUpdateTime, LocalDateTime.now())
                .in(ReportModelPO::getId, validIds)
                .eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
                .update();
        if (deleted) {
            for (ReportModelPO model : models) {
                reportModelFileStorageService.deleteQuietly(model.getPath());
            }
        }
        return deleted;
    }

    @Override
    public ReportModelVO getReportModel(String id) {
        ReportModelPO model = requireNormal(id);
        return toVO(model, resolveUserNameMap(Arrays.asList(model)));
    }

    @Override
    public ReportModelPO requireNormal(String id) {
        if (StrUtil.isBlank(id)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板ID不能为空");
        }
        ReportModelPO model = this.lambdaQuery()
                .eq(ReportModelPO::getId, id)
                .eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
                .one();
        if (model == null) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板不存在或已删除");
        }
        return model;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitAudit(ReportModelParam.SubmitAuditParam param) {
        if (param == null || StrUtil.isBlank(param.getId())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板ID不能为空");
        }
        ReportModelPO model = requireNormal(param.getId());
        if (!ReportModelConst.STATE_EDITING.equals(model.getState())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "仅编制状态的模板允许提交审核");
        }
        model.setState(ReportModelConst.STATE_AUDITING);
        model.setCheckId(null);
        model.setCheckTime(null);
        model.setCheckResult(null);
        model.setUpdateBy(resolveCurrentUserId());
        model.setUpdateTime(LocalDateTime.now());
        return this.updateById(model);
    }

    private boolean allowEdit(String state) {
        return ReportModelConst.STATE_EDITING.equals(state) || ReportModelConst.STATE_REJECTED.equals(state);
    }

    private void checkNameUnique(String name, String excludeId) {
        LambdaQueryWrapper<ReportModelPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ReportModelPO::getName, name)
                .eq(ReportModelPO::getStatus, ReportModelConst.STATUS_NORMAL)
                .ne(StrUtil.isNotBlank(excludeId), ReportModelPO::getId, excludeId);
        if (this.count(wrapper) > 0) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板名称已存在");
        }
    }

    private String resolveName(String name) {
        String result = trimToNull(name);
        if (result == null) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板名称不能为空");
        }
        if (result.length() > 32) {
            throw new BusinessException(CommonResponseEnum.FAIL, "模板名称不能超过32个字符");
        }
        return result;
    }

    private LocalDateTime parseDateTime(String value, String errorMessage) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        try {
            return LocalDateTime.parse(value.trim(), DATE_TIME_FORMATTER);
        } catch (DateTimeParseException exception) {
            throw new BusinessException(CommonResponseEnum.FAIL, errorMessage);
        }
    }

    private ReportModelVO toVO(ReportModelPO model) {
        return toVO(model, resolveUserNameMap(Arrays.asList(model)));
    }

    private ReportModelVO toVO(ReportModelPO model, Map<String, String> userNameMap) {
        ReportModelVO vo = new ReportModelVO();
        BeanUtil.copyProperties(model, vo);
        vo.setCreateBy(resolveUserName(model.getCreateBy(), userNameMap));
        vo.setUpdateBy(resolveUserName(model.getUpdateBy(), userNameMap));
        vo.setEditorName(resolveEditorName(model, userNameMap));
        vo.setCheckerName(resolveUserName(model.getCheckId(), userNameMap));
        return vo;
    }

    /**
     * 列表“编辑人”优先取更新人，新增后若尚未产生独立更新记录，则回退到创建人。
     */
    private Map<String, String> resolveUserNameMap(List<ReportModelPO> models) {
        List<String> userIds = models.stream()
                .flatMap(model -> Arrays.asList(model.getCreateBy(), model.getUpdateBy(), model.getCheckId()).stream())
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
        return reportModelUserNameResolver.resolveUserNames(userIds);
    }

    private String resolveEditorName(ReportModelPO model, Map<String, String> userNameMap) {
        if (model == null) {
            return null;
        }
        if (StrUtil.isNotBlank(model.getUpdateBy())) {
            return resolveUserName(model.getUpdateBy(), userNameMap);
        }
        return StrUtil.isNotBlank(model.getCreateBy()) ? resolveUserName(model.getCreateBy(), userNameMap) : null;
    }

    private String resolveUserName(String userId, Map<String, String> userNameMap) {
        if (StrUtil.isBlank(userId)) {
            return null;
        }
        String userName = userNameMap == null ? null : userNameMap.get(userId);
        return StrUtil.isNotBlank(userName) ? userName : userId;
    }

    private String resolveCurrentUserId() {
        String userId = RequestUtil.getUserId();
        return StrUtil.isBlank(userId) ? null : userId;
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String text = value.trim();
        return text.isEmpty() ? null : text;
    }
}
