/**
 * Incident Tracking Service
 *
 * Manages incidents triggered by SLO breaches and integration failures.
 * Uses system_messages table with type='incident' for storage.
 *
 * Features:
 * - Auto-create incidents on SLO breach
 * - Incident severity classification
 * - Incident timeline/history
 * - On-call assignment
 * - Incident resolution tracking
 * - Auto-close after 24h of healthy status
 */

import { systemMessagesAdapter } from '@/lib/adapters/system';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';

// Incident severity levels
export type IncidentSeverity = 'critical' | 'warning' | 'info';

// Incident status
export type IncidentStatus = 'open' | 'acknowledged' | 'resolved';

// Incident types
export type IncidentType =
  | 'slo_breach'
  | 'integration_down'
  | 'high_error_rate'
  | 'queue_backlog'
  | 'latency_spike';

// Incident data shape
export interface Incident {
  id: string;
  type: IncidentType;
  severity: IncidentSeverity;
  title: string;
  description: string;
  component: string;
  status: IncidentStatus;
  acknowledgedBy: string | null;
  acknowledgedAt: Date | null;
  resolvedAt: Date | null;
  metadata: Record<string, unknown>;
  createdAt: Date;
  updatedAt: Date;
}

// Incident creation payload
export interface CreateIncidentPayload {
  type: IncidentType;
  severity: IncidentSeverity;
  title: string;
  description: string;
  component: string;
  metadata?: Record<string, unknown>;
  createdBy: string;
}

// Cooldown tracking to prevent alert storms
const incidentCooldowns = new Map<string, number>();
const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes between duplicate incidents

/**
 * Generate incident key for deduplication
 */
function getIncidentKey(type: IncidentType, component: string): string {
  return `${type}:${component}`;
}

/**
 * Check if incident is in cooldown period
 */
function isInCooldown(type: IncidentType, component: string): boolean {
  const key = getIncidentKey(type, component);
  const lastCreated = incidentCooldowns.get(key);
  if (!lastCreated) return false;
  return Date.now() - lastCreated < COOLDOWN_MS;
}

/**
 * Set cooldown for incident type
 */
function setCooldown(type: IncidentType, component: string): void {
  const key = getIncidentKey(type, component);
  incidentCooldowns.set(key, Date.now());
}

/**
 * Create a new incident
 */
export async function createIncident(
  payload: CreateIncidentPayload
): Promise<Incident | null> {
  // Check cooldown to prevent alert storms
  if (isInCooldown(payload.type, payload.component)) {
    logger.debug('Incident creation skipped - in cooldown', {
      type: payload.type,
      component: payload.component,
    });
    return null;
  }

  try {
    const id = createId();
    const now = new Date();

    // Store incident in system_messages with type='incident'
    const systemMessage = await systemMessagesAdapter.create({
      data: {
        id,
        type: `incident_${payload.severity}`,
        title: payload.title,
        message: JSON.stringify({
          description: payload.description,
          incidentType: payload.type,
          component: payload.component,
          status: 'open' as IncidentStatus,
          acknowledgedBy: null,
          acknowledgedAt: null,
          resolvedAt: null,
          metadata: payload.metadata || {},
        }),
        isActive: true,
        createdBy: payload.createdBy,
        createdAt: now,
        updatedAt: now,
        expiresAt: null, // Incidents don't expire automatically
      },
    });

    // Set cooldown
    setCooldown(payload.type, payload.component);

    logger.info('Incident created', {
      id: systemMessage.id,
      type: payload.type,
      severity: payload.severity,
      component: payload.component,
    });

    return parseIncidentFromSystemMessage(systemMessage);
  } catch (error) {
    logger.error('Failed to create incident', error);
    return null;
  }
}

/**
 * Parse incident data from system_message record
 */
function parseIncidentFromSystemMessage(
  record: {
    id: string;
    type: string;
    title: string | null;
    message: string;
    isActive: boolean;
    createdAt: Date;
    updatedAt: Date;
    createdBy: string;
    expiresAt: Date | null;
  }
): Incident {
  const messageData = JSON.parse(record.message) as {
    description: string;
    incidentType: IncidentType;
    component: string;
    status: IncidentStatus;
    acknowledgedBy: string | null;
    acknowledgedAt: string | null;
    resolvedAt: string | null;
    metadata: Record<string, unknown>;
  };

  // Extract severity from type (e.g., 'incident_critical' -> 'critical')
  const severity = record.type.replace(
    'incident_',
    ''
  ) as IncidentSeverity;

  return {
    id: record.id,
    type: messageData.incidentType,
    severity,
    title: record.title || 'Incident',
    description: messageData.description,
    component: messageData.component,
    status: messageData.status,
    acknowledgedBy: messageData.acknowledgedBy,
    acknowledgedAt: messageData.acknowledgedAt
      ? new Date(messageData.acknowledgedAt)
      : null,
    resolvedAt: messageData.resolvedAt
      ? new Date(messageData.resolvedAt)
      : null,
    metadata: messageData.metadata,
    createdAt: record.createdAt,
    updatedAt: record.updatedAt,
  };
}

/**
 * Get active incidents
 */
export async function getActiveIncidents(): Promise<Incident[]> {
  try {
    const records = await systemMessagesAdapter.findMany({
      where: {
        type: { startsWith: 'incident_' },
        isActive: true,
      },
      orderBy: { createdAt: 'desc' },
    });

    return records
      .map((record) => {
        try {
          return parseIncidentFromSystemMessage(record);
        } catch {
          return null;
        }
      })
      .filter((incident): incident is Incident => incident !== null)
      .filter((incident) => incident.status !== 'resolved');
  } catch (error) {
    logger.error('Failed to get active incidents', error);
    return [];
  }
}

/**
 * Get incident by ID
 */
export async function getIncidentById(id: string): Promise<Incident | null> {
  try {
    const record = await systemMessagesAdapter.findFirst({
      where: {
        id,
        type: { startsWith: 'incident_' },
      },
    });

    if (!record) return null;
    return parseIncidentFromSystemMessage(record);
  } catch (error) {
    logger.error('Failed to get incident', { id, error });
    return null;
  }
}

/**
 * Acknowledge an incident
 */
export async function acknowledgeIncident(
  id: string,
  acknowledgedBy: string
): Promise<Incident | null> {
  try {
    const record = await systemMessagesAdapter.findFirst({
      where: {
        id,
        type: { startsWith: 'incident_' },
      },
    });

    if (!record) return null;

    const messageData = JSON.parse(record.message);
    messageData.status = 'acknowledged';
    messageData.acknowledgedBy = acknowledgedBy;
    messageData.acknowledgedAt = new Date().toISOString();

    const updated = await systemMessagesAdapter.update({
      where: { id },
      data: {
        message: JSON.stringify(messageData),
        updatedAt: new Date(),
      },
    });

    logger.info('Incident acknowledged', { id, acknowledgedBy });
    return parseIncidentFromSystemMessage(updated);
  } catch (error) {
    logger.error('Failed to acknowledge incident', { id, error });
    return null;
  }
}

/**
 * Resolve an incident
 */
export async function resolveIncident(id: string): Promise<Incident | null> {
  try {
    const record = await systemMessagesAdapter.findFirst({
      where: {
        id,
        type: { startsWith: 'incident_' },
      },
    });

    if (!record) return null;

    const messageData = JSON.parse(record.message);
    messageData.status = 'resolved';
    messageData.resolvedAt = new Date().toISOString();

    const updated = await systemMessagesAdapter.update({
      where: { id },
      data: {
        message: JSON.stringify(messageData),
        isActive: false, // Resolved incidents become inactive
        updatedAt: new Date(),
      },
    });

    logger.info('Incident resolved', { id });
    return parseIncidentFromSystemMessage(updated);
  } catch (error) {
    logger.error('Failed to resolve incident', { id, error });
    return null;
  }
}

/**
 * Auto-create incident from SLO breach
 */
export async function createSLOBreachIncident(
  component: string,
  currentValue: number,
  targetValue: number,
  metric: string,
  systemUserId: string
): Promise<Incident | null> {
  const severity: IncidentSeverity =
    currentValue < targetValue * 0.9 ? 'critical' : 'warning';

  return createIncident({
    type: 'slo_breach',
    severity,
    title: `SLO Breach: ${metric}`,
    description: `${metric} is at ${currentValue.toFixed(2)}% (target: ${targetValue}%)`,
    component,
    metadata: {
      metric,
      currentValue,
      targetValue,
      breachAmount: targetValue - currentValue,
    },
    createdBy: systemUserId,
  });
}

/**
 * Auto-create incident from integration failure
 */
export async function createIntegrationDownIncident(
  integration: string,
  errorMessage: string,
  systemUserId: string
): Promise<Incident | null> {
  return createIncident({
    type: 'integration_down',
    severity: 'critical',
    title: `Integration Down: ${integration}`,
    description: `${integration} integration is experiencing failures: ${errorMessage}`,
    component: integration,
    metadata: {
      integration,
      errorMessage,
    },
    createdBy: systemUserId,
  });
}

/**
 * Auto-resolve incidents for a component after sustained healthy period
 * Should be called periodically (e.g., every 5 minutes)
 */
export async function autoResolveHealthyIncidents(
  component: string,
  healthyDurationMs: number = 24 * 60 * 60 * 1000 // 24 hours default
): Promise<number> {
  try {
    const cutoff = new Date(Date.now() - healthyDurationMs);

    const activeIncidents = await systemMessagesAdapter.findMany({
      where: {
        type: { startsWith: 'incident_' },
        isActive: true,
        updatedAt: { lte: cutoff },
      },
    });

    let resolvedCount = 0;
    for (const record of activeIncidents) {
      try {
        const messageData = JSON.parse(record.message);
        if (messageData.component === component && messageData.status !== 'resolved') {
          await resolveIncident(record.id);
          resolvedCount++;
        }
      } catch {
        // Skip invalid records
      }
    }

    if (resolvedCount > 0) {
      logger.info('Auto-resolved incidents', { component, count: resolvedCount });
    }

    return resolvedCount;
  } catch (error) {
    logger.error('Failed to auto-resolve incidents', { component, error });
    return 0;
  }
}

/**
 * Get incident history for a component
 */
export async function getIncidentHistory(
  component?: string,
  limit: number = 50
): Promise<Incident[]> {
  try {
    const records = await systemMessagesAdapter.findMany({
      where: {
        type: { startsWith: 'incident_' },
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });

    return records
      .map((record) => {
        try {
          return parseIncidentFromSystemMessage(record);
        } catch {
          return null;
        }
      })
      .filter((incident): incident is Incident => incident !== null)
      .filter((incident) => !component || incident.component === component);
  } catch (error) {
    logger.error('Failed to get incident history', error);
    return [];
  }
}

/**
 * Get incident statistics
 */
export async function getIncidentStats(): Promise<{
  total: number;
  open: number;
  acknowledged: number;
  resolved: number;
  bySeverity: Record<IncidentSeverity, number>;
  avgResolutionTimeMs: number | null;
}> {
  try {
    const records = await systemMessagesAdapter.findMany({
      where: {
        type: { startsWith: 'incident_' },
      },
    });

    const incidents = records
      .map((record) => {
        try {
          return parseIncidentFromSystemMessage(record);
        } catch {
          return null;
        }
      })
      .filter((incident): incident is Incident => incident !== null);

    const stats = {
      total: incidents.length,
      open: incidents.filter((i) => i.status === 'open').length,
      acknowledged: incidents.filter((i) => i.status === 'acknowledged').length,
      resolved: incidents.filter((i) => i.status === 'resolved').length,
      bySeverity: {
        critical: incidents.filter((i) => i.severity === 'critical').length,
        warning: incidents.filter((i) => i.severity === 'warning').length,
        info: incidents.filter((i) => i.severity === 'info').length,
      },
      avgResolutionTimeMs: null as number | null,
    };

    // Calculate average resolution time
    const resolvedIncidents = incidents.filter(
      (i) => i.status === 'resolved' && i.resolvedAt
    );
    if (resolvedIncidents.length > 0) {
      const totalResolutionTime = resolvedIncidents.reduce((sum, incident) => {
        if (incident.resolvedAt) {
          return (
            sum + (incident.resolvedAt.getTime() - incident.createdAt.getTime())
          );
        }
        return sum;
      }, 0);
      stats.avgResolutionTimeMs = Math.round(
        totalResolutionTime / resolvedIncidents.length
      );
    }

    return stats;
  } catch (error) {
    logger.error('Failed to get incident stats', error);
    return {
      total: 0,
      open: 0,
      acknowledged: 0,
      resolved: 0,
      bySeverity: { critical: 0, warning: 0, info: 0 },
      avgResolutionTimeMs: null,
    };
  }
}
