package me.qwq.doghouse.service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;

import me.qwq.doghouse.dao.MailTaskMapper;
import me.qwq.doghouse.entity.MailTask;
import me.qwq.doghouse.enums.MailTaskStatusEnum;
import me.qwq.doghouse.enums.MailTaskTypeEnum;

@Service
public class MailTaskService extends BaseServiceImpl<MailTaskMapper, MailTask> {

    public static final int MAX_ATTEMPTS = 5;
    private static final int DEFAULT_BATCH_SIZE = 10;

    @Transactional
    public void replaceReplyTasks(Long commentId, Collection<String> recipients, LocalDateTime sendAfter) {
        cancelOutstandingTasks(commentId, MailTaskTypeEnum.REPLY_SUBSCRIPTION);
        if (recipients == null || recipients.isEmpty()) {
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        List<MailTask> tasks = recipients.stream()
                .map(recipient -> new MailTask()
                        .setTaskType(MailTaskTypeEnum.REPLY_SUBSCRIPTION)
                        .setCommentId(commentId)
                        .setRecipient(recipient)
                        .setStatus(MailTaskStatusEnum.PENDING)
                        .setSendAfter(sendAfter)
                        .setAttempts(0)
                        .setCreateTime(now)
                        .setUpdateTime(now))
                .toList();
        saveBatch(tasks);
    }

    /**
     * 入队新评论的管理员邮件通知
     * @param commentId 评论 ID
     * @param recipient 收件人地址
     */
    public void enqueueAdminTask(Long commentId, String recipient) {
        if (commentId == null || recipient == null || recipient.isBlank()) {
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        save(new MailTask()
                .setTaskType(MailTaskTypeEnum.ADMIN_NOTIFICATION)
                .setCommentId(commentId)
                .setRecipient(recipient.toLowerCase())
                .setStatus(MailTaskStatusEnum.PENDING)
                .setSendAfter(now.plusSeconds(1))
                .setAttempts(0)
                .setCreateTime(now)
                .setUpdateTime(now));
    }

    /**
     * 取消未完成的评论邮件任务
     * @param commentId 指定评论 id
     * @param taskType 邮件任务类型
     * @return 是否成功取消指定条件的任务
     */
    public boolean cancelOutstandingTasks(Long commentId, MailTaskTypeEnum taskType) {
        if (commentId == null || taskType == null) {
            return false;
        }

        LambdaQueryWrapper<MailTask> query = outstandingQuery(commentId, taskType);
        boolean exists = count(query) > 0;
        if (!exists) {
            return false;
        }

        return update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getCommentId, commentId)
                .eq(MailTask::getTaskType, taskType)
                .in(MailTask::getStatus,
                        MailTaskStatusEnum.PENDING,
                        MailTaskStatusEnum.FAILED,
                        MailTaskStatusEnum.SENDING)
                .set(MailTask::getStatus, MailTaskStatusEnum.CANCELLED)
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }

    /**
     * 重设订阅邮件任务延迟, 会将邮件类型为订阅邮件、邮件状态为 PEDING 和 FAILED 的重置为 PEDING, 并重设延迟
     * @param delay 延迟
     */
    public void resetReplyTaskDelay(Duration delay) {
        LocalDateTime sendAfter = LocalDateTime.now().plus(delay);
        update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getTaskType, MailTaskTypeEnum.REPLY_SUBSCRIPTION)
                .in(MailTask::getStatus, MailTaskStatusEnum.PENDING, MailTaskStatusEnum.FAILED)
                .set(MailTask::getStatus, MailTaskStatusEnum.PENDING)
                .set(MailTask::getSendAfter, sendAfter)
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }

    /**
     * 查询当前满足发送条件的邮件任务, 详见查询条件
     * @return 当前满足发送条件的邮件任务的列表
     */
    public List<MailTask> pollDueTasks() {
        return list(new LambdaQueryWrapper<MailTask>()
                .in(MailTask::getStatus, MailTaskStatusEnum.PENDING, MailTaskStatusEnum.FAILED)
                .le(MailTask::getSendAfter, LocalDateTime.now())
                .lt(MailTask::getAttempts, MAX_ATTEMPTS)
                .orderByAsc(MailTask::getSendAfter)
                .last("LIMIT " + DEFAULT_BATCH_SIZE));
    }
    
    /**
     * 将指定邮件任务标记为发送中
     * @param task
     * @return
     */
    public boolean markSending(MailTask task) {
        if (task == null || task.getId() == null) {
            return false;
        }
        return update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getId, task.getId())
                .in(MailTask::getStatus, MailTaskStatusEnum.PENDING, MailTaskStatusEnum.FAILED)
                .set(MailTask::getStatus, MailTaskStatusEnum.SENDING)
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }

    /**
     * 将指定邮件任务标记为已发送
     * @param task
     */
    public void markSent(MailTask task) {
        update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getId, task.getId())
                .set(MailTask::getStatus, MailTaskStatusEnum.SENT)
                .set(MailTask::getLastError, null)
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }

    /**
     * 将指定邮件任务标记为取消
     * @param task
     * @param reason
     */
    public void markCancelled(MailTask task, String reason) {
        update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getId, task.getId())
                .set(MailTask::getStatus, MailTaskStatusEnum.CANCELLED)
                .set(MailTask::getLastError, trimError(reason))
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }
    
    /**
     * 将指定邮件任务标记为失败
     * @param task
     * @param e
     */
    public void markFailed(MailTask task, Exception e) {
        int attempts = task.getAttempts() == null ? 0 : task.getAttempts();
        int nextAttempts = attempts + 1;
        MailTaskStatusEnum status = nextAttempts >= MAX_ATTEMPTS ?
                MailTaskStatusEnum.FAILED :
                MailTaskStatusEnum.PENDING;
        long delayMinutes = Math.min(60L, 1L << Math.min(nextAttempts, 6));

        update(new LambdaUpdateWrapper<MailTask>()
                .eq(MailTask::getId, task.getId())
                .set(MailTask::getStatus, status)
                .set(MailTask::getAttempts, nextAttempts)
                .set(MailTask::getLastError, trimError(e.getMessage()))
                .set(MailTask::getSendAfter, LocalDateTime.now().plusMinutes(delayMinutes))
                .set(MailTask::getUpdateTime, LocalDateTime.now()));
    }

    /**
     * 查询指定评论 id 下是否有待处理邮件任务
     * @param commentId
     * @return 是否有待处理邮件任务
     */
    public boolean hasOutstandingReplyTasks(Long commentId) {
        return count(outstandingQuery(commentId, MailTaskTypeEnum.REPLY_SUBSCRIPTION)) > 0;
    }

    private LambdaQueryWrapper<MailTask> outstandingQuery(Long commentId, MailTaskTypeEnum taskType) {
        return new LambdaQueryWrapper<MailTask>()
                .eq(MailTask::getCommentId, commentId)
                .eq(MailTask::getTaskType, taskType)
                .in(MailTask::getStatus,
                        MailTaskStatusEnum.PENDING,
                        MailTaskStatusEnum.FAILED,
                        MailTaskStatusEnum.SENDING);
    }

    private static String trimError(String error) {
        if (error == null) {
            return null;
        }
        return error.length() > 1000 ? error.substring(0, 1000) : error;
    }
}
