/**
 * 日志管理服务
 * 提供日志查询、配置管理、统计分析等功能
 * 
 * 数据来源：PostgreSQL 数据库（SystemLog 表）
 */

import { Injectable } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { BusinessException } from '@common/exceptions/business.exception';
import {
  QueryLogDto,
  QueryErrorLogDto,
  QuerySlowRequestDto,
  QueryLogStatsDto,
  QueryAlertHistoryDto,
  UpdateLogConfigDto,
  CleanupLogDto,
  UpdateAlertConfigDto,
  TestAlertDto,
  LogLevel as DtoLogLevel,
  AlertType as DtoAlertType,
  AlertStatus as DtoAlertStatus,
  AlertSeverity as DtoAlertSeverity,
} from '../dto';
import { LogLevel, LogAlertType, LogAlertStatus, LogAlertSeverity, Prisma } from '@prisma/client';
import { winstonLogger } from '../config/winston.config';

// ==================== 类型定义 ====================

export interface LogItem {
  '@timestamp': string;
  level: DtoLogLevel;
  message: string;
  trace: {
    id: string;
    span_id: string;
    parent_span_id?: string;
  };
  http: {
    request_id: string;
    method: string;
    url: string;
    status_code: number;
    duration_ms: number;
  };
  user?: {
    id: string;
    name: string;
  };
  client: {
    ip: string;
    user_agent: string;
  };
  service: {
    name: string;
    region: string;
    instance: string;
  };
  error?: {
    type: string;
    message: string;
    code?: string;
    stack?: string;
  };
}

export interface PaginatedResult<T> {
  items: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

export interface LogConfig {
  level: DtoLogLevel;
  sampling: {
    enabled: boolean;
    rules: {
      alwaysLog: { levels: DtoLogLevel[]; paths: string[] };
      neverLog: { paths: string[] };
      dynamic: {
        qpsThreshold: number;
        minSampleRate: number;
        maxSampleRate: number;
      };
    };
  };
  retention: {
    http: number;
    application: number;
    error: number;
  };
  alerts: AlertConfig;
  updatedAt: string;
  updatedBy: string;
}

export interface AlertConfig {
  slowRequest: {
    enabled: boolean;
    thresholdMs: number;
    excludePaths: string[];
  };
  errorRate: {
    enabled: boolean;
    thresholdPercent: number;
    windowMinutes: number;
    minRequests?: number;
  };
  ipAnomaly?: {
    enabled: boolean;
    maxRequestsPerMinute: number;
    blockDurationMinutes: number;
  };
  diskSpace: {
    enabled: boolean;
    warningPercent: number;
    criticalPercent: number;
  };
  notifications?: {
    slack: { enabled: boolean; webhookConfigured: boolean };
    feishu: { enabled: boolean; webhookConfigured: boolean };
    email: { enabled: boolean; recipientCount: number };
  };
  updatedAt?: string;
  updatedBy?: string;
}

export interface AlertItem {
  id: string;
  type: DtoAlertType;
  severity: DtoAlertSeverity;
  message: string;
  context: Record<string, any>;
  sentAt: string;
  channels: string[];
  status: DtoAlertStatus;
}

export interface SpanInfo {
  spanId: string;
  parentSpanId: string | null;
  service: string;
  region: string;
  operation: string;
  startTime: string;
  duration: number;
  status: 'SUCCESS' | 'ERROR';
}

export interface TimelineEvent {
  time: number;
  event: string;
}

export interface TraceData {
  traceId: string;
  startTime: string;
  endTime: string;
  totalDuration: number;
  spans: SpanInfo[];
  timeline: TimelineEvent[];
}

// ==================== 服务实现 ====================

@Injectable()
export class LogManagementService {
  // 默认配置（可通过 LogConfig 表覆盖）
  private defaultConfig: LogConfig = {
    level: DtoLogLevel.INFO,
    sampling: {
      enabled: true,
      rules: {
        alwaysLog: {
          levels: [DtoLogLevel.ERROR, DtoLogLevel.WARN],
          paths: ['/api/v1/auth/*'],
        },
        neverLog: {
          paths: ['/health', '/ready', '/metrics'],
        },
        dynamic: {
          qpsThreshold: 1000,
          minSampleRate: 0.1,
          maxSampleRate: 1.0,
        },
      },
    },
    retention: {
      http: 14,
      application: 30,
      error: 90,
    },
    alerts: {
      slowRequest: {
        enabled: true,
        thresholdMs: 2000,
        excludePaths: ['/api/v1/files/upload'],
      },
      errorRate: {
        enabled: true,
        thresholdPercent: 5,
        windowMinutes: 5,
        minRequests: 100,
      },
      ipAnomaly: {
        enabled: true,
        maxRequestsPerMinute: 100,
        blockDurationMinutes: 10,
      },
      diskSpace: {
        enabled: true,
        warningPercent: 80,
        criticalPercent: 90,
      },
      notifications: {
        slack: { enabled: false, webhookConfigured: false },
        feishu: { enabled: false, webhookConfigured: false },
        email: { enabled: false, recipientCount: 0 },
      },
    },
    updatedAt: new Date().toISOString(),
    updatedBy: 'system',
  };

  constructor(private readonly prisma: PrismaService) {}

  // ==================== 辅助方法 ====================

  /**
   * 验证字符串是否为有效的 UUID 格式
   */
  private isValidUUID(str: string): boolean {
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    return uuidRegex.test(str);
  }

  /**
   * 构造 createdAt 时间范围 where 子句（startTime/endTime 可选）
   */
  private timeRangeWhere(startTime?: string, endTime?: string): Prisma.DateTimeFilter | undefined {
    if (!startTime && !endTime) return undefined;
    const filter: Prisma.DateTimeFilter = {};
    if (startTime) filter.gte = new Date(startTime);
    if (endTime) filter.lte = new Date(endTime);
    return filter;
  }

  /**
   * 计算分页元数据（page/limit/total → totalPages/hasNext/hasPrev）
   */
  private paginationMeta(page: number, limit: number, total: number) {
    const totalPages = Math.ceil(total / limit);
    return {
      total,
      page,
      limit,
      totalPages,
      hasNext: page < totalPages,
      hasPrev: page > 1,
    };
  }

  /**
   * 把 Prisma groupBy 结果按指定字段聚合成 { [key]: count }
   */
  private groupByCount<T extends Record<string, any>>(
    rows: T[],
    key: keyof T,
  ): Record<string, number> {
    const map: Record<string, number> = {};
    for (const item of rows) {
      const k = item[key];
      if (k !== null && k !== undefined) {
        map[String(k)] = (item as any)._count;
      }
    }
    return map;
  }

  // ==================== 日志查询 ====================

  /**
   * 查询日志列表
   */
  async queryLogs(query: QueryLogDto): Promise<PaginatedResult<LogItem> & { summary?: any }> {
    this.validateTimeRange(query.startTime, query.endTime, 7);

    const page = query.page || 1;
    const limit = query.limit || 50;
    const skip = (page - 1) * limit;

    // 构建查询条件
    const where: any = {};

    const timeRange = this.timeRangeWhere(query.startTime, query.endTime);
    if (timeRange) where.createdAt = timeRange;

    if (query.level) {
      where.level = query.level as LogLevel;
    }
    if (query.traceId) {
      where.traceId = query.traceId;
    }
    if (query.requestId) {
      where.requestId = query.requestId;
    }
    if (query.userId && this.isValidUUID(query.userId)) {
      where.userId = query.userId;
    }
    if (query.userName) {
      where.userName = { contains: query.userName, mode: 'insensitive' };
    }
    if (query.service) {
      where.service = query.service;
    }
    if (query.region) {
      where.region = query.region;
    }
    if (query.url) {
      where.url = { 
        contains: query.url, 
        mode: 'insensitive'
      };
    }
    if (query.keyword) {
      where.OR = [
        { message: { contains: query.keyword, mode: 'insensitive' } },
        { url: { contains: query.keyword, mode: 'insensitive' } },
        { errorMessage: { contains: query.keyword, mode: 'insensitive' } },
      ];
    }
    // 持续时间范围查询
    if (query.minDuration !== undefined || query.maxDuration !== undefined) {
      const durationMs: any = {};
      if (query.minDuration !== undefined) {
        durationMs.gte = query.minDuration;
      }
      if (query.maxDuration !== undefined) {
        durationMs.lte = query.maxDuration;
      }
      where.durationMs = durationMs;
    }
    if (query.statusCode) {
      where.statusCode = query.statusCode;
    }
    if (query.hasError) {
      where.level = LogLevel.ERROR;
    }

    // 排序
    const orderBy: Prisma.SystemLogOrderByWithRelationInput = {};
    if (query.sortBy === '@timestamp') {
      orderBy.createdAt = query.sortOrder || 'desc';
    } else if (query.sortBy === 'level') {
      orderBy.level = query.sortOrder || 'desc';
    } else if (query.sortBy === 'duration') {
      orderBy.durationMs = query.sortOrder || 'desc';
    } else if (query.sortBy === 'service') {
      orderBy.service = query.sortOrder || 'asc';
    } else {
      orderBy.createdAt = 'desc';
    }

    // 调试日志：查看构建的查询条件
    winstonLogger.debug('Query where conditions:', JSON.stringify(where, null, 2));
    winstonLogger.debug('Query orderBy:', JSON.stringify(orderBy, null, 2));

    // 查询数据
    const [logs, total] = await Promise.all([
      this.prisma.systemLog.findMany({
        where,
        orderBy,
        skip,
        take: limit,
      }),
      this.prisma.systemLog.count({ where }),
    ]);

    // 转换为 API 响应格式
    const items: LogItem[] = logs.map(log => this.transformLogToItem(log));

    return {
      items,
      ...this.paginationMeta(page, limit, total),
    };
  }

  /**
   * 按追踪 ID 查询完整链路
   */
  async getTrace(traceId: string): Promise<TraceData> {
    const logs = await this.prisma.systemLog.findMany({
      where: { traceId },
      orderBy: { createdAt: 'asc' },
    });

    if (logs.length === 0) {
      throw new BusinessException('追踪记录不存在', 'TRACE_NOT_FOUND', 404);
    }

    const startTime = logs[0].createdAt;
    const endTime = logs[logs.length - 1].createdAt;
    const totalDuration = endTime.getTime() - startTime.getTime();

    // 构建 spans
    const spans: SpanInfo[] = logs.map(log => ({
      spanId: log.spanId || log.requestId,
      parentSpanId: log.parentSpanId || null,
      service: log.service,
      region: log.region,
      operation: `${log.method || 'UNKNOWN'} ${log.url || ''}`,
      startTime: log.createdAt.toISOString(),
      duration: log.durationMs || 0,
      status: log.level === LogLevel.ERROR ? 'ERROR' : 'SUCCESS',
    }));

    // 构建时间线
    const timeline: TimelineEvent[] = [];
    let currentTime = 0;
    for (const log of logs) {
      timeline.push({
        time: currentTime,
        event: `${log.service} ${log.method || ''} ${log.url || ''} started`,
      });
      currentTime += log.durationMs || 0;
      timeline.push({
        time: currentTime,
        event: `${log.service} ${log.method || ''} ${log.url || ''} completed`,
      });
    }

    return {
      traceId,
      startTime: startTime.toISOString(),
      endTime: endTime.toISOString(),
      totalDuration,
      spans,
      timeline,
    };
  }

  /**
   * 查询错误日志
   */
  async queryErrorLogs(query: QueryErrorLogDto): Promise<PaginatedResult<LogItem> & { summary: any }> {
    this.validateTimeRange(query.startTime, query.endTime, 7);

    const page = query.page || 1;
    const limit = query.limit || 50;
    const skip = (page - 1) * limit;

    // 构建查询条件
    const where: Prisma.SystemLogWhereInput = {
      level: LogLevel.ERROR,
    };

    const errTimeRange = this.timeRangeWhere(query.startTime, query.endTime);
    if (errTimeRange) where.createdAt = errTimeRange;

    if (query.errorType) {
      where.errorType = query.errorType;
    }
    if (query.errorCode) {
      where.errorCode = query.errorCode;
    }
    if (query.userId && this.isValidUUID(query.userId)) {
      where.userId = query.userId;
    }
    if (query.userName) {
      where.userName = { contains: query.userName, mode: 'insensitive' };
    }
    if (query.service) {
      where.service = query.service;
    }
    if (query.url) {
      where.url = { contains: query.url, mode: 'insensitive' };
    }
    if (query.keyword) {
      where.OR = [
        { errorMessage: { contains: query.keyword, mode: 'insensitive' } },
        { message: { contains: query.keyword, mode: 'insensitive' } },
      ];
    }

    // 查询数据
    const [logs, total, byType] = await Promise.all([
      this.prisma.systemLog.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      this.prisma.systemLog.count({ where }),
      this.prisma.systemLog.groupBy({
        by: ['errorType'],
        where,
        _count: true,
      }),
    ]);

    const items: LogItem[] = logs.map(log => this.transformLogToItem(log));

    return {
      items,
      ...this.paginationMeta(page, limit, total),
      summary: {
        total,
        byType: this.groupByCount(byType, 'errorType'),
        byHour: [], // TODO: 实现按小时统计
      },
    };
  }

  /**
   * 查询慢请求
   */
  async querySlowRequests(query: QuerySlowRequestDto): Promise<PaginatedResult<any> & { summary: any }> {
    this.validateTimeRange(query.startTime, query.endTime, 7);

    const thresholdMs = query.thresholdMs || 2000;
    const page = query.page || 1;
    const limit = query.limit || 50;
    const skip = (page - 1) * limit;

    // 构建查询条件
    const where: Prisma.SystemLogWhereInput = {
      durationMs: { gte: thresholdMs },
    };

    const slowTimeRange = this.timeRangeWhere(query.startTime, query.endTime);
    if (slowTimeRange) where.createdAt = slowTimeRange;

    if (query.service) {
      where.service = query.service;
    }
    if (query.url) {
      where.url = { contains: query.url, mode: 'insensitive' };
    }
    if (query.userId && this.isValidUUID(query.userId)) {
      where.userId = query.userId;
    }
    if (query.userName) {
      where.userName = { contains: query.userName, mode: 'insensitive' };
    }

    // 查询数据
    const [logs, total, stats] = await Promise.all([
      this.prisma.systemLog.findMany({
        where,
        orderBy: { durationMs: 'desc' },
        skip,
        take: limit,
      }),
      this.prisma.systemLog.count({ where }),
      this.prisma.systemLog.aggregate({
        where,
        _avg: { durationMs: true },
        _max: { durationMs: true },
      }),
    ]);

    // 转换为慢请求格式
    const items = logs.map(log => ({
      timestamp: log.createdAt.toISOString(),
      traceId: log.traceId,
      requestId: log.requestId,
      method: log.method,
      url: log.url,
      duration: log.durationMs,
      user: log.userId ? { id: log.userId, name: log.userName || '' } : undefined,
      service: log.service,
      region: log.region,
    }));

    return {
      items,
      ...this.paginationMeta(page, limit, total),
      summary: {
        total,
        avgDuration: Math.round(stats._avg.durationMs || 0),
        maxDuration: stats._max.durationMs || 0,
        p95Duration: 0, // TODO: 实现 P95 计算
        p99Duration: 0, // TODO: 实现 P99 计算
        topPaths: [], // TODO: 实现 Top 路径统计
        byService: {}, // TODO: 实现按服务统计
      },
    };
  }

  // ==================== 配置管理 ====================

  /**
   * 获取日志配置
   */
  async getConfig(): Promise<LogConfig> {
    try {
      const configs = await this.prisma.logConfig.findMany();
      
      // 合并数据库配置和默认配置
      const config = { ...this.defaultConfig };
      
      for (const item of configs) {
        if (item.key === 'level') {
          config.level = item.value as unknown as DtoLogLevel;
        } else if (item.key === 'sampling') {
          config.sampling = { ...config.sampling, ...(item.value as any) };
        } else if (item.key === 'retention') {
          config.retention = { ...config.retention, ...(item.value as any) };
        } else if (item.key === 'alerts') {
          config.alerts = { ...config.alerts, ...(item.value as any) };
        }
        config.updatedAt = item.updatedAt.toISOString();
        config.updatedBy = item.updatedBy || 'system';
      }

      return config;
    } catch (error) {
      winstonLogger.warn('Failed to load log config from database, using default', { error });
      return this.defaultConfig;
    }
  }

  /**
   * 更新日志配置
   */
  async updateConfig(dto: UpdateLogConfigDto, userId: string): Promise<LogConfig> {
    const updates: { key: string; value: any }[] = [];
    // 仅在需要 merge 已有配置时调用一次 getConfig
    const needsMerge = dto.sampling || dto.alerts;
    const currentConfig = needsMerge ? await this.getConfig() : null;

    if (dto.level) {
      updates.push({ key: 'level', value: dto.level });
    }
    if (dto.sampling && currentConfig) {
      updates.push({
        key: 'sampling',
        value: { ...currentConfig.sampling, ...dto.sampling },
      });
    }
    if (dto.alerts && currentConfig) {
      updates.push({
        key: 'alerts',
        value: { ...currentConfig.alerts, ...dto.alerts },
      });
    }

    // 批量更新配置
    for (const update of updates) {
      await this.prisma.logConfig.upsert({
        where: { key: update.key },
        create: {
          key: update.key,
          value: update.value as any,
          updatedBy: userId,
        },
        update: {
          value: update.value as any,
          updatedBy: userId,
        },
      });
    }

    return this.getConfig();
  }

  /**
   * 手动清理日志
   */
  async cleanupLogs(dto: CleanupLogDto): Promise<any> {
    const type = dto.type || 'all';
    const olderThanDays = dto.olderThanDays || 30;
    const dryRun = dto.dryRun || false;

    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);

    // 构建查询条件
    const where: Prisma.SystemLogWhereInput = {
      createdAt: { lt: cutoffDate },
    };

    if (type !== 'all') {
      if (type === 'error') {
        where.level = LogLevel.ERROR;
      } else if (type === 'http') {
        where.method = { not: null };
      }
    }

    // 统计要删除的数量
    const count = await this.prisma.systemLog.count({ where });

    // 记录清理操作
    const cleanupRecord = await this.prisma.logCleanupRecord.create({
      data: {
        type,
        olderThanDays,
        dryRun,
        status: dryRun ? 'COMPLETED' : 'RUNNING',
        deletedCount: dryRun ? 0 : count,
      },
    });

    if (!dryRun) {
      // 实际删除
      await this.prisma.systemLog.deleteMany({ where });

      // 更新清理记录
      await this.prisma.logCleanupRecord.update({
        where: { id: cleanupRecord.id },
        data: {
          status: 'COMPLETED',
          deletedCount: count,
          completedAt: new Date(),
        },
      });
    }

    return {
      dryRun,
      deletedFiles: dryRun ? 0 : count,
      freedSpace: 'N/A', // 数据库方案不计算空间
      details: {
        [type]: { files: count, size: 'N/A' },
      },
      completedAt: new Date().toISOString(),
    };
  }

  // ==================== 统计分析 ====================

  /**
   * 获取日志统计
   */
  async getStats(query: QueryLogStatsDto): Promise<any> {
    this.validateTimeRange(query.startTime, query.endTime, 30);

    const now = new Date();
    const startTime = query.startTime ? new Date(query.startTime) : new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const endTime = query.endTime ? new Date(query.endTime) : now;

    const where: Prisma.SystemLogWhereInput = {
      createdAt: {
        gte: startTime,
        lte: endTime,
      },
    };

    // 并行查询各种统计
    const [
      totalLogs,
      errorCount,
      warnCount,
      avgDuration,
      byLevel,
      byService,
    ] = await Promise.all([
      this.prisma.systemLog.count({ where }),
      this.prisma.systemLog.count({ where: { ...where, level: LogLevel.ERROR } }),
      this.prisma.systemLog.count({ where: { ...where, level: LogLevel.WARN } }),
      this.prisma.systemLog.aggregate({
        where,
        _avg: { durationMs: true },
      }),
      this.prisma.systemLog.groupBy({
        by: ['level'],
        where,
        _count: true,
      }),
      this.prisma.systemLog.groupBy({
        by: ['service'],
        where,
        _count: true,
      }),
    ]);

    // 按级别统计（保留 ERROR/WARN/INFO/DEBUG 默认 0）
    const byLevelMap: Record<string, number> = {
      ERROR: 0,
      WARN: 0,
      INFO: 0,
      DEBUG: 0,
      ...this.groupByCount(byLevel, 'level'),
    };

    return {
      period: { start: startTime.toISOString(), end: endTime.toISOString() },
      summary: {
        totalLogs,
        totalRequests: totalLogs,
        errorCount,
        warnCount,
        avgDuration: Math.round(avgDuration._avg.durationMs || 0),
        p95Duration: 0, // TODO
        p99Duration: 0, // TODO
        slowRequestCount: 0, // TODO
      },
      byLevel: byLevelMap,
      byService: this.groupByCount(byService, 'service'),
      byRegion: {}, // TODO
      byHour: [], // TODO
      diskUsage: {
        total: 'N/A',
        used: 'N/A',
        usedPercent: 0,
        byType: {},
      },
    };
  }

  // ==================== 告警管理 ====================

  /**
   * 获取告警历史
   */
  async getAlertHistory(query: QueryAlertHistoryDto): Promise<PaginatedResult<AlertItem> & { summary: any }> {
    this.validateTimeRange(query.startTime, query.endTime, 30);

    const page = query.page || 1;
    const limit = query.limit || 50;
    const skip = (page - 1) * limit;

    const where: Prisma.LogAlertWhereInput = {};

    const alertTimeRange = this.timeRangeWhere(query.startTime, query.endTime);
    if (alertTimeRange) where.createdAt = alertTimeRange;

    if (query.type) {
      where.type = query.type as LogAlertType;
    }
    if (query.status) {
      where.status = query.status as LogAlertStatus;
    }

    const [alerts, total, byType, byStatus] = await Promise.all([
      this.prisma.logAlert.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      this.prisma.logAlert.count({ where }),
      this.prisma.logAlert.groupBy({
        by: ['type'],
        where,
        _count: true,
      }),
      this.prisma.logAlert.groupBy({
        by: ['status'],
        where,
        _count: true,
      }),
    ]);

    const items: AlertItem[] = alerts.map(alert => ({
      id: alert.id,
      type: alert.type as unknown as DtoAlertType,
      severity: alert.severity as unknown as DtoAlertSeverity,
      message: alert.message,
      context: (alert.context as Record<string, any>) || {},
      sentAt: alert.createdAt.toISOString(),
      channels: alert.channels || [],
      status: alert.status as unknown as DtoAlertStatus,
    }));

    return {
      items,
      ...this.paginationMeta(page, limit, total),
      summary: {
        total,
        byType: this.groupByCount(byType, 'type'),
        byStatus: this.groupByCount(byStatus, 'status'),
      },
    };
  }

  /**
   * 获取告警配置
   */
  async getAlertConfig(): Promise<AlertConfig> {
    const config = await this.getConfig();
    return config.alerts;
  }

  /**
   * 更新告警配置
   */
  async updateAlertConfig(dto: UpdateAlertConfigDto, userId: string): Promise<AlertConfig> {
    const currentConfig = await this.getConfig();
    const newAlerts = { ...currentConfig.alerts };

    if (dto.slowRequest) {
      newAlerts.slowRequest = { ...newAlerts.slowRequest, ...dto.slowRequest };
    }
    if (dto.errorRate) {
      newAlerts.errorRate = { ...newAlerts.errorRate, ...dto.errorRate };
    }
    if (dto.ipAnomaly && newAlerts.ipAnomaly) {
      newAlerts.ipAnomaly = { ...newAlerts.ipAnomaly, ...dto.ipAnomaly };
    }
    if (dto.diskSpace) {
      newAlerts.diskSpace = { ...newAlerts.diskSpace, ...dto.diskSpace };
    }

    newAlerts.updatedAt = new Date().toISOString();
    newAlerts.updatedBy = userId;

    await this.prisma.logConfig.upsert({
      where: { key: 'alerts' },
      create: {
        key: 'alerts',
        value: newAlerts as any,
        updatedBy: userId,
      },
      update: {
        value: newAlerts as any,
        updatedBy: userId,
      },
    });

    return newAlerts;
  }

  /**
   * 测试告警通道
   */
  async testAlert(dto: TestAlertDto): Promise<any> {
    const { channel, message } = dto;
    const config = await this.getAlertConfig();
    const notifications = config.notifications;

    const isConfigured =
      (channel === 'slack' && !!notifications?.slack?.webhookConfigured) ||
      (channel === 'feishu' && !!notifications?.feishu?.webhookConfigured) ||
      (channel === 'email' && (notifications?.email?.recipientCount ?? 0) > 0);

    if (!isConfigured) {
      throw new BusinessException(
        `${channel} 通道未配置`,
        'LOG_ALERT_CHANNEL_INVALID',
        400,
      );
    }

    // 记录测试告警
    await this.prisma.logAlert.create({
      data: {
        type: LogAlertType.SLOW_REQUEST,
        severity: LogAlertSeverity.INFO,
        message: message || `测试告警 - ${channel}`,
        channels: [channel],
        status: LogAlertStatus.SENT,
        context: { test: true },
      },
    });

    return {
      channel,
      status: 'SUCCESS',
      sentAt: new Date().toISOString(),
    };
  }

  // ==================== 私有方法 ====================

  private validateTimeRange(startTime?: string, endTime?: string, maxDays: number = 7): void {
    if (startTime && endTime) {
      const start = new Date(startTime);
      const end = new Date(endTime);
      const diffDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);

      if (diffDays > maxDays) {
        throw new BusinessException(
          `时间范围过大，最大允许 ${maxDays} 天`,
          'LOG_QUERY_TIME_RANGE_TOO_LARGE',
          400,
        );
      }
    }
  }

  /**
   * 将数据库记录转换为 API 响应格式
   */
  private transformLogToItem(log: any): LogItem {
    return {
      '@timestamp': log.createdAt.toISOString(),
      level: log.level as DtoLogLevel,
      message: log.message || `${log.method || ''} ${log.url || ''} | ${log.statusCode || ''} | ${log.durationMs || 0}ms`,
      trace: {
        id: log.traceId,
        span_id: log.spanId || log.requestId,
        parent_span_id: log.parentSpanId,
      },
      http: {
        request_id: log.requestId,
        method: log.method || '',
        url: log.url || '',
        status_code: log.statusCode || 0,
        duration_ms: log.durationMs || 0,
      },
      user: log.userId ? {
        id: log.userId,
        name: log.userName || '',
      } : undefined,
      client: {
        ip: log.clientIp || '',
        user_agent: log.userAgent || '',
      },
      service: {
        name: log.service,
        region: log.region,
        instance: log.instance || '',
      },
      error: log.errorType ? {
        type: log.errorType,
        message: log.errorMessage || '',
        code: log.errorCode,
        // 防御性兜底：生产环境永不返回堆栈，避免历史数据 / 旁路写入泄漏
        stack: process.env.NODE_ENV === 'production' ? undefined : log.errorStack,
      } : undefined,
    };
  }
}
