package com.njcn.rdms.module.project.service.workreport.remind;

import com.njcn.rdms.module.project.constant.WorkReportConstants;
import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportDO;
import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportDO;
import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportDO;
import com.njcn.rdms.module.project.dal.mysql.workreport.monthly.MonthlyReportMapper;
import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportMapper;
import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportMapper;
import com.njcn.rdms.module.project.framework.notify.NotifySendEvent;
import com.njcn.rdms.module.project.framework.notify.NotifyTemplateCodeConstants;
import com.njcn.rdms.module.system.api.notify.NotifyMessageSendApi;
import com.njcn.rdms.module.system.api.notify.dto.NotifyUnreadMessageListReqDTO;
import com.njcn.rdms.module.system.api.notify.dto.NotifyUnreadMessageRespDTO;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.module.system.enums.notify.NotifyMessageLevelConstants;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.IsoFields;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@Slf4j
public class WorkReportPendingSubmitRemindService {

    private static final List<String> REMIND_STATUS_CODES = List.of(
            WorkReportConstants.STATUS_DRAFT,
            WorkReportConstants.STATUS_REJECTED);

    @Resource
    private WeeklyReportMapper weeklyReportMapper;
    @Resource
    private MonthlyReportMapper monthlyReportMapper;
    @Resource
    private ProjectReportMapper projectReportMapper;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private NotifyMessageSendApi notifyMessageSendApi;

    @Transactional(rollbackFor = Exception.class)
    public void remindPendingSubmitReports() {
        LocalDateTime cutoffTime = LocalDateTime.now().minusDays(2);
        remindWeekly(cutoffTime);
        remindMonthly(cutoffTime);
        remindProject(cutoffTime);
    }

    private void remindWeekly(LocalDateTime cutoffTime) {
        List<WeeklyReportDO> reports = weeklyReportMapper.selectListByPendingSubmitReminder(cutoffTime, REMIND_STATUS_CODES);
        Set<String> unreadKeys = loadUnreadReminderKeys(
                reports.stream().map(WeeklyReportDO::getReporterId).toList(),
                List.of(NotifyTemplateCodeConstants.WORK_REPORT_TEAM_REMIND));
        for (WeeklyReportDO report : reports) {
            if (report == null || report.getReporterId() == null) {
                continue;
            }
            if (hasUnreadReminder(report.getReporterId(), WorkReportConstants.REPORT_TYPE_WEEKLY, report.getId(), unreadKeys)) {
                continue;
            }
            Map<String, Object> params = new HashMap<>();
            params.put("reportTypeName", "周报");
            params.put("periodKey", formatWeeklyPeriod(report.getPeriodKey(), report.getPeriodStartDate(), report.getPeriodEndDate()));
            params.put("managerName", defaultText(report.getSupervisorName(), "系统通知"));
            appendRemindIdentity(params, WorkReportConstants.REPORT_TYPE_WEEKLY, report.getId());
            applicationEventPublisher.publishEvent(NotifySendEvent.of(
                    List.of(report.getReporterId()),
                    NotifyTemplateCodeConstants.WORK_REPORT_TEAM_REMIND,
                    params,
                    NotifyMessageLevelConstants.REMIND));
        }
    }

    private void remindMonthly(LocalDateTime cutoffTime) {
        List<MonthlyReportDO> reports = monthlyReportMapper.selectListByPendingSubmitReminder(cutoffTime, REMIND_STATUS_CODES);
        Set<String> unreadKeys = loadUnreadReminderKeys(
                reports.stream().map(MonthlyReportDO::getReporterId).toList(),
                List.of(NotifyTemplateCodeConstants.WORK_REPORT_TEAM_REMIND));
        for (MonthlyReportDO report : reports) {
            if (report == null || report.getReporterId() == null) {
                continue;
            }
            if (hasUnreadReminder(report.getReporterId(), WorkReportConstants.REPORT_TYPE_MONTHLY, report.getId(), unreadKeys)) {
                continue;
            }
            Map<String, Object> params = new HashMap<>();
            params.put("reportTypeName", "月报");
            params.put("periodKey", formatMonthlyPeriod(report.getPeriodKey(), report.getPeriodStartDate()));
            params.put("managerName", defaultText(report.getSupervisorName(), "系统通知"));
            appendRemindIdentity(params, WorkReportConstants.REPORT_TYPE_MONTHLY, report.getId());
            applicationEventPublisher.publishEvent(NotifySendEvent.of(
                    List.of(report.getReporterId()),
                    NotifyTemplateCodeConstants.WORK_REPORT_TEAM_REMIND,
                    params,
                    NotifyMessageLevelConstants.REMIND));
        }
    }

    private void remindProject(LocalDateTime cutoffTime) {
        List<ProjectReportDO> reports = projectReportMapper.selectListByPendingSubmitReminder(cutoffTime, REMIND_STATUS_CODES);
        Set<String> unreadKeys = loadUnreadReminderKeys(
                reports.stream().map(ProjectReportDO::getProjectOwnerId).toList(),
                List.of(NotifyTemplateCodeConstants.WORK_REPORT_PROJECT_HALF_MONTH_TEAM_REMIND));
        for (ProjectReportDO report : reports) {
            if (report == null || report.getProjectOwnerId() == null) {
                continue;
            }
            if (hasUnreadReminder(report.getProjectOwnerId(), WorkReportConstants.REPORT_TYPE_PROJECT, report.getId(), unreadKeys)) {
                continue;
            }
            Map<String, Object> params = new HashMap<>();
            params.put("projectName", defaultText(report.getProjectName(), ""));
            params.put("periodLabel", defaultText(report.getPeriodLabel(), report.getPeriodKey()));
            params.put("managerName", defaultText(report.getSupervisorName(), "系统通知"));
            appendRemindIdentity(params, WorkReportConstants.REPORT_TYPE_PROJECT, report.getId());
            applicationEventPublisher.publishEvent(NotifySendEvent.of(
                    List.of(report.getProjectOwnerId()),
                    NotifyTemplateCodeConstants.WORK_REPORT_PROJECT_HALF_MONTH_TEAM_REMIND,
                    params,
                    NotifyMessageLevelConstants.REMIND));
        }
    }

    private String formatWeeklyPeriod(String periodKey, LocalDate startDate, LocalDate endDate) {
        if (startDate == null || endDate == null) {
            return defaultText(periodKey, "");
        }
        int weekBasedYear = startDate.get(IsoFields.WEEK_BASED_YEAR);
        int weekOfYear = startDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
        return weekBasedYear + "年" + weekOfYear + "周（"
                + startDate.getMonthValue() + "月" + startDate.getDayOfMonth() + "日-"
                + endDate.getMonthValue() + "月" + endDate.getDayOfMonth() + "日）";
    }

    private String formatMonthlyPeriod(String periodKey, LocalDate startDate) {
        if (startDate == null) {
            return defaultText(periodKey, "");
        }
        return startDate.getYear() + "年" + startDate.getMonthValue() + "月";
    }

    private Set<String> loadUnreadReminderKeys(Collection<Long> userIds, List<String> templateCodes) {
        if (userIds == null || userIds.isEmpty() || templateCodes == null || templateCodes.isEmpty()) {
            return Set.of();
        }
        List<Long> filteredUserIds = userIds.stream().filter(java.util.Objects::nonNull).distinct().toList();
        if (filteredUserIds.isEmpty()) {
            return Set.of();
        }
        try {
            CommonResult<List<NotifyUnreadMessageRespDTO>> result = notifyMessageSendApi.getUnreadNotifyMessageListByTemplateCodes(
                    new NotifyUnreadMessageListReqDTO()
                            .setUserIds(new ArrayList<>(filteredUserIds))
                            .setTemplateCodes(templateCodes));
            List<NotifyUnreadMessageRespDTO> messages = result == null ? null : result.getCheckedData();
            if (messages == null || messages.isEmpty()) {
                return Set.of();
            }
            Set<String> keys = new HashSet<>();
            for (NotifyUnreadMessageRespDTO message : messages) {
                if (message == null || message.getUserId() == null || message.getTemplateParams() == null) {
                    continue;
                }
                Object reportType = message.getTemplateParams().get(WorkReportConstants.PARAM_REMIND_REPORT_TYPE);
                Object reportId = message.getTemplateParams().get(WorkReportConstants.PARAM_REMIND_REPORT_ID);
                if (reportType == null || reportId == null) {
                    continue;
                }
                keys.add(buildUnreadReminderKey(message.getUserId(), String.valueOf(reportType), String.valueOf(reportId)));
            }
            return keys;
        } catch (Exception ex) {
            log.warn("[loadUnreadReminderKeys][未读消息查询失败，本轮按无未读处理]", ex);
            return Set.of();
        }
    }

    private boolean hasUnreadReminder(Long userId, String reportType, Long reportId, Set<String> unreadKeys) {
        if (userId == null || reportId == null || unreadKeys == null || unreadKeys.isEmpty()) {
            return false;
        }
        return unreadKeys.contains(buildUnreadReminderKey(userId, reportType, String.valueOf(reportId)));
    }

    private String buildUnreadReminderKey(Long userId, String reportType, String reportId) {
        return userId + "#" + reportType + "#" + reportId;
    }

    private void appendRemindIdentity(Map<String, Object> params, String reportType, Long reportId) {
        params.put(WorkReportConstants.PARAM_REMIND_REPORT_TYPE, reportType);
        params.put(WorkReportConstants.PARAM_REMIND_REPORT_ID, reportId == null ? null : String.valueOf(reportId));
    }

    private String defaultText(String text, String fallback) {
        return StringUtils.hasText(text) ? text.trim() : fallback;
    }
}
