import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { MonitorAlertEntity } from './monitor-alert.entity';
import { MonitorRunEntity } from './monitor-run.entity';
import { MonitorTargetConfig } from './monitoring.types';
import { LogAnalysisService } from './log-analysis.service';
import { MonitorAlertNotificationService } from './monitor-alert-notification.service';
import { LogSeverity } from './log-analysis.types';

@Injectable()
export class MonitorAlertService {
  constructor(
    @InjectRepository(MonitorAlertEntity)
    private readonly alertRepository: Repository<MonitorAlertEntity>,
    private readonly logAnalysisService: LogAnalysisService,
    private readonly notificationService: MonitorAlertNotificationService,
  ) {}

  async syncAlertsForRun(
    run: MonitorRunEntity,
    config: MonitorTargetConfig,
    fallbackLogs: Parameters<LogAnalysisService['analyzeRun']>[2],
  ) {
    const analysis = await this.logAnalysisService.analyzeRun(run, config, fallbackLogs);
    const now = new Date();
    type AlertCandidate = {
      category: string;
      title: string;
      severity: LogSeverity;
      sourceType: 'run-status' | 'log-analysis';
      summary: string;
      details: Record<string, unknown>;
    };

    const candidates: AlertCandidate[] = [
      ...(run.status === 'failed' || run.status === 'partial'
        ? [
            {
              category: run.errorTag ?? `run-${run.status}`,
              title: run.summary ?? `${run.target.name} 监测异常`,
              severity: (run.status === 'failed' ? 'major' : 'warning') as LogSeverity,
              sourceType: 'run-status' as const,
              summary: run.failureReason ?? run.summary ?? '监测结果异常',
              details: {
                runId: run.id,
                traceId: run.traceId,
                status: run.status,
                errorTag: run.errorTag,
              } as Record<string, unknown>,
            },
          ]
        : []),
      ...analysis.issues.map((issue) => ({
        category: issue.category,
        title: issue.title,
        severity: issue.severity,
        sourceType: 'log-analysis' as const,
        summary: issue.summary,
        details: issue,
      })),
    ];

    if (!candidates.length) {
      await this.resolveOpenAlertsForTarget(
        run.targetId,
        '监测已恢复正常，自动关闭关联告警。',
      );
      return { analysis, alerts: [] };
    }

    const alerts: MonitorAlertEntity[] = [];
    for (const candidate of candidates) {
      const existing = await this.alertRepository.findOne({
        where: {
          targetId: run.targetId,
          category: candidate.category,
          status: 'open',
        },
        order: { lastDetectedAt: 'DESC' },
      });
      const alert =
        existing ??
        this.alertRepository.create({
          targetId: run.targetId,
          runId: run.id,
          serviceKey: run.target.serviceKey,
          title: candidate.title,
          category: candidate.category,
          severity: candidate.severity,
          status: 'open',
          sourceType: candidate.sourceType,
          summary: candidate.summary,
          detailsJson: null,
          firstDetectedAt: now,
          lastDetectedAt: now,
          occurrenceCount: 0,
          notifiedAt: null,
          notificationStatus: null,
          notificationMessage: null,
        });

      alert.runId = run.id;
      alert.title = candidate.title;
      alert.severity = candidate.severity;
      alert.summary = candidate.summary;
      alert.sourceType = candidate.sourceType;
      alert.detailsJson = JSON.stringify(candidate.details);
      alert.lastDetectedAt = now;
      alert.occurrenceCount = (alert.occurrenceCount ?? 0) + 1;
      if (alert.status === 'resolved') {
        alert.status = 'open';
      }
      await this.alertRepository.save(alert);

      const notifyResult = await this.notificationService.notifyAlert(alert);
      if (notifyResult.attempted) {
        alert.notifiedAt = new Date();
      }
      alert.notificationStatus = notifyResult.status;
      alert.notificationMessage = notifyResult.message;
      await this.alertRepository.save(alert);
      alerts.push(alert);
    }

    return { analysis, alerts };
  }

  async getAlerts(filters: {
    status?: 'open' | 'acknowledged' | 'resolved';
    severity?: 'info' | 'warning' | 'major' | 'critical';
    targetId?: string;
    limit?: number;
  }) {
    const query = this.alertRepository
      .createQueryBuilder('alert')
      .leftJoinAndSelect('alert.target', 'target')
      .orderBy('alert.lastDetectedAt', 'DESC');
    if (filters.status) query.andWhere('alert.status = :status', { status: filters.status });
    if (filters.severity) query.andWhere('alert.severity = :severity', { severity: filters.severity });
    if (filters.targetId) query.andWhere('alert.targetId = :targetId', { targetId: filters.targetId });
    const take = Math.min(Math.max(filters.limit ?? 100, 1), 300);
    const [list, total] = await query.take(take).getManyAndCount();
    return { list, total };
  }

  async getAlertDetail(alertId: string) {
    const alert = await this.alertRepository.findOne({
      where: { id: alertId },
      relations: { target: true, run: true },
    });
    if (!alert) throw new NotFoundException('告警事件不存在');
    return {
      alert,
      detail: {
        details: this.parseJson(alert.detailsJson),
      },
    };
  }

  async updateAlertStatus(
    alertId: string,
    status: 'open' | 'acknowledged' | 'resolved',
  ) {
    const alert = await this.alertRepository.findOneBy({ id: alertId });
    if (!alert) throw new NotFoundException('告警事件不存在');
    alert.status = status;
    await this.alertRepository.save(alert);
    return alert;
  }

  private async resolveOpenAlertsForTarget(targetId: string, message: string) {
    const list = await this.alertRepository.find({
      where: [{ targetId, status: 'open' }, { targetId, status: 'acknowledged' }],
    });
    for (const item of list) {
      item.status = 'resolved';
      item.notificationStatus = item.notificationStatus ?? 'auto-resolved';
      item.notificationMessage = message;
    }
    if (list.length) await this.alertRepository.save(list);
  }

  private parseJson(raw: string | null) {
    try {
      return raw ? JSON.parse(raw) : {};
    } catch {
      return {};
    }
  }
}
