import {
  AlertSource,
  AlertCategory,
  Severity,
  EntityTypes,
  NormalizedAlertInput,
} from "@shieldai/types";

type EntityType = (typeof EntityTypes)[keyof typeof EntityTypes];

function sanitizePayload(
  payload: Record<string, unknown>,
  maxDepth: number = 5
): Record<string, unknown> {
  const seen = new WeakSet<object>();
  const clone = (obj: unknown, depth: number): unknown => {
    if (depth > maxDepth) return "[max depth]";
    if (obj === null || typeof obj !== "object") return obj;
    if (seen.has(obj as object)) return "[circular]";
    seen.add(obj as object);
    if (Array.isArray(obj)) return obj.map((item) => clone(item, depth + 1));
    return Object.fromEntries(
      Object.entries(obj as Record<string, unknown>).map(([k, v]) => [k, clone(v, depth + 1)])
    );
  };
  return clone(payload, 0) as Record<string, unknown>;
}

interface DarkWatchAlertPayload {
  exposureId: string;
  breachName: string;
  severity: string;
  channel: string;
  dataType?: string[];
  dataSource?: string;
}

interface SpamShieldAlertPayload {
  phoneNumber: string;
  decision: string;
  confidence: number;
  reasons?: string[];
  channel?: "call" | "sms";
  hiyaReputationScore?: number;
  truecallerSpamScore?: number;
}

interface VoicePrintAlertPayload {
  jobId: string;
  verdict: string;
  syntheticScore: number;
  confidence: number;
  matchedEnrollmentId?: string;
  matchedSimilarity?: number;
  analysisType?: string;
}

interface CallAnalysisAlertPayload {
  callId: string;
  eventType?: string;
  mosScore?: number;
  anomaly?: string;
  sentiment?: { label: string; score: number };
}

const SEVERITY_MAP: Record<string, Severity> = {
  LOW: "LOW",
  INFO: "INFO",
  MEDIUM: "MEDIUM",
  WARNING: "WARNING",
  HIGH: "HIGH",
  CRITICAL: "CRITICAL",
};

function mapSeverity(raw: string | number): Severity {
  if (typeof raw === "number") {
    if (raw >= 0.9) return "CRITICAL";
    if (raw >= 0.7) return "HIGH";
    if (raw >= 0.5) return "WARNING";
    if (raw >= 0.3) return "MEDIUM";
    if (raw >= 0.1) return "INFO";
    return "LOW";
  }
  const upper = raw.toUpperCase();
  return SEVERITY_MAP[upper] ?? "INFO";
}

export class AlertNormalizer {
  public normalizeDarkWatchAlert(
    userId: string,
    sourceAlertId: string,
    payload: DarkWatchAlertPayload,
    timestamp?: Date
  ): NormalizedAlertInput {
    const severity = mapSeverity(payload.severity);
    const entities: Array<{ type: EntityType; value: string }> = [];

    if (payload.dataSource) {
      entities.push({ type: EntityTypes.EMAIL, value: payload.breachName });
    }

    return {
      source: AlertSource.DARKWATCH,
      category: AlertCategory.BREACH_EXPOSURE,
      severity,
      userId,
      title: `Breach Exposure: ${payload.breachName}`,
      description: payload.dataType
        ? `Data types exposed: ${payload.dataType.join(", ")} in ${payload.breachName}`
        : `Exposure detected in ${payload.breachName}`,
      entities,
      sourceAlertId,
      payload: sanitizePayload(payload as unknown as Record<string, unknown>),
      timestamp,
    };
  }

  public normalizeSpamShieldAlert(
    userId: string,
    sourceAlertId: string,
    payload: SpamShieldAlertPayload,
    timestamp?: Date
  ): NormalizedAlertInput {
    const decision = payload.decision.toUpperCase();
    const severity =
      decision === "BLOCK"
        ? "HIGH"
        : decision === "FLAG"
        ? "WARNING"
        : "INFO";

    const channel = payload.channel === "sms" ? "sms" : "call";
    const category =
      channel === "sms"
        ? AlertCategory.SPAM_SMS
        : AlertCategory.SPAM_CALL;

    const entities: Array<{ type: EntityType; value: string }> = [
      { type: EntityTypes.PHONE_NUMBER, value: payload.phoneNumber },
    ];

    return {
      source: AlertSource.SPAMSHIELD,
      category,
      severity,
      userId,
      title: `${channel === "sms" ? "SMS" : "Call"} ${decision}: ${payload.phoneNumber}`,
      description: payload.reasons
        ? `SpamShield ${decision} decision. Reasons: ${payload.reasons.join(", ")}`
        : `SpamShield ${decision} decision with confidence ${Math.round(payload.confidence * 100)}%`,
      entities,
      sourceAlertId,
      payload: sanitizePayload(payload as unknown as Record<string, unknown>),
      timestamp,
    };
  }

  public normalizeVoicePrintAlert(
    userId: string,
    sourceAlertId: string,
    payload: VoicePrintAlertPayload,
    timestamp?: Date
  ): NormalizedAlertInput {
    const verdict = payload.verdict.toUpperCase();
    let severity: Severity;
    let category: AlertCategory;

    if (payload.analysisType === "VOICE_MATCH" && payload.matchedEnrollmentId) {
      category = AlertCategory.VOICE_MISMATCH;
      severity =
        payload.matchedSimilarity !== undefined && payload.matchedSimilarity > 0.85
          ? "MEDIUM"
          : "LOW";
    } else {
      category = AlertCategory.SYNTHETIC_VOICE;
      severity =
        verdict === "SYNTHETIC"
          ? mapSeverity(payload.syntheticScore)
          : verdict === "UNCERTAIN"
          ? "MEDIUM"
          : "INFO";
    }

    const entities: Array<{ type: EntityType; value: string }> = [];
    if (payload.matchedEnrollmentId) {
      entities.push({ type: EntityTypes.USER_ID, value: payload.matchedEnrollmentId });
    }

    return {
      source: AlertSource.VOICEPRINT,
      category,
      severity,
      userId,
      title: `Voice ${verdict}: Job ${payload.jobId}`,
      description: payload.analysisType
        ? `Analysis type: ${payload.analysisType}. Verdict: ${verdict} (confidence: ${Math.round(payload.confidence * 100)}%)`
        : `Synthetic voice detection: ${verdict} (score: ${payload.syntheticScore.toFixed(3)})`,
      entities,
      sourceAlertId,
      payload: sanitizePayload(payload as unknown as Record<string, unknown>),
      timestamp,
    };
  }

  public normalizeCallAnalysisAlert(
    userId: string,
    sourceAlertId: string,
    payload: CallAnalysisAlertPayload,
    timestamp?: Date
  ): NormalizedAlertInput {
    let category: AlertCategory;
    let severity: Severity;
    let title: string;
    let description: string;

    if (payload.anomaly) {
      category = AlertCategory.CALL_ANOMALY;
      severity = "WARNING";
      title = `Call Anomaly: ${payload.anomaly}`;
      description = `Anomaly "${payload.anomaly}" detected in call ${payload.callId}`;
    } else if (payload.mosScore !== undefined) {
      category = AlertCategory.CALL_QUALITY;
      severity =
        payload.mosScore < 2.5
          ? "CRITICAL"
          : payload.mosScore < 3.5
          ? "HIGH"
          : payload.mosScore < 4.0
          ? "MEDIUM"
          : "INFO";
      title = `Call Quality: MOS ${payload.mosScore.toFixed(1)}`;
      description = `MOS score ${payload.mosScore.toFixed(1)} for call ${payload.callId}`;
    } else if (payload.eventType) {
      category = AlertCategory.CALL_EVENT;
      severity = "INFO";
      title = `Call Event: ${payload.eventType}`;
      description = `Event "${payload.eventType}" during call ${payload.callId}`;
    } else {
      category = AlertCategory.CALL_EVENT;
      severity = "INFO";
      title = `Call Alert: ${payload.callId}`;
      description = `Alert for call ${payload.callId}`;
    }

    const entities: Array<{ type: EntityType; value: string }> = [
      { type: EntityTypes.CALL_ID, value: payload.callId },
    ];

    return {
      source: AlertSource.CALL_ANALYSIS,
      category,
      severity,
      userId,
      title,
      description,
      entities,
      sourceAlertId,
      payload: sanitizePayload(payload as unknown as Record<string, unknown>),
      timestamp,
    };
  }
}

export const alertNormalizer = new AlertNormalizer();
