/**
 * Alert Evaluator
 *
 * Evaluates monitoring alert rules against current system metrics.
 * Supports dot-notation metric paths, configurable conditions,
 * cooldown periods, and multi-channel alert dispatch.
 */

import type { Payload } from "payload";
import type { AlertCondition, AlertSeverity, SystemMetrics } from "./types";

// ============================================================================
// Pure Functions
// ============================================================================

/**
 * Resolves a dot-notation metric path against a metrics object.
 * Example: getMetricValue(metrics, 'system.cpuUsagePercent') => 92
 */
export function getMetricValue(metrics: Record<string, unknown>, path: string): number | undefined {
  const parts = path.split(".");
  let current: unknown = metrics;

  for (const part of parts) {
    if (current === null || current === undefined || typeof current !== "object") {
      return undefined;
    }
    current = (current as Record<string, unknown>)[part];
  }

  return typeof current === "number" ? current : undefined;
}

/**
 * Evaluates a condition against a value and threshold.
 */
export function evaluateCondition(condition: AlertCondition, value: number, threshold: number): boolean {
  switch (condition) {
    case "gt":
      return value > threshold;
    case "lt":
      return value < threshold;
    case "eq":
      return value === threshold;
    case "gte":
      return value >= threshold;
    case "lte":
      return value <= threshold;
    default:
      return false;
  }
}

// ============================================================================
// Types
// ============================================================================

interface AlertRule {
  id: number;
  name: string;
  metric: string;
  condition: AlertCondition;
  threshold: number;
  severity: AlertSeverity;
  channels: Array<"email" | "slack" | "discord">;
  recipients?: {
    emails?: Array<{ email: string }>;
    slackWebhook?: string;
    discordWebhook?: string;
  };
  cooldownMinutes: number;
  enabled: boolean;
}

// Maps AlertSeverity to the AlertLevel expected by alert-service
const SEVERITY_TO_LEVEL: Record<AlertSeverity, string> = {
  warning: "warning",
  error: "error",
  critical: "critical",
};

// ============================================================================
// AlertEvaluator Class
// ============================================================================

export class AlertEvaluator {
  /** Tracks last fire time per rule to enforce cooldown */
  private cooldownMap: Map<string, number> = new Map();

  /**
   * Returns true if the rule should fire (not in cooldown).
   */
  shouldFire(ruleId: string, cooldownMinutes: number): boolean {
    const lastFired = this.cooldownMap.get(ruleId);
    if (lastFired) {
      const elapsedMinutes = (Date.now() - lastFired) / 60_000;
      if (elapsedMinutes < cooldownMinutes) return false;
    }
    return true;
  }

  /** Record that a rule fired successfully. */
  recordFired(ruleId: string): void {
    this.cooldownMap.set(ruleId, Date.now());
  }

  /**
   * Evaluates all enabled rules against current metrics.
   * Fires alerts for rules that match and are not in cooldown.
   */
  async evaluateRules(payload: Payload, metrics: Omit<SystemMetrics, "timestamp">): Promise<void> {
    try {
      const rules = await payload.find({
        collection: "monitoring-alert-rules",
        where: { enabled: { equals: true } },
        limit: 100,
      });

      for (const doc of rules.docs) {
        const rule = doc as unknown as AlertRule;
        const value = getMetricValue(metrics as unknown as Record<string, unknown>, rule.metric);
        if (value === undefined) continue;

        if (evaluateCondition(rule.condition, value, rule.threshold)) {
          const ruleKey = String(rule.id);
          if (this.shouldFire(ruleKey, rule.cooldownMinutes)) {
            await this.dispatchAlert(payload, rule, value);
            this.recordFired(ruleKey);
          }
        }
      }
    } catch (error) {
      console.error("[AlertEvaluator] Error evaluating rules:", error);
    }
  }

  /**
   * Creates an alert history record and attempts to send notifications
   * via the existing alert service.
   */
  private async dispatchAlert(payload: Payload, rule: AlertRule, value: number): Promise<void> {
    const message = `${rule.name}: ${rule.metric} = ${value} (threshold: ${rule.condition} ${rule.threshold})`;

    try {
      await payload.create({
        collection: "monitoring-alert-history",
        data: {
          rule: rule.id,
          metric: rule.metric,
          value,
          threshold: rule.threshold,
          severity: rule.severity,
          message,
          channelsSent: rule.channels,
        },
      });

      // Try to send via existing alert service
      try {
        const { sendAlert } = await import("../alerting/alert-service.js");
        await sendAlert(payload, {
          level: SEVERITY_TO_LEVEL[rule.severity] as "warning" | "error" | "critical",
          title: `[${rule.severity.toUpperCase()}] ${rule.name}`,
          message,
          details: {
            metric: rule.metric,
            value,
            threshold: rule.threshold,
            condition: rule.condition,
          },
        });
      } catch {
        // Alert service not available, history record is sufficient
        console.warn(`[AlertEvaluator] Could not dispatch via alert-service: ${message}`);
      }
    } catch (error) {
      console.error("[AlertEvaluator] Error dispatching alert:", error);
    }
  }
}
