import type { ServiceConfig, ServiceState } from './types.js';

const states = new Map<string, ServiceState>();

function createInitialState(): ServiceState {
  return {
    consecutiveFailures: 0,
    consecutiveSuccesses: 0,
    lastAlertSentAt: null,
    lastStatus: 'unknown',
    transitions: [],
    autoFixAttempts: 0,
    lastAutoFixAt: null,
    incidentId: null,
    isFlapping: false,
    flappingAlertSent: false,
    firstFailureAt: null,
  };
}

export function getState(serviceId: string): ServiceState {
  let state = states.get(serviceId);
  if (!state) {
    state = createInitialState();
    states.set(serviceId, state);
  }
  return state;
}

export function recordSuccess(serviceId: string): void {
  const state = getState(serviceId);
  state.consecutiveFailures = 0;
  state.consecutiveSuccesses++;
  state.firstFailureAt = null;

  if (state.lastStatus === 'down') {
    state.transitions.push({ timestamp: Date.now(), toStatus: 'ok' });
  }
  state.lastStatus = 'ok';
}

export function recordFailure(serviceId: string): void {
  const state = getState(serviceId);
  state.consecutiveFailures++;
  state.consecutiveSuccesses = 0;

  if (state.firstFailureAt === null) {
    state.firstFailureAt = Date.now();
  }

  if (state.lastStatus !== 'down') {
    state.transitions.push({ timestamp: Date.now(), toStatus: 'down' });
  }
  state.lastStatus = 'down';
}

export function resetIncident(serviceId: string): void {
  const state = getState(serviceId);
  state.autoFixAttempts = 0;
  state.lastAutoFixAt = null;
  state.incidentId = null;
  state.isFlapping = false;
  state.flappingAlertSent = false;
}

export function isFlapping(serviceId: string, windowMs: number, threshold: number): boolean {
  const state = getState(serviceId);
  const now = Date.now();

  // Prune old transitions
  state.transitions = state.transitions.filter((t) => now - t.timestamp < windowMs);

  const flapping = state.transitions.length >= threshold;
  state.isFlapping = flapping;
  return flapping;
}

export function shouldSendAlert(
  serviceId: string,
  failuresBeforeAlert: number,
  cooldownMs: number,
): boolean {
  const state = getState(serviceId);

  if (state.consecutiveFailures < failuresBeforeAlert) return false;
  if (state.isFlapping && state.flappingAlertSent) return false;

  if (state.lastAlertSentAt !== null) {
    const elapsed = Date.now() - state.lastAlertSentAt;
    if (elapsed < cooldownMs) return false;
  }

  return true;
}

export function markAlertSent(serviceId: string): void {
  const state = getState(serviceId);
  state.lastAlertSentAt = Date.now();
}

export function markFlappingAlertSent(serviceId: string): void {
  const state = getState(serviceId);
  state.flappingAlertSent = true;
}

export function canAutoFix(
  serviceId: string,
  maxAttempts: number,
  cooldownMs: number,
): boolean {
  const state = getState(serviceId);

  if (state.autoFixAttempts >= maxAttempts) return false;

  if (state.lastAutoFixAt !== null) {
    const elapsed = Date.now() - state.lastAutoFixAt;
    if (elapsed < cooldownMs) return false;
  }

  return true;
}

export function recordAutoFix(serviceId: string): void {
  const state = getState(serviceId);
  state.autoFixAttempts++;
  state.lastAutoFixAt = Date.now();
}

export function wasJustRecovered(serviceId: string): boolean {
  const state = getState(serviceId);
  return state.consecutiveSuccesses === 1 && state.incidentId !== null;
}
