package com.fuint.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuint.common.dto.TaskDto;
import com.fuint.common.enums.StatusEnum;
import com.fuint.common.param.TaskPage;
import com.fuint.common.param.TaskRecordPage;
import com.fuint.common.service.BalanceService;
import com.fuint.common.service.PointService;
import com.fuint.common.service.StoreService;
import com.fuint.common.service.TaskService;
import com.fuint.common.util.DateUtil;
import com.fuint.framework.annoation.OperationServiceLog;
import com.fuint.framework.exception.BusinessCheckException;
import com.fuint.framework.pagination.PaginationResponse;
import com.fuint.repository.mapper.MtBalanceMapper;
import com.fuint.repository.mapper.MtLookWorkMapper;
import com.fuint.repository.mapper.MtOrderMapper;
import com.fuint.repository.mapper.MtPointMapper;
import com.fuint.repository.mapper.MtTaskMapper;
import com.fuint.repository.mapper.MtTaskRecordMapper;
import com.fuint.repository.mapper.MtUserMapper;
import com.fuint.repository.model.MtBalance;
import com.fuint.repository.model.MtLookWork;
import com.fuint.repository.model.MtPoint;
import com.fuint.repository.model.MtStore;
import com.fuint.repository.model.MtTask;
import com.fuint.repository.model.MtTaskRecord;
import com.fuint.repository.model.MtUser;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 任务中心服务实现
 *
 * Created by Codex
 */
@Service
@AllArgsConstructor(onConstructor_ = {@Lazy})
public class TaskServiceImpl extends ServiceImpl<MtTaskMapper, MtTask> implements TaskService {

    private static final Logger logger = LoggerFactory.getLogger(TaskServiceImpl.class);
    private static final String TASK_CODE_ORDER_COMPLETE_BY_AMOUNT = "ORDER_COMPLETE_BY_AMOUNT";
    private static final String TASK_CODE_LOOK_PUBLISH_BY_LIKE = "LOOK_PUBLISH_BY_LIKE";

    private MtTaskMapper mtTaskMapper;

    private MtTaskRecordMapper mtTaskRecordMapper;

    private MtUserMapper mtUserMapper;

    private MtPointMapper mtPointMapper;

    private MtBalanceMapper mtBalanceMapper;

    private MtOrderMapper mtOrderMapper;

    private MtLookWorkMapper mtLookWorkMapper;

    private PointService pointService;

    private BalanceService balanceService;

    private StoreService storeService;

    @Override
    public PaginationResponse<MtTask> queryTaskListByPagination(TaskPage taskPage) {
        Page<MtTask> pageHelper = PageHelper.startPage(taskPage.getPage(), taskPage.getPageSize());
        LambdaQueryWrapper<MtTask> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.ne(MtTask::getStatus, StatusEnum.DISABLE.getKey());

        if (StringUtils.isNotBlank(taskPage.getTaskName())) {
            lambdaQueryWrapper.like(MtTask::getTaskName, taskPage.getTaskName());
        }
        if (StringUtils.isNotBlank(taskPage.getTaskType())) {
            lambdaQueryWrapper.eq(MtTask::getTaskType, taskPage.getTaskType());
        }
        if (StringUtils.isNotBlank(taskPage.getStatus())) {
            lambdaQueryWrapper.eq(MtTask::getStatus, taskPage.getStatus());
        }
        if (taskPage.getMerchantId() != null) {
            lambdaQueryWrapper.eq(MtTask::getMerchantId, taskPage.getMerchantId());
        }
        if (taskPage.getStoreId() != null) {
            lambdaQueryWrapper.eq(MtTask::getStoreId, taskPage.getStoreId());
        }

        lambdaQueryWrapper.orderByAsc(MtTask::getSort).orderByDesc(MtTask::getId);
        List<MtTask> dataList = mtTaskMapper.selectList(lambdaQueryWrapper);

        PageRequest pageRequest = PageRequest.of(taskPage.getPage(), taskPage.getPageSize());
        PageImpl pageImpl = new PageImpl(dataList, pageRequest, pageHelper.getTotal());
        PaginationResponse<MtTask> paginationResponse = new PaginationResponse(pageImpl, MtTask.class);
        paginationResponse.setTotalPages(pageHelper.getPages());
        paginationResponse.setTotalElements(pageHelper.getTotal());
        paginationResponse.setContent(dataList);
        return paginationResponse;
    }

    @Override
    public PaginationResponse<Map<String, Object>> queryTaskRecordListByPagination(TaskRecordPage taskRecordPage) {
        List<Integer> matchTaskIds = null;
        if (StringUtils.isNotBlank(taskRecordPage.getTaskName())) {
            LambdaQueryWrapper<MtTask> taskWrapper = Wrappers.lambdaQuery();
            taskWrapper.select(MtTask::getId)
                    .like(MtTask::getTaskName, taskRecordPage.getTaskName())
                    .ne(MtTask::getStatus, StatusEnum.DISABLE.getKey());
            if (taskRecordPage.getMerchantId() != null) {
                taskWrapper.eq(MtTask::getMerchantId, taskRecordPage.getMerchantId());
            }
            if (taskRecordPage.getStoreId() != null) {
                taskWrapper.and(wq -> wq.eq(MtTask::getStoreId, 0).or().eq(MtTask::getStoreId, taskRecordPage.getStoreId()));
            }
            List<MtTask> taskList = mtTaskMapper.selectList(taskWrapper);
            matchTaskIds = new ArrayList<>();
            for (MtTask task : taskList) {
                matchTaskIds.add(task.getId());
            }
            if (matchTaskIds.isEmpty()) {
                return emptyTaskRecordPage(taskRecordPage);
            }
        }

        List<Integer> matchUserIds = null;
        if (StringUtils.isNotBlank(taskRecordPage.getUserNo()) || StringUtils.isNotBlank(taskRecordPage.getMobile())) {
            LambdaQueryWrapper<MtUser> userWrapper = Wrappers.lambdaQuery();
            userWrapper.select(MtUser::getId)
                    .ne(MtUser::getStatus, StatusEnum.DISABLE.getKey());
            if (taskRecordPage.getMerchantId() != null) {
                userWrapper.eq(MtUser::getMerchantId, taskRecordPage.getMerchantId());
            }
            if (StringUtils.isNotBlank(taskRecordPage.getUserNo())) {
                userWrapper.like(MtUser::getUserNo, taskRecordPage.getUserNo());
            }
            if (StringUtils.isNotBlank(taskRecordPage.getMobile())) {
                userWrapper.like(MtUser::getMobile, taskRecordPage.getMobile());
            }
            List<MtUser> userList = mtUserMapper.selectList(userWrapper);
            matchUserIds = new ArrayList<>();
            for (MtUser user : userList) {
                matchUserIds.add(user.getId());
            }
            if (matchUserIds.isEmpty()) {
                return emptyTaskRecordPage(taskRecordPage);
            }
        }

        Page<MtTaskRecord> pageHelper = PageHelper.startPage(taskRecordPage.getPage(), taskRecordPage.getPageSize());
        LambdaQueryWrapper<MtTaskRecord> wrapper = Wrappers.lambdaQuery();
        wrapper.ne(MtTaskRecord::getStatus, StatusEnum.DISABLE.getKey());
        if (taskRecordPage.getMerchantId() != null) {
            wrapper.eq(MtTaskRecord::getMerchantId, taskRecordPage.getMerchantId());
        }
        if (taskRecordPage.getStoreId() != null) {
            wrapper.and(wq -> wq.eq(MtTaskRecord::getStoreId, 0).or().eq(MtTaskRecord::getStoreId, taskRecordPage.getStoreId()));
        }
        if (taskRecordPage.getTaskId() != null && taskRecordPage.getTaskId() > 0) {
            wrapper.eq(MtTaskRecord::getTaskId, taskRecordPage.getTaskId());
        }
        if (StringUtils.isNotBlank(taskRecordPage.getTaskDate())) {
            wrapper.eq(MtTaskRecord::getTaskDate, taskRecordPage.getTaskDate());
        }
        if (matchTaskIds != null) {
            wrapper.in(MtTaskRecord::getTaskId, matchTaskIds);
        }
        if (matchUserIds != null) {
            wrapper.in(MtTaskRecord::getUserId, matchUserIds);
        }

        wrapper.orderByDesc(MtTaskRecord::getTaskDate)
                .orderByDesc(MtTaskRecord::getLastCompleteTime)
                .orderByDesc(MtTaskRecord::getId);

        List<MtTaskRecord> recordList = mtTaskRecordMapper.selectList(wrapper);
        if (recordList == null) {
            recordList = Collections.emptyList();
        }

        Set<Integer> taskIdSet = new HashSet<>();
        Set<Integer> userIdSet = new HashSet<>();
        for (MtTaskRecord record : recordList) {
            if (record.getTaskId() != null && record.getTaskId() > 0) {
                taskIdSet.add(record.getTaskId());
            }
            if (record.getUserId() != null && record.getUserId() > 0) {
                userIdSet.add(record.getUserId());
            }
        }

        Map<Integer, MtTask> taskMap = new HashMap<>();
        if (!taskIdSet.isEmpty()) {
            LambdaQueryWrapper<MtTask> taskWrapper = Wrappers.lambdaQuery();
            taskWrapper.in(MtTask::getId, taskIdSet);
            List<MtTask> taskList = mtTaskMapper.selectList(taskWrapper);
            for (MtTask task : taskList) {
                taskMap.put(task.getId(), task);
            }
        }

        Map<Integer, MtUser> userMap = new HashMap<>();
        if (!userIdSet.isEmpty()) {
            LambdaQueryWrapper<MtUser> userWrapper = Wrappers.lambdaQuery();
            userWrapper.in(MtUser::getId, userIdSet);
            List<MtUser> userList = mtUserMapper.selectList(userWrapper);
            for (MtUser user : userList) {
                userMap.put(user.getId(), user);
            }
        }

        List<Map<String, Object>> content = new ArrayList<>();
        for (MtTaskRecord record : recordList) {
            MtTask task = taskMap.get(record.getTaskId());
            MtUser user = userMap.get(record.getUserId());

            Map<String, Object> item = new HashMap<>();
            item.put("id", record.getId());
            item.put("merchantId", record.getMerchantId());
            item.put("storeId", record.getStoreId());
            item.put("taskId", record.getTaskId());
            item.put("taskCode", task == null ? "" : task.getTaskCode());
            item.put("taskName", task == null ? "" : task.getTaskName());
            item.put("taskType", task == null ? "" : task.getTaskType());
            item.put("userId", record.getUserId());
            item.put("userNo", user == null ? "" : user.getUserNo());
            item.put("name", user == null ? "" : user.getName());
            item.put("mobile", user == null ? "" : user.getMobile());
            item.put("taskDate", record.getTaskDate());
            item.put("completeNum", record.getCompleteNum() == null ? 0 : record.getCompleteNum());
            item.put("claimNum", record.getClaimNum() == null ? 0 : record.getClaimNum());
            item.put("lastCompleteTime", record.getLastCompleteTime());
            item.put("lastClaimTime", record.getLastClaimTime());
            item.put("createTime", record.getCreateTime());
            item.put("updateTime", record.getUpdateTime());
            item.put("operator", record.getOperator());
            item.put("status", record.getStatus());
            content.add(item);
        }

        PageRequest pageRequest = PageRequest.of(taskRecordPage.getPage(), taskRecordPage.getPageSize());
        PageImpl pageImpl = new PageImpl(content, pageRequest, pageHelper.getTotal());
        PaginationResponse<Map<String, Object>> paginationResponse = new PaginationResponse(pageImpl, Map.class);
        paginationResponse.setTotalPages(pageHelper.getPages());
        paginationResponse.setTotalElements(pageHelper.getTotal());
        paginationResponse.setContent(content);
        return paginationResponse;
    }

    @Override
    @OperationServiceLog(description = "新增任务")
    public MtTask addTask(TaskDto taskDto) throws BusinessCheckException {
        MtTask mtTask = new MtTask();
        BeanUtils.copyProperties(taskDto, mtTask);

        Integer storeId = taskDto.getStoreId() == null ? 0 : taskDto.getStoreId();
        if (taskDto.getMerchantId() == null || taskDto.getMerchantId() <= 0) {
            MtStore mtStore = storeService.queryStoreById(storeId);
            if (mtStore != null) {
                mtTask.setMerchantId(mtStore.getMerchantId());
            }
        }
        if (mtTask.getMerchantId() == null || mtTask.getMerchantId() <= 0) {
            throw new BusinessCheckException("新增任务失败：所属商户不能为空！");
        }
        if (StringUtils.isBlank(mtTask.getTaskCode())) {
            throw new BusinessCheckException("任务编码不能为空");
        }
        if (StringUtils.isBlank(mtTask.getTaskName())) {
            throw new BusinessCheckException("任务名称不能为空");
        }

        LambdaQueryWrapper<MtTask> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MtTask::getMerchantId, mtTask.getMerchantId())
                .eq(MtTask::getStoreId, storeId)
                .eq(MtTask::getTaskCode, mtTask.getTaskCode())
                .ne(MtTask::getStatus, StatusEnum.DISABLE.getKey());
        if (mtTaskMapper.selectCount(wrapper) > 0) {
            throw new BusinessCheckException("任务编码已存在，请更换后重试");
        }

        mtTask.setStoreId(storeId);
        mtTask.setRewardPoint(mtTask.getRewardPoint() == null ? 0 : mtTask.getRewardPoint());
        mtTask.setRewardBalance(mtTask.getRewardBalance() == null ? BigDecimal.ZERO : mtTask.getRewardBalance());
        mtTask.setPointThreshold(mtTask.getPointThreshold() == null ? 0 : mtTask.getPointThreshold());
        mtTask.setBalanceThreshold(mtTask.getBalanceThreshold() == null ? BigDecimal.ZERO : mtTask.getBalanceThreshold());
        mtTask.setTargetGoodsIds(normalizeGoodsIds(mtTask.getTargetGoodsIds()));
        mtTask.setCompleteThreshold(mtTask.getCompleteThreshold() == null ? BigDecimal.ZERO : mtTask.getCompleteThreshold());
        mtTask.setDailyLimit(mtTask.getDailyLimit() == null ? 1 : mtTask.getDailyLimit());
        mtTask.setSort(mtTask.getSort() == null ? 0 : mtTask.getSort());
        mtTask.setStatus(StringUtils.isBlank(mtTask.getStatus()) ? StatusEnum.ENABLED.getKey() : mtTask.getStatus());
        mtTask.setCreateTime(new Date());
        mtTask.setUpdateTime(new Date());

        Integer id = mtTaskMapper.insert(mtTask);
        if (id > 0) {
            return mtTask;
        }
        logger.error("新增任务失败.");
        throw new BusinessCheckException("抱歉，新增任务失败！");
    }

    @Override
    public MtTask queryTaskById(Integer id) {
        return mtTaskMapper.selectById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @OperationServiceLog(description = "更新任务")
    public MtTask updateTask(TaskDto taskDto) throws BusinessCheckException {
        MtTask mtTask = queryTaskById(taskDto.getId());
        if (mtTask == null) {
            throw new BusinessCheckException("该任务不存在");
        }

        Integer targetStoreId = taskDto.getStoreId() == null ? mtTask.getStoreId() : taskDto.getStoreId();
        if (StringUtils.isNotBlank(taskDto.getTaskCode())) {
            LambdaQueryWrapper<MtTask> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(MtTask::getMerchantId, mtTask.getMerchantId())
                    .eq(MtTask::getStoreId, targetStoreId)
                    .eq(MtTask::getTaskCode, taskDto.getTaskCode())
                    .ne(MtTask::getStatus, StatusEnum.DISABLE.getKey())
                    .ne(MtTask::getId, mtTask.getId());
            if (mtTaskMapper.selectCount(wrapper) > 0) {
                throw new BusinessCheckException("任务编码已存在，请更换后重试");
            }
            mtTask.setTaskCode(taskDto.getTaskCode());
        }
        if (StringUtils.isNotBlank(taskDto.getTaskName())) {
            mtTask.setTaskName(taskDto.getTaskName());
        }
        if (taskDto.getStoreId() != null) {
            mtTask.setStoreId(targetStoreId);
        }
        if (StringUtils.isNotBlank(taskDto.getTaskType())) {
            mtTask.setTaskType(taskDto.getTaskType());
        }
        if (taskDto.getTaskDesc() != null) {
            mtTask.setTaskDesc(taskDto.getTaskDesc());
        }
        if (taskDto.getJumpUrl() != null) {
            mtTask.setJumpUrl(taskDto.getJumpUrl());
        }
        if (taskDto.getRewardPoint() != null) {
            mtTask.setRewardPoint(taskDto.getRewardPoint());
        }
        if (taskDto.getRewardBalance() != null) {
            mtTask.setRewardBalance(taskDto.getRewardBalance());
        }
        if (taskDto.getPointThreshold() != null) {
            mtTask.setPointThreshold(taskDto.getPointThreshold());
        }
        if (taskDto.getBalanceThreshold() != null) {
            mtTask.setBalanceThreshold(taskDto.getBalanceThreshold());
        }
        if (taskDto.getTargetGoodsIds() != null) {
            mtTask.setTargetGoodsIds(normalizeGoodsIds(taskDto.getTargetGoodsIds()));
        }
        if (taskDto.getCompleteThreshold() != null) {
            mtTask.setCompleteThreshold(taskDto.getCompleteThreshold());
        }
        if (taskDto.getDailyLimit() != null) {
            mtTask.setDailyLimit(taskDto.getDailyLimit());
        }
        if (taskDto.getSort() != null) {
            mtTask.setSort(taskDto.getSort());
        }
        if (taskDto.getOperator() != null) {
            mtTask.setOperator(taskDto.getOperator());
        }
        if (taskDto.getStatus() != null) {
            mtTask.setStatus(taskDto.getStatus());
        }

        mtTask.setUpdateTime(new Date());
        mtTaskMapper.updateById(mtTask);
        return mtTask;
    }

    @Override
    public List<MtTask> queryTaskListByParams(Map<String, Object> params) {
        String status = params.get("status") == null ? StatusEnum.ENABLED.getKey() : params.get("status").toString();
        String storeId = params.get("storeId") == null ? "" : params.get("storeId").toString();
        String merchantId = params.get("merchantId") == null ? "" : params.get("merchantId").toString();

        LambdaQueryWrapper<MtTask> lambdaQueryWrapper = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(status)) {
            lambdaQueryWrapper.eq(MtTask::getStatus, status);
        }
        if (StringUtils.isNotBlank(merchantId)) {
            lambdaQueryWrapper.eq(MtTask::getMerchantId, merchantId);
        }
        if (StringUtils.isNotBlank(storeId)) {
            lambdaQueryWrapper.and(wq -> wq
                    .eq(MtTask::getStoreId, 0)
                    .or()
                    .eq(MtTask::getStoreId, storeId));
        }

        lambdaQueryWrapper.orderByAsc(MtTask::getSort).orderByDesc(MtTask::getId);
        return mtTaskMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<Map<String, Object>> queryClientTaskList(Integer merchantId, Integer storeId, Integer userId) {
        Map<String, Object> params = new HashMap<>();
        params.put("merchantId", merchantId);
        params.put("storeId", storeId);
        params.put("status", StatusEnum.ENABLED.getKey());
        List<MtTask> taskList = this.queryTaskListByParams(params);

        Map<Integer, MtTaskRecord> recordMap = new HashMap<>();
        if (userId != null && userId > 0) {
            String taskDate = DateUtil.getNow(DateUtil.PATTERN_ISO_DATE);
            LambdaQueryWrapper<MtTaskRecord> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(MtTaskRecord::getMerchantId, merchantId)
                    .eq(MtTaskRecord::getUserId, userId)
                    .eq(MtTaskRecord::getTaskDate, taskDate)
                    .eq(MtTaskRecord::getStatus, StatusEnum.ENABLED.getKey());
            List<MtTaskRecord> recordList = mtTaskRecordMapper.selectList(wrapper);
            for (MtTaskRecord record : recordList) {
                recordMap.put(record.getTaskId(), record);
            }
        }

        List<Map<String, Object>> result = new ArrayList<>();
        for (MtTask task : taskList) {
            MtTaskRecord record = recordMap.get(task.getId());
            int completeNum = record == null || record.getCompleteNum() == null ? 0 : record.getCompleteNum();
            int claimNum = record == null || record.getClaimNum() == null ? 0 : record.getClaimNum();
            int dailyLimit = task.getDailyLimit() == null ? 1 : task.getDailyLimit();
            int rewardPoint = task.getRewardPoint() == null ? 0 : task.getRewardPoint();
            BigDecimal rewardBalance = task.getRewardBalance() == null ? BigDecimal.ZERO : task.getRewardBalance();
            BigDecimal completeThreshold = task.getCompleteThreshold() == null ? BigDecimal.ZERO : task.getCompleteThreshold();
            Integer pointThreshold = task.getPointThreshold() == null ? 0 : task.getPointThreshold();
            BigDecimal balanceThreshold = task.getBalanceThreshold() == null ? BigDecimal.ZERO : task.getBalanceThreshold();

            Map<String, Object> item = new HashMap<>();
            item.put("id", task.getId());
            item.put("taskCode", task.getTaskCode());
            item.put("taskName", task.getTaskName());
            item.put("taskType", task.getTaskType());
            item.put("taskDesc", task.getTaskDesc());
            item.put("jumpUrl", task.getJumpUrl());
            item.put("rewardPoint", rewardPoint);
            item.put("rewardBalance", rewardBalance);
            item.put("pointThreshold", pointThreshold);
            item.put("balanceThreshold", balanceThreshold);
            item.put("targetGoodsIds", task.getTargetGoodsIds());
            item.put("completeThreshold", completeThreshold);
            item.put("dailyLimit", dailyLimit);
            item.put("sort", task.getSort());
            item.put("completeNum", completeNum);
            item.put("claimNum", claimNum);
            item.put("canComplete", completeNum < dailyLimit);
            item.put("canClaim", claimNum < completeNum);
            item.put("finished", claimNum >= dailyLimit);
            result.add(item);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> completeTask(Integer merchantId, Integer storeId, Integer userId, Integer taskId, String operator) throws BusinessCheckException {
        MtTask taskInfo = queryTaskByScope(merchantId, storeId, taskId);
        if (taskInfo == null) {
            throw new BusinessCheckException("任务不存在或已下线");
        }
        int dailyLimit = taskInfo.getDailyLimit() == null ? 1 : taskInfo.getDailyLimit();
        String taskDate = DateUtil.getNow(DateUtil.PATTERN_ISO_DATE);
        Date nowTime = new Date();
        MtTaskRecord record = getOrCreateRecord(merchantId, storeId, userId, taskId, taskDate, operator, nowTime);
        validateTaskCompleteCondition(taskInfo, merchantId, storeId, userId, record, taskDate);

        int effect = mtTaskRecordMapper.incrCompleteNum(record.getId(), dailyLimit, nowTime);
        if (effect < 1) {
            throw new BusinessCheckException("今日任务已达上限");
        }
        MtTaskRecord latest = mtTaskRecordMapper.selectById(record.getId());
        return buildTaskProgress(taskInfo, latest);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> claimTask(Integer merchantId, Integer storeId, Integer userId, Integer taskId, String operator) throws BusinessCheckException {
        MtTask taskInfo = queryTaskByScope(merchantId, storeId, taskId);
        if (taskInfo == null) {
            throw new BusinessCheckException("任务不存在或已下线");
        }
        int dailyLimit = taskInfo.getDailyLimit() == null ? 1 : taskInfo.getDailyLimit();
        int rewardPoint = taskInfo.getRewardPoint() == null ? 0 : taskInfo.getRewardPoint();
        BigDecimal rewardBalance = taskInfo.getRewardBalance() == null ? BigDecimal.ZERO : taskInfo.getRewardBalance();
        if (rewardPoint <= 0 && rewardBalance.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessCheckException("该任务未配置奖励");
        }

        String taskDate = DateUtil.getNow(DateUtil.PATTERN_ISO_DATE);
        Date nowTime = new Date();
        MtTaskRecord record = getOrCreateRecord(merchantId, storeId, userId, taskId, taskDate, operator, nowTime);
        int completeNum = record.getCompleteNum() == null ? 0 : record.getCompleteNum();
        int claimNum = record.getClaimNum() == null ? 0 : record.getClaimNum();

        if (claimNum >= dailyLimit) {
            throw new BusinessCheckException("今日奖励已领取完");
        }
        if (claimNum >= completeNum) {
            throw new BusinessCheckException("请先完成任务后再领取");
        }

        int nextClaimNum = claimNum + 1;
        String orderSn = buildOrderSn(taskId, userId, taskDate, nextClaimNum);
        LambdaQueryWrapper<MtPoint> pointWrapper = Wrappers.lambdaQuery();
        pointWrapper.eq(MtPoint::getUserId, userId)
                .eq(MtPoint::getOrderSn, orderSn)
                .eq(MtPoint::getStatus, StatusEnum.ENABLED.getKey());
        MtPoint existed = mtPointMapper.selectOne(pointWrapper);
        if (existed != null) {
            if (claimNum < nextClaimNum) {
                mtTaskRecordMapper.incrClaimNumIfMatch(record.getId(), claimNum, nowTime);
                record = mtTaskRecordMapper.selectById(record.getId());
            }
            return buildTaskProgress(taskInfo, record);
        }

        int effect = mtTaskRecordMapper.incrClaimNumIfMatch(record.getId(), claimNum, nowTime);
        if (effect < 1) {
            MtTaskRecord latest = mtTaskRecordMapper.selectById(record.getId());
            int latestClaimNum = latest.getClaimNum() == null ? 0 : latest.getClaimNum();
            if (latestClaimNum >= nextClaimNum) {
                return buildTaskProgress(taskInfo, latest);
            }
            throw new BusinessCheckException("领取过于频繁，请稍后重试");
        }

        // 幂等发积分：同任务同日期同轮次固定 orderSn，只会发一次
        pointWrapper = Wrappers.lambdaQuery();
        pointWrapper.eq(MtPoint::getUserId, userId)
                .eq(MtPoint::getOrderSn, orderSn)
                .eq(MtPoint::getStatus, StatusEnum.ENABLED.getKey());
        existed = mtPointMapper.selectOne(pointWrapper);
        if (existed == null) {
            MtPoint reqPointDto = new MtPoint();
            reqPointDto.setUserId(userId);
            reqPointDto.setOrderSn(orderSn);
            reqPointDto.setAmount(rewardPoint);
            reqPointDto.setDescription("任务奖励：" + taskInfo.getTaskName() + " +" + rewardPoint + "积分");
            reqPointDto.setOperator(operator);
            if (rewardPoint > 0) {
                pointService.addPoint(reqPointDto);
            }
        }

        // 幂等发余额：同任务同日期同轮次固定 orderSn，只会发一次
        if (rewardBalance.compareTo(BigDecimal.ZERO) > 0) {
            LambdaQueryWrapper<MtBalance> balanceWrapper = Wrappers.lambdaQuery();
            balanceWrapper.eq(MtBalance::getUserId, userId)
                    .eq(MtBalance::getOrderSn, orderSn)
                    .eq(MtBalance::getStatus, StatusEnum.ENABLED.getKey());
            MtBalance balanceExisted = mtBalanceMapper.selectOne(balanceWrapper);
            if (balanceExisted == null) {
                MtBalance reqBalance = new MtBalance();
                reqBalance.setUserId(userId);
                reqBalance.setOrderSn(orderSn);
                reqBalance.setAmount(rewardBalance);
                reqBalance.setDescription("任务奖励：" + taskInfo.getTaskName() + " +" + rewardBalance + "余额");
                reqBalance.setOperator(operator);
                balanceService.addBalance(reqBalance, true);
            }
        }

        MtTaskRecord latest = mtTaskRecordMapper.selectById(record.getId());
        return buildTaskProgress(taskInfo, latest);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @OperationServiceLog(description = "导入公共任务")
    public Map<String, Object> importPublicTask(Integer merchantId, Integer storeId, List<Integer> taskIds, String operator) throws BusinessCheckException {
        if (merchantId == null || merchantId <= 0) {
            throw new BusinessCheckException("当前账号未绑定商户，无法导入");
        }
        if (taskIds == null || taskIds.isEmpty()) {
            throw new BusinessCheckException("请选择要导入的任务");
        }

        Integer targetStoreId = storeId == null ? 0 : storeId;
        if (targetStoreId > 0) {
            MtStore mtStore = storeService.queryStoreById(targetStoreId);
            if (mtStore == null || !merchantId.equals(mtStore.getMerchantId())) {
                throw new BusinessCheckException("目标店铺不存在或不属于当前商户");
            }
        }

        List<Integer> validTaskIds = new ArrayList<>();
        Set<Integer> uniqueSet = new HashSet<>();
        for (Integer taskId : taskIds) {
            if (taskId == null || taskId <= 0 || uniqueSet.contains(taskId)) {
                continue;
            }
            uniqueSet.add(taskId);
            validTaskIds.add(taskId);
        }
        if (validTaskIds.isEmpty()) {
            throw new BusinessCheckException("请选择有效的任务");
        }

        LambdaQueryWrapper<MtTask> sourceWrapper = Wrappers.lambdaQuery();
        sourceWrapper.eq(MtTask::getMerchantId, 0)
                .eq(MtTask::getStoreId, 0)
                .in(MtTask::getId, validTaskIds)
                .ne(MtTask::getStatus, StatusEnum.DISABLE.getKey());
        List<MtTask> sourceList = mtTaskMapper.selectList(sourceWrapper);
        if (sourceList == null || sourceList.isEmpty()) {
            throw new BusinessCheckException("未找到可导入的公共任务");
        }
        Map<Integer, MtTask> sourceMap = new HashMap<>();
        for (MtTask source : sourceList) {
            sourceMap.put(source.getId(), source);
        }

        int importedCount = 0;
        int skippedCount = 0;
        List<Integer> importedTaskIds = new ArrayList<>();
        Date now = new Date();
        for (Integer taskId : validTaskIds) {
            MtTask source = sourceMap.get(taskId);
            if (source == null) {
                skippedCount++;
                continue;
            }

            LambdaQueryWrapper<MtTask> existedWrapper = Wrappers.lambdaQuery();
            existedWrapper.eq(MtTask::getMerchantId, merchantId)
                    .eq(MtTask::getStoreId, targetStoreId)
                    .eq(MtTask::getTaskCode, source.getTaskCode())
                    .ne(MtTask::getStatus, StatusEnum.DISABLE.getKey());
            if (mtTaskMapper.selectCount(existedWrapper) > 0) {
                skippedCount++;
                continue;
            }

            MtTask target = copyTaskForImport(source, merchantId, targetStoreId, operator, now);
            mtTaskMapper.insert(target);
            importedCount++;
            importedTaskIds.add(target.getId());
        }

        Map<String, Object> result = new HashMap<>();
        result.put("importedCount", importedCount);
        result.put("skippedCount", skippedCount);
        result.put("importedTaskIds", importedTaskIds);
        return result;
    }

    private MtTask queryTaskByScope(Integer merchantId, Integer storeId, Integer taskId) {
        LambdaQueryWrapper<MtTask> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MtTask::getId, taskId)
                .eq(MtTask::getMerchantId, merchantId)
                .eq(MtTask::getStatus, StatusEnum.ENABLED.getKey());
        if (storeId != null && storeId > 0) {
            wrapper.and(wq -> wq.eq(MtTask::getStoreId, 0).or().eq(MtTask::getStoreId, storeId));
        }
        return mtTaskMapper.selectOne(wrapper);
    }

    private PaginationResponse<Map<String, Object>> emptyTaskRecordPage(TaskRecordPage taskRecordPage) {
        List<Map<String, Object>> content = new ArrayList<>();
        PageRequest pageRequest = PageRequest.of(taskRecordPage.getPage(), taskRecordPage.getPageSize());
        PageImpl pageImpl = new PageImpl(content, pageRequest, 0);
        PaginationResponse<Map<String, Object>> paginationResponse = new PaginationResponse(pageImpl, Map.class);
        paginationResponse.setTotalPages(0);
        paginationResponse.setTotalElements(0);
        paginationResponse.setContent(content);
        return paginationResponse;
    }

    private MtTaskRecord getOrCreateRecord(Integer merchantId, Integer storeId, Integer userId, Integer taskId, String taskDate, String operator, Date nowTime) {
        LambdaQueryWrapper<MtTaskRecord> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MtTaskRecord::getMerchantId, merchantId)
                .eq(MtTaskRecord::getTaskId, taskId)
                .eq(MtTaskRecord::getUserId, userId)
                .eq(MtTaskRecord::getTaskDate, taskDate)
                .eq(MtTaskRecord::getStatus, StatusEnum.ENABLED.getKey());
        MtTaskRecord record = mtTaskRecordMapper.selectOne(wrapper);
        if (record != null) {
            return record;
        }
        MtTaskRecord insertItem = new MtTaskRecord();
        insertItem.setMerchantId(merchantId);
        insertItem.setStoreId(storeId == null ? 0 : storeId);
        insertItem.setTaskId(taskId);
        insertItem.setUserId(userId);
        insertItem.setTaskDate(taskDate);
        insertItem.setCompleteNum(0);
        insertItem.setClaimNum(0);
        insertItem.setCreateTime(nowTime);
        insertItem.setUpdateTime(nowTime);
        insertItem.setOperator(operator);
        insertItem.setStatus(StatusEnum.ENABLED.getKey());
        try {
            mtTaskRecordMapper.insert(insertItem);
            return insertItem;
        } catch (Exception e) {
            // 并发下可能已创建，回查最新记录
            return mtTaskRecordMapper.selectOne(wrapper);
        }
    }

    private String buildOrderSn(Integer taskId, Integer userId, String taskDate, Integer seq) {
        return "TASK-" + taskId + "-" + userId + "-" + taskDate.replace("-", "") + "-" + seq;
    }

    private Map<String, Object> buildTaskProgress(MtTask taskInfo, MtTaskRecord record) {
        int completeNum = record == null || record.getCompleteNum() == null ? 0 : record.getCompleteNum();
        int claimNum = record == null || record.getClaimNum() == null ? 0 : record.getClaimNum();
        int dailyLimit = taskInfo.getDailyLimit() == null ? 1 : taskInfo.getDailyLimit();

        Map<String, Object> result = new HashMap<>();
        result.put("taskId", taskInfo.getId());
        result.put("completeNum", completeNum);
        result.put("claimNum", claimNum);
        result.put("dailyLimit", dailyLimit);
        result.put("canComplete", completeNum < dailyLimit);
        result.put("canClaim", claimNum < completeNum);
        result.put("finished", claimNum >= dailyLimit);
        return result;
    }

    private void validateTaskCompleteCondition(MtTask taskInfo,
                                               Integer merchantId,
                                               Integer storeId,
                                               Integer userId,
                                               MtTaskRecord record,
                                               String taskDate) throws BusinessCheckException {
        if (taskInfo == null) {
            return;
        }
        String taskCode = taskInfo.getTaskCode() == null ? "" : taskInfo.getTaskCode();
        String taskType = taskInfo.getTaskType() == null ? "" : taskInfo.getTaskType();
        if ("ORDER".equals(taskType) || TASK_CODE_ORDER_COMPLETE_BY_AMOUNT.equals(taskCode)) {
            validateOrderTask(taskInfo, merchantId, storeId, userId, record, taskDate);
            return;
        }
        if ("LOOK".equals(taskType) || TASK_CODE_LOOK_PUBLISH_BY_LIKE.equals(taskCode)) {
            validateLookLikeTask(taskInfo, merchantId, storeId, userId, record, taskDate);
        }
    }

    private void validateOrderTask(MtTask taskInfo,
                                   Integer merchantId,
                                   Integer storeId,
                                   Integer userId,
                                   MtTaskRecord record,
                                   String taskDate) throws BusinessCheckException {
        Integer pointThreshold = taskInfo.getPointThreshold() == null ? 0 : taskInfo.getPointThreshold();
        BigDecimal balanceThreshold = taskInfo.getBalanceThreshold() == null ? BigDecimal.ZERO : taskInfo.getBalanceThreshold();
        List<Integer> goodsIdList = parseGoodsIds(taskInfo.getTargetGoodsIds());

        Integer pointThresholdFilter = pointThreshold > 0 ? pointThreshold : null;
        BigDecimal balanceThresholdFilter = balanceThreshold.compareTo(BigDecimal.ZERO) > 0 ? balanceThreshold : null;
        Date startTime = getDateStart(taskDate);
        Date endTime = getDateEnd(taskDate);

        Integer matchNum = mtOrderMapper.countCompleteTaskOrder(merchantId, storeId, userId, startTime, endTime,
                pointThresholdFilter, balanceThresholdFilter, goodsIdList);
        int completeNum = record == null || record.getCompleteNum() == null ? 0 : record.getCompleteNum();
        if (matchNum == null || matchNum <= completeNum) {
            throw new BusinessCheckException("未达到完成条件：今日订单未满足阈值或指定商品条件");
        }
    }

    private void validateLookLikeTask(MtTask taskInfo,
                                      Integer merchantId,
                                      Integer storeId,
                                      Integer userId,
                                      MtTaskRecord record,
                                      String taskDate) throws BusinessCheckException {
        BigDecimal threshold = taskInfo.getCompleteThreshold() == null ? BigDecimal.ZERO : taskInfo.getCompleteThreshold();
        if (threshold.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessCheckException("请先配置作品点赞阈值");
        }
        int thresholdLike = threshold.intValue();
        Date startTime = getDateStart(taskDate);
        Date endTime = getDateEnd(taskDate);

        LambdaQueryWrapper<MtLookWork> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MtLookWork::getMerchantId, merchantId)
                .eq(MtLookWork::getUserId, userId)
                .eq(MtLookWork::getStatus, StatusEnum.ENABLED.getKey())
                .ge(MtLookWork::getCreateTime, startTime)
                .lt(MtLookWork::getCreateTime, endTime)
                .ge(MtLookWork::getLikeNum, thresholdLike);
        if (storeId != null && storeId > 0) {
            wrapper.and(wq -> wq.eq(MtLookWork::getStoreId, 0).or().eq(MtLookWork::getStoreId, storeId));
        }
        Integer matchNum = mtLookWorkMapper.selectCount(wrapper);
        int completeNum = record == null || record.getCompleteNum() == null ? 0 : record.getCompleteNum();
        if (matchNum == null || matchNum <= completeNum) {
            throw new BusinessCheckException("未达到完成条件：今日发布作品点赞数不足");
        }
    }

    private Date getDateStart(String taskDate) {
        Date date;
        try {
            date = DateUtil.parseDate(taskDate, DateUtil.PATTERN_ISO_DATE);
        } catch (Exception e) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    private Date getDateEnd(String taskDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(getDateStart(taskDate));
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    private MtTask copyTaskForImport(MtTask source, Integer merchantId, Integer storeId, String operator, Date now) {
        MtTask target = new MtTask();
        target.setMerchantId(merchantId);
        target.setStoreId(storeId == null ? 0 : storeId);
        target.setTaskCode(source.getTaskCode());
        target.setTaskName(source.getTaskName());
        target.setTaskType(source.getTaskType());
        target.setTaskDesc(source.getTaskDesc());
        target.setJumpUrl(source.getJumpUrl());
        target.setRewardPoint(source.getRewardPoint() == null ? 0 : source.getRewardPoint());
        target.setRewardBalance(source.getRewardBalance() == null ? BigDecimal.ZERO : source.getRewardBalance());
        target.setPointThreshold(source.getPointThreshold() == null ? 0 : source.getPointThreshold());
        target.setBalanceThreshold(source.getBalanceThreshold() == null ? BigDecimal.ZERO : source.getBalanceThreshold());
        target.setTargetGoodsIds(normalizeGoodsIds(source.getTargetGoodsIds()));
        target.setCompleteThreshold(source.getCompleteThreshold() == null ? BigDecimal.ZERO : source.getCompleteThreshold());
        target.setDailyLimit(source.getDailyLimit() == null ? 1 : source.getDailyLimit());
        target.setSort(source.getSort() == null ? 0 : source.getSort());
        target.setOperator(operator);
        target.setStatus(StringUtils.isBlank(source.getStatus()) ? StatusEnum.ENABLED.getKey() : source.getStatus());
        target.setCreateTime(now);
        target.setUpdateTime(now);
        return target;
    }

    private String normalizeGoodsIds(String goodsIds) {
        List<Integer> goodsIdList = parseGoodsIds(goodsIds);
        if (goodsIdList.isEmpty()) {
            return "";
        }
        List<String> result = new ArrayList<>();
        for (Integer goodsId : goodsIdList) {
            result.add(String.valueOf(goodsId));
        }
        return String.join(",", result);
    }

    private List<Integer> parseGoodsIds(String goodsIds) {
        List<Integer> result = new ArrayList<>();
        if (StringUtils.isBlank(goodsIds)) {
            return result;
        }
        String[] parts = goodsIds.split("[,，]");
        Set<Integer> uniqueSet = new HashSet<>();
        for (String part : parts) {
            if (StringUtils.isBlank(part)) {
                continue;
            }
            String value = part.trim();
            if (!value.matches("\\d+")) {
                continue;
            }
            Integer goodsId = Integer.parseInt(value);
            if (goodsId > 0 && !uniqueSet.contains(goodsId)) {
                uniqueSet.add(goodsId);
                result.add(goodsId);
            }
        }
        return result;
    }
}
