package com.example.demo.service;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.model.UserErrorLog;
import com.example.demo.model.UserErrorLog.ErrorType;
import com.example.demo.repository.UserErrorLogRepository;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 用户错误日志服务
 * 负责记录、查询和统计用户错误
 */
@Service
public class UserErrorLogService {

    private static final Logger logger = LoggerFactory.getLogger(UserErrorLogService.class);
    
    // 记录已经记录过错误日志的任务ID，避免重复记录
    private final Set<String> loggedErrorTaskIds = ConcurrentHashMap.newKeySet();

    @Autowired
    private UserErrorLogRepository userErrorLogRepository;

    // ==================== 记录错误 ====================

    /**
     * 异步记录错误日志（推荐使用，不阻塞主流程）
     * 自动去重：同一个taskId只记录一次
     */
    @Async
    public void logErrorAsync(String username, ErrorType errorType, String errorMessage, 
                              String errorSource, String taskId, String taskType) {
        try {
            // 如果有taskId，检查是否已经记录过
            if (taskId != null && !taskId.isEmpty()) {
                if (!loggedErrorTaskIds.add(taskId)) {
                    // 已经记录过，跳过
                    logger.debug("错误日志已记录过，跳过重复记录: taskId={}", taskId);
                    return;
                }
            }
            logError(username, errorType, errorMessage, errorSource, taskId, taskType, null, null);
        } catch (Exception e) {
            logger.error("异步记录错误日志失败: {}", e.getMessage());
        }
    }

    /**
     * 记录错误日志（完整版本）
     */
    @Transactional
    public UserErrorLog logError(String username, ErrorType errorType, String errorMessage,
                                  String errorSource, String taskId, String taskType,
                                  HttpServletRequest request, Exception exception) {
        try {
            UserErrorLog errorLog = new UserErrorLog(username, errorType, errorMessage, errorSource);
            errorLog.setTaskId(taskId);
            errorLog.setTaskType(taskType);

            // 从请求中获取信息
            if (request != null) {
                errorLog.setRequestPath(request.getRequestURI());
                errorLog.setRequestMethod(request.getMethod());
                errorLog.setIpAddress(getClientIpAddress(request));
                errorLog.setUserAgent(request.getHeader("User-Agent"));
            }

            // 记录异常堆栈（截取前3000字符避免过长）
            if (exception != null) {
                String stackTrace = getStackTraceString(exception);
                if (stackTrace.length() > 3000) {
                    stackTrace = stackTrace.substring(0, 3000) + "...[truncated]";
                }
                errorLog.setStackTrace(stackTrace);
            }

            UserErrorLog saved = userErrorLogRepository.save(errorLog);
            logger.info("记录用户错误日志: id={}, username={}, type={}, source={}", 
                    saved.getId(), username, errorType, errorSource);
            return saved;

        } catch (Exception e) {
            logger.error("保存错误日志失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 快捷方法：记录任务错误
     */
    @Async
    public void logTaskError(String username, String taskId, String taskType, 
                             ErrorType errorType, String errorMessage) {
        logError(username, errorType, errorMessage, "TaskService", taskId, taskType, null, null);
    }

    /**
     * 快捷方法：记录API错误
     */
    @Async
    public void logApiError(String username, String apiPath, String errorMessage, Exception e) {
        UserErrorLog log = new UserErrorLog(username, ErrorType.API_CALL_ERROR, errorMessage, "ApiService");
        log.setRequestPath(apiPath);
        if (e != null) {
            log.setStackTrace(getStackTraceString(e));
        }
        try {
            userErrorLogRepository.save(log);
        } catch (Exception ex) {
            logger.error("记录API错误失败: {}", ex.getMessage());
        }
    }

    /**
     * 快捷方法：记录支付错误
     */
    @Async
    public void logPaymentError(String username, String orderId, String errorMessage) {
        UserErrorLog log = new UserErrorLog(username, ErrorType.PAYMENT_ERROR, errorMessage, "PaymentService");
        log.setTaskId(orderId);
        log.setTaskType("PAYMENT");
        try {
            userErrorLogRepository.save(log);
        } catch (Exception e) {
            logger.error("记录支付错误失败: {}", e.getMessage());
        }
    }

    /**
     * 快捷方法：记录认证错误
     */
    @Async
    public void logAuthError(String username, ErrorType errorType, String errorMessage, 
                             HttpServletRequest request) {
        logError(username, errorType, errorMessage, "AuthService", null, null, request, null);
    }

    // ==================== 查询错误 ====================

    /**
     * 获取所有错误日志（分页）
     */
    public Page<UserErrorLog> getAllErrors(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        return userErrorLogRepository.findAll(pageable);
    }

    /**
     * 获取用户的错误日志（分页）
     */
    public Page<UserErrorLog> getUserErrors(String username, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        return userErrorLogRepository.findByUsername(username, pageable);
    }

    /**
     * 按错误类型查询（分页）
     */
    public Page<UserErrorLog> getErrorsByType(ErrorType errorType, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        return userErrorLogRepository.findByErrorType(errorType, pageable);
    }

    /**
     * 获取最近的错误
     */
    public List<UserErrorLog> getRecentErrors(int limit) {
        if (limit <= 10) {
            return userErrorLogRepository.findTop10ByOrderByCreatedAtDesc();
        }
        return userErrorLogRepository.findTop50ByOrderByCreatedAtDesc();
    }

    /**
     * 获取用户最近的错误
     */
    public List<UserErrorLog> getUserRecentErrors(String username) {
        return userErrorLogRepository.findTop10ByUsernameOrderByCreatedAtDesc(username);
    }

    /**
     * 按任务ID查询错误
     */
    public List<UserErrorLog> getErrorsByTaskId(String taskId) {
        return userErrorLogRepository.findByTaskIdOrderByCreatedAtDesc(taskId);
    }

    /**
     * 按时间范围查询
     */
    public List<UserErrorLog> getErrorsByDateRange(LocalDate startDate, LocalDate endDate) {
        LocalDateTime startTime = startDate.atStartOfDay();
        LocalDateTime endTime = endDate.atTime(LocalTime.MAX);
        return userErrorLogRepository.findByCreatedAtBetweenOrderByCreatedAtDesc(startTime, endTime);
    }

    // ==================== 统计功能 ====================

    /**
     * 获取错误统计概览
     */
    public Map<String, Object> getErrorStatistics(int days) {
        Map<String, Object> stats = new HashMap<>();
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startTime = now.minusDays(days);

        // 总错误数（指定天数内）
        long totalErrors = userErrorLogRepository.countByCreatedAtBetween(startTime, now);
        stats.put("totalErrors", totalErrors);

        // 今日错误数
        LocalDateTime todayStart = now.toLocalDate().atStartOfDay();
        long todayErrors = userErrorLogRepository.countByCreatedAtBetween(todayStart, now);
        stats.put("todayErrors", todayErrors);

        // 本周错误数
        LocalDateTime weekStart = now.minusDays(7);
        long weekErrors = userErrorLogRepository.countByCreatedAtBetween(weekStart, now);
        stats.put("weekErrors", weekErrors);

        // 按类型统计
        List<Object[]> byType = userErrorLogRepository.countByErrorType(startTime);
        Map<String, Long> errorsByType = new LinkedHashMap<>();
        for (Object[] row : byType) {
            ErrorType type = (ErrorType) row[0];
            Long count = (Long) row[1];
            errorsByType.put(type.name(), count);
        }
        stats.put("errorsByType", errorsByType);

        // 按来源统计
        List<Object[]> bySource = userErrorLogRepository.countByErrorSource(startTime);
        Map<String, Long> errorsBySource = new LinkedHashMap<>();
        for (Object[] row : bySource) {
            String source = (String) row[0];
            Long count = (Long) row[1];
            errorsBySource.put(source, count);
        }
        stats.put("errorsBySource", errorsBySource);

        // 按日期统计
        List<Object[]> byDate = userErrorLogRepository.countByDate(startTime);
        Map<String, Long> errorsByDate = new LinkedHashMap<>();
        for (Object[] row : byDate) {
            String date = row[0].toString();
            Long count = (Long) row[1];
            errorsByDate.put(date, count);
        }
        stats.put("errorsByDate", errorsByDate);

        return stats;
    }

    /**
     * 获取用户错误统计
     */
    public Map<String, Object> getUserErrorStatistics(String username) {
        Map<String, Object> stats = new HashMap<>();
        
        long totalErrors = userErrorLogRepository.countByUsername(username);
        stats.put("totalErrors", totalErrors);
        
        List<UserErrorLog> recentErrors = userErrorLogRepository.findTop10ByUsernameOrderByCreatedAtDesc(username);
        stats.put("recentErrors", recentErrors);

        return stats;
    }

    /**
     * 定时清理旧错误日志（保疙30天）
     * 每天凌晨3点执行
     */
    @Scheduled(cron = "0 0 3 * * ?")
    @Transactional
    public void cleanupOldErrors() {
        try {
            LocalDateTime beforeTime = LocalDateTime.now().minusDays(30);
            userErrorLogRepository.deleteByCreatedAtBefore(beforeTime);
            logger.info("清理30天前的错误日志完成");
        } catch (Exception e) {
            logger.error("清理旧错误日志失败: {}", e.getMessage());
        }
    }

    // ==================== 工具方法 ====================

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个代理时取第一个IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }

    /**
     * 获取异常堆栈字符串
     */
    private String getStackTraceString(Exception e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        return sw.toString();
    }
}
