package com.tashow.cloud.app.service.feishu;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper;
import com.tashow.cloud.app.mapper.BuriedPointMapper;
import com.tashow.cloud.app.model.BuriedPoint;
import com.tashow.cloud.app.model.MqMessageRecord;
import com.tashow.cloud.app.mq.message.BuriedMessages;
import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient;
import com.tashow.cloud.sdk.feishu.config.LarkConfig;
import com.tashow.cloud.sdk.feishu.util.ChartImageGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 埋点监控服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BuriedPointMonitorService {
    private static final int ALERT_THRESHOLD = 3;
    private static final int MONITORING_HOURS = 12;
    private final Map<String, Long> alertCache = new ConcurrentHashMap<>();
    
    private final BuriedPointFailRecordMapper buriedPointFailRecordMapper;
    private final BuriedPointMapper buriedPointMapper;
    private final FeiShuAlertClient feiShuAlertClient;
    private final FeiShuCardDataService feiShuCardDataService;
    private final LarkConfig larkConfig;

    /**
     * 检查失败记录并发送告警
     */
    public boolean checkFailRecordsAndAlert(String cause) {
        try {
            Date now = new Date();
            Date hoursAgo = getDateHoursAgo(now, MONITORING_HOURS);
            boolean sentAlert = false;
            List<Date[]> timeRanges = getHourRanges(hoursAgo, now);
            long mqFailCount = countFailures(buriedPointFailRecordMapper, MqMessageRecord.class, hoursAgo, now);
            long buriedFailCount = countFailures(buriedPointMapper, BuriedPoint.class, hoursAgo, now);
            if (mqFailCount > ALERT_THRESHOLD||buriedFailCount > ALERT_THRESHOLD) {
                if (!hasRecentlySentAlert(cause)) {
                    sendAlert(mqFailCount,  cause, getMqStats(timeRanges));
                    alertCache.put(cause, System.currentTimeMillis());
                    sentAlert = true;
                }
            }

            return sentAlert;
        } catch (Exception e) {
            log.error("[埋点监控] 检查失败记录异常", e);
            return false;
        }
    }
    
    /**
     * 检查是否最近已发送过相同类型的告警
     */
    private boolean hasRecentlySentAlert(String alertType) {
        Long lastSentTime = alertCache.get(alertType);
        if (lastSentTime == null) {
            return false;
        }
        
        long hourInMillis = MONITORING_HOURS * 60 * 60 * 1000L;
        return (System.currentTimeMillis() - lastSentTime) < hourInMillis;
    }


    /**
     * 获取消息队列统计数据
     */
    private List<ChartImageGenerator.MonitoringDataPoint> getMqStats(List<Date[]> timeRanges) {
        Map<Date, Integer> successData = batchQueryMqStatus(timeRanges, MqMessageRecord.STATUS_SUCCESS);
        Map<Date, Integer> failedData = batchQueryMqFailures(timeRanges);
        
        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:00");
        return timeRanges.stream()
            .map(range -> new ChartImageGenerator.MonitoringDataPoint(
                timeFormat.format(range[0]),
                successData.getOrDefault(range[0], 0),
                failedData.getOrDefault(range[0], 0)
            ))
            .toList();
    }
    
    /**
     * 获取埋点表统计数据
     */
    private List<ChartImageGenerator.MonitoringDataPoint> getBuriedStats(List<Date[]> timeRanges) {
        // 批量查询每个时间区间的数据
        Map<Date, Integer> successData = batchQueryBuriedStatus(timeRanges, BuriedMessages.STATUS_SUCCESS);
        Map<Date, Integer> failedData = batchQueryBuriedStatus(timeRanges, BuriedMessages.STATUS_ERROR);
        SimpleDateFormat timeFormat = new SimpleDateFormat("HH:00");
        return timeRanges.stream()
            .map(range -> new ChartImageGenerator.MonitoringDataPoint(
                timeFormat.format(range[0]),
                successData.getOrDefault(range[0], 0),
                failedData.getOrDefault(range[0], 0)
            ))
            .toList();
    }
    
    /**
     * 查询MQ状态数据
     */
    private Map<Date, Integer> batchQueryMqStatus(List<Date[]> timeRanges, int status) {
        Map<Date, Integer> result = new HashMap<>();
        for (Date[] range : timeRanges) {
            LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
            query.ge(MqMessageRecord::getCreateTime, range[0])
                 .lt(MqMessageRecord::getCreateTime, range[1])
                 .eq(MqMessageRecord::getStatus, status);
            result.put(range[0], buriedPointFailRecordMapper.selectCount(query).intValue());
        }
        return result;
    }
    
    /**
     * 批量查询MQ失败数据
     */
    private Map<Date, Integer> batchQueryMqFailures(List<Date[]> timeRanges) {
        Map<Date, Integer> result = new HashMap<>();
        for (Date[] range : timeRanges) {
            LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
            query.ge(MqMessageRecord::getCreateTime, range[0])
                 .lt(MqMessageRecord::getCreateTime, range[1])
                 .in(MqMessageRecord::getStatus, Arrays.asList(
                     MqMessageRecord.STATUS_UNPROCESSED, MqMessageRecord.STATUS_FAILED));
            result.put(range[0], buriedPointFailRecordMapper.selectCount(query).intValue());
        }
        return result;
    }
    
    /**
     * 批量查询埋点状态数据
     */
    private Map<Date, Integer> batchQueryBuriedStatus(List<Date[]> timeRanges, int status) {
        Map<Date, Integer> result = new HashMap<>();
        for (Date[] range : timeRanges) {
            LambdaQueryWrapper<BuriedPoint> query = new LambdaQueryWrapper<>();
            query.ge(BuriedPoint::getCreateTime, range[0])
                 .lt(BuriedPoint::getCreateTime, range[1])
                 .eq(BuriedPoint::getStatus, status);
            result.put(range[0], buriedPointMapper.selectCount(query).intValue());
        }
        return result;
    }

    /**
     * 计算失败数量
     */
    private <T> long countFailures(Object mapper, Class<T> entityClass, Date startDate, Date endDate) {
        try {
            if (entityClass == BuriedPoint.class) {
                LambdaQueryWrapper<BuriedPoint> query = new LambdaQueryWrapper<>();
                query.ge(BuriedPoint::getCreateTime, startDate)
                     .le(BuriedPoint::getCreateTime, endDate)
                     .eq(BuriedPoint::getStatus, BuriedMessages.STATUS_ERROR);
                return ((BuriedPointMapper)mapper).selectCount(query);
            } else {
                LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
                query.ge(MqMessageRecord::getCreateTime, startDate)
                     .le(MqMessageRecord::getCreateTime, endDate)
                     .eq(MqMessageRecord::getStatus, MqMessageRecord.STATUS_FAILED);
                return ((BuriedPointFailRecordMapper)mapper).selectCount(query);
            }
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 发送告警
     */
    private void sendAlert(long failCount, String alertMsg, List<ChartImageGenerator.MonitoringDataPoint> data) {
        try {
            String imageKey = feiShuAlertClient.uploadImage(data, alertMsg);
            String title = alertMsg.split(":")[0].trim();
            
            Map<String, Object> templateData = Map.of(
                "alert_title", title,
                "image_key", Map.of("img_key", imageKey),
                "current_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
                "fail_count", failCount
            );
            
            String messageId = feiShuAlertClient.sendCardMessage(
                larkConfig.getChatId(),
                larkConfig.getExceptionCards(),
                new HashMap<>(templateData)
            );
            feiShuCardDataService.saveCardData(messageId, templateData);
        } catch (Exception e) {
            log.error("[埋点监控] 发送告警失败: {}", e.getMessage());
        }
    }

    /**
     * 获取小时范围列表
     */
    private List<Date[]> getHourRanges(Date startDate, Date endDate) {
        List<Date[]> ranges = new ArrayList<>();
        Calendar cal = Calendar.getInstance();
        
        cal.setTime(endDate);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        Date endHour = cal.getTime();
        
        cal.add(Calendar.HOUR_OF_DAY, -(MONITORING_HOURS - 1));
        Date startHour = startDate.after(cal.getTime()) ? startDate : cal.getTime();
        
        cal.setTime(startHour);
        while (!cal.getTime().after(endHour)) {
            Date hourStart = cal.getTime();
            cal.add(Calendar.HOUR_OF_DAY, 1);
            Date hourEnd = cal.getTime().after(endDate) ? endDate : cal.getTime();
            ranges.add(new Date[]{hourStart, hourEnd});
            
            if (hourEnd.equals(endDate)) break;
        }
        return ranges;
    }

    /**
     * 获取指定时间前N小时的时间
     */
    private Date getDateHoursAgo(Date date, int hours) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, -hours);
        return calendar.getTime();
    }
} 