/**
 * Sidecar health monitoring and alerting system.
 *
 * Proactively monitors Python sidecar health with periodic checks,
 * failure alerting, and automatic recovery detection.
 *
 * Features:
 *   - Periodic health checks (configurable interval)
 *   - Alert hooks for downstream notifications
 *   - Health status history
 *   - Automatic recovery detection
 */

import { sidecarHealth, type SidecarHealthResponse } from '../../lib/python-bridge.js';

// ── Configuration ────────────────────────────────────────────────

const CHECK_INTERVAL_MS = parseInt(process.env.SIDECAR_HEALTH_INTERVAL_MS || '30000', 10); // 30s
const ALERT_THRESHOLD = parseInt(process.env.SIDECAR_ALERT_THRESHOLD || '3', 10); // consecutive failures
const MAX_HISTORY = 100;

// ── Types ───────────────────────────────────────────────────────

export type SidecarHealthStatus = 'healthy' | 'unhealthy' | 'degraded' | 'unknown';

export interface HealthCheck {
  timestamp: string;
  status: SidecarHealthStatus;
  response?: SidecarHealthResponse;
  error?: string;
  responseTimeMs: number;
}

export interface SidecarHealthState {
  current: SidecarHealthStatus;
  lastCheckAt?: string;
  lastSuccessAt?: string;
  lastFailureAt?: string;
  consecutiveFailures: number;
  consecutiveSuccesses: number;
  totalChecks: number;
  totalFailures: number;
  history: HealthCheck[];
}

export interface AlertConfig {
  onUnhealthy?: (state: SidecarHealthState, check: HealthCheck) => void;
  onRecovered?: (state: SidecarHealthState, check: HealthCheck) => void;
  onDegraded?: (state: SidecarHealthState, check: HealthCheck) => void;
}

// ── State ─────────────────────────────────────────────────────────

const state: SidecarHealthState = {
  current: 'unknown',
  consecutiveFailures: 0,
  consecutiveSuccesses: 0,
  totalChecks: 0,
  totalFailures: 0,
  history: [],
};

let alertConfig: AlertConfig = {};
let checkInterval: ReturnType<typeof setInterval> | null = null;

// ── Core functions ──────────────────────────────────────────────

/**
 * Perform a single health check.
 */
async function performHealthCheck(): Promise<HealthCheck> {
  const start = performance.now();
  const timestamp = new Date().toISOString();

  try {
    const response = await sidecarHealth();
    const responseTimeMs = Math.round(performance.now() - start);

    const check: HealthCheck = {
      timestamp,
      status: 'healthy',
      response,
      responseTimeMs,
    };

    return check;
  } catch (err) {
    const responseTimeMs = Math.round(performance.now() - start);
    const error = err instanceof Error ? err.message : 'Unknown error';

    return {
      timestamp,
      status: 'unhealthy',
      error,
      responseTimeMs,
    };
  }
}

/**
 * Update state based on health check result.
 */
function updateState(check: HealthCheck): void {
  state.totalChecks++;
  state.lastCheckAt = check.timestamp;
  state.history.unshift(check);

  // Trim history
  if (state.history.length > MAX_HISTORY) {
    state.history = state.history.slice(0, MAX_HISTORY);
  }

  if (check.status === 'healthy') {
    state.consecutiveSuccesses++;
    state.consecutiveFailures = 0;
    state.lastSuccessAt = check.timestamp;

    // Check for recovery
    if (state.current === 'unhealthy' || state.current === 'degraded') {
      state.current = 'healthy';
      alertConfig.onRecovered?.(state, check);
    } else {
      state.current = 'healthy';
    }
  } else {
    state.consecutiveFailures++;
    state.consecutiveSuccesses = 0;
    state.totalFailures++;
    state.lastFailureAt = check.timestamp;

    // Determine status
    if (state.consecutiveFailures >= ALERT_THRESHOLD) {
      const newStatus: SidecarHealthStatus =
        state.consecutiveFailures >= ALERT_THRESHOLD * 2 ? 'unhealthy' : 'degraded';

      if (state.current !== newStatus) {
        state.current = newStatus;
        if (newStatus === 'unhealthy') {
          alertConfig.onUnhealthy?.(state, check);
        } else {
          alertConfig.onDegraded?.(state, check);
        }
      }
    } else if (state.current === 'healthy') {
      state.current = 'degraded';
    }
  }
}

/**
 * Run a single health check cycle.
 */
async function runHealthCheck(): Promise<void> {
  const check = await performHealthCheck();
  updateState(check);
}

// ── Public API ────────────────────────────────────────────────────

/**
 * Start periodic health monitoring.
 */
export function startHealthMonitoring(config?: AlertConfig): void {
  if (config) {
    alertConfig = config;
  }

  if (checkInterval) {
    clearInterval(checkInterval);
  }

  // Initial check
  runHealthCheck().catch(() => {
    // Silent failure on initial check
  });

  // Periodic checks
  checkInterval = setInterval(() => {
    runHealthCheck().catch(() => {
      // Silent failure - error is recorded in state
    });
  }, CHECK_INTERVAL_MS);
}

/**
 * Stop health monitoring.
 */
export function stopHealthMonitoring(): void {
  if (checkInterval) {
    clearInterval(checkInterval);
    checkInterval = null;
  }
}

/**
 * Get current health state.
 */
export function getHealthState(): SidecarHealthState {
  return { ...state };
}

/**
 * Get health check history.
 */
export function getHealthHistory(limit = 10): HealthCheck[] {
  return state.history.slice(0, limit);
}

/**
 * Perform immediate health check.
 */
export async function checkHealthNow(): Promise<HealthCheck> {
  const check = await performHealthCheck();
  updateState(check);
  return check;
}

/**
 * Check if sidecar is currently healthy.
 */
export function isHealthy(): boolean {
  return state.current === 'healthy';
}

/**
 * Get health summary for metrics/reporting.
 */
export function getHealthSummary(): {
  status: SidecarHealthStatus;
  uptime: number;
  availability: number;
  avgResponseTimeMs: number;
  checkIntervalMs: number;
  lastCheckAt?: string;
  lastSuccessAt?: string;
  lastFailureAt?: string;
} {
  const recent = state.history.slice(0, 20);
  const responseTimes = recent.filter(h => h.status === 'healthy').map(h => h.responseTimeMs);
  const avgResponseTimeMs =
    responseTimes.length > 0
      ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length)
      : 0;

  const availability =
    state.totalChecks > 0
      ? Math.round(((state.totalChecks - state.totalFailures) / state.totalChecks) * 1000) / 10
      : 100;

  // Calculate uptime percentage based on consecutive successes/failures
  const uptime = state.current === 'healthy' ? 100 : state.current === 'degraded' ? 50 : 0;

  return {
    status: state.current,
    uptime,
    availability,
    avgResponseTimeMs,
    checkIntervalMs: CHECK_INTERVAL_MS,
    lastCheckAt: state.lastCheckAt,
    lastSuccessAt: state.lastSuccessAt,
    lastFailureAt: state.lastFailureAt,
  };
}

/**
 * Reset health state (for testing).
 */
export function resetHealthState(): void {
  state.current = 'unknown';
  state.lastCheckAt = undefined;
  state.lastSuccessAt = undefined;
  state.lastFailureAt = undefined;
  state.consecutiveFailures = 0;
  state.consecutiveSuccesses = 0;
  state.totalChecks = 0;
  state.totalFailures = 0;
  state.history = [];
}

// ── Default console alerting (development) ──────────────────────

if (process.env.NODE_ENV === 'development') {
  /* eslint-disable no-console -- Development-only sidecar health alerts are intentionally surfaced in the local service console. */
  alertConfig = {
    onUnhealthy: (state, check) => {
      console.error(
        `[sidecar-health] ALERT: Sidecar unhealthy after ${state.consecutiveFailures} consecutive failures`,
        {
          error: check.error,
          responseTime: check.responseTimeMs,
        }
      );
    },
    onRecovered: (state, check) => {
      console.log(
        `[sidecar-health] RECOVERED: Sidecar is healthy after ${state.consecutiveSuccesses} consecutive successes`,
        {
          responseTime: check.responseTimeMs,
        }
      );
    },
    onDegraded: (state, check) => {
      console.warn(
        `[sidecar-health] WARNING: Sidecar degraded after ${state.consecutiveFailures} failures`,
        {
          error: check.error,
          responseTime: check.responseTimeMs,
        }
      );
    },
  };
  /* eslint-enable no-console */
}
