import type { ServiceStatus, ServiceHealth, PlatformStatus } from './types.js';

// ── Service Definitions ──────────────────────────────────────
// Services to health-check. URLs are resolved from env vars at runtime.

interface ServiceDef {
  name: string;
  envVar: string;
  defaultUrl?: string;
  healthPath: string;
}

const SERVICE_DEFS: ServiceDef[] = [
  {
    name: 'Platform Service',
    envVar: 'PLATFORM_SERVICE_URL',
    defaultUrl: 'http://localhost:4003',
    healthPath: '/health',
  },
  {
    name: 'Extraction Service',
    envVar: 'EXTRACTION_SERVICE_URL',
    defaultUrl: 'http://localhost:4005',
    healthPath: '/health',
  },
  {
    name: 'MCP Server',
    envVar: 'MCP_SERVER_URL',
    defaultUrl: 'http://localhost:4007',
    healthPath: '/health',
  },
  {
    name: 'Mailpit',
    envVar: 'MAILPIT_UI_URL',
    defaultUrl: 'http://mailpit:8025',
    healthPath: '/',
  },
  {
    name: 'Backend API',
    envVar: 'BACKEND_URL',
    defaultUrl: undefined,
    healthPath: '/health',
  },
];

function getEnabledServiceDefs(): ServiceDef[] {
  return SERVICE_DEFS.filter(def => {
    const configuredUrl = process.env[def.envVar]?.trim();
    return Boolean(configuredUrl || def.defaultUrl);
  });
}

/**
 * Check health of a single service.
 */
async function checkService(def: ServiceDef): Promise<ServiceStatus> {
  const configuredUrl = process.env[def.envVar]?.trim();
  const baseUrl = configuredUrl || def.defaultUrl;
  if (!baseUrl) {
    return {
      name: def.name,
      url: 'not_configured',
      health: 'maintenance',
      responseTimeMs: null,
      lastCheckedAt: new Date().toISOString(),
      error: `${def.envVar} is not configured`,
    };
  }

  const url = `${baseUrl}${def.healthPath}`;
  const start = Date.now();

  try {
    const controller = new globalThis.AbortController();
    const timeout = globalThis.setTimeout(() => controller.abort(), 5_000);

    const response = await fetch(url, {
      method: 'GET',
      signal: controller.signal,
    });

    globalThis.clearTimeout(timeout);
    const responseTimeMs = Date.now() - start;

    let health: ServiceHealth = 'operational';
    if (!response.ok) {
      health = response.status >= 500 ? 'major_outage' : 'degraded';
    } else if (responseTimeMs > 3000) {
      health = 'degraded';
    }

    return {
      name: def.name,
      url: baseUrl,
      health,
      responseTimeMs,
      lastCheckedAt: new Date().toISOString(),
    };
  } catch (err: unknown) {
    return {
      name: def.name,
      url: baseUrl,
      health: 'major_outage',
      responseTimeMs: null,
      lastCheckedAt: new Date().toISOString(),
      error: err instanceof Error ? err.message : String(err),
    };
  }
}

/**
 * Derive overall platform health from individual service statuses.
 */
function deriveOverallHealth(services: ServiceStatus[]): ServiceHealth {
  const hasOutage = services.some(s => s.health === 'major_outage');
  const hasPartial = services.some(s => s.health === 'partial_outage');
  const hasDegraded = services.some(s => s.health === 'degraded');
  const hasMaintenance = services.some(s => s.health === 'maintenance');

  if (hasOutage) return 'major_outage';
  if (hasPartial) return 'partial_outage';
  if (hasDegraded) return 'degraded';
  if (hasMaintenance) return 'maintenance';
  return 'operational';
}

/**
 * Check all services and return platform-wide status.
 */
export async function checkAllServices(): Promise<PlatformStatus> {
  const enabledDefs = getEnabledServiceDefs();
  const results = await Promise.allSettled(enabledDefs.map(checkService));

  const services: ServiceStatus[] = results.map((result, i) => {
    if (result.status === 'fulfilled') return result.value;
    return {
      name: enabledDefs[i].name,
      url: enabledDefs[i].defaultUrl ?? 'not_configured',
      health: 'major_outage' as ServiceHealth,
      responseTimeMs: null,
      lastCheckedAt: new Date().toISOString(),
      error: result.reason instanceof Error ? result.reason.message : String(result.reason),
    };
  });

  return {
    overall: deriveOverallHealth(services),
    services,
    checkedAt: new Date().toISOString(),
  };
}
