import fs from 'fs';
import path from 'path';

import {
  DATA_DIR,
  POLL_INTERVAL,
  SCHEDULER_POLL_INTERVAL,
} from './config.js';

const HEALTH_DIR = path.join(DATA_DIR, 'health');

type HealthSection = 'host' | 'pipeline' | 'warden';

interface HealthBase {
  updated_at: string;
}

export interface HostHealth extends HealthBase {
  process_started_at?: string;
  db_initialized_at?: string;
  channels_connected_at?: string;
  ipc_watcher_started_at?: string;
  message_loop_heartbeat_at?: string;
  scheduler_heartbeat_at?: string;
  assistant_name?: string;
  pid?: number;
  db_writable?: boolean;
}

export interface PipelineHealth extends HealthBase {
  telegram_connected_at?: string;
  last_inbound_received_at?: string;
  last_inbound_persisted_at?: string;
  last_routed_at?: string;
  last_run_started_at?: string;
  last_run_finished_at?: string;
  last_reply_sent_at?: string;
  last_reply_failed_at?: string;
  last_registered_chat_jid?: string;
  last_routed_chat_jid?: string;
  last_run_chat_jid?: string;
  last_reply_chat_jid?: string;
  last_failure_code?: string;
  last_failure_message?: string;
}

export interface WardenHealth extends HealthBase {
  last_run_started_at?: string;
  last_run_finished_at?: string;
  last_success_at?: string;
  last_failure_at?: string;
  last_group_folder?: string;
  last_chat_jid?: string;
  last_run_id?: string;
  last_error_code?: string;
  last_error_message?: string;
  last_duration_ms?: number;
  last_exit_code?: number | null;
}

export interface FullHealthSnapshot {
  host: HostHealth;
  pipeline: PipelineHealth;
  warden: WardenHealth;
}

function nowIso(): string {
  return new Date().toISOString();
}

function ensureHealthDir(): void {
  fs.mkdirSync(HEALTH_DIR, { recursive: true });
}

function sectionPath(section: HealthSection): string {
  return path.join(HEALTH_DIR, `${section}.json`);
}

function readSection<T extends HealthBase>(section: HealthSection): T {
  ensureHealthDir();
  const file = sectionPath(section);
  if (!fs.existsSync(file)) {
    return { updated_at: nowIso() } as T;
  }

  try {
    return JSON.parse(fs.readFileSync(file, 'utf-8')) as T;
  } catch {
    return { updated_at: nowIso() } as T;
  }
}

function writeSection<T extends HealthBase>(
  section: HealthSection,
  patch: Partial<T>,
): void {
  ensureHealthDir();
  const current = readSection<T>(section);
  const next = {
    ...current,
    ...patch,
    updated_at: nowIso(),
  };
  fs.writeFileSync(sectionPath(section), JSON.stringify(next, null, 2) + '\n');
}

export function readHealthSnapshot(): FullHealthSnapshot {
  return {
    host: readSection<HostHealth>('host'),
    pipeline: readSection<PipelineHealth>('pipeline'),
    warden: readSection<WardenHealth>('warden'),
  };
}

export function markHostStarted(assistantName: string): void {
  writeSection<HostHealth>('host', {
    process_started_at: nowIso(),
    assistant_name: assistantName,
    pid: process.pid,
  });
}

export function markDbInitialized(): void {
  writeSection<HostHealth>('host', {
    db_initialized_at: nowIso(),
    db_writable: true,
  });
}

export function markChannelsConnected(): void {
  writeSection<HostHealth>('host', {
    channels_connected_at: nowIso(),
  });
}

export function markIpcWatcherStarted(): void {
  writeSection<HostHealth>('host', {
    ipc_watcher_started_at: nowIso(),
  });
}

export function heartbeatMessageLoop(): void {
  writeSection<HostHealth>('host', {
    message_loop_heartbeat_at: nowIso(),
  });
}

export function heartbeatScheduler(): void {
  writeSection<HostHealth>('host', {
    scheduler_heartbeat_at: nowIso(),
  });
}

export function markTelegramConnected(): void {
  writeSection<PipelineHealth>('pipeline', {
    telegram_connected_at: nowIso(),
  });
}

export function markInboundReceived(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_inbound_received_at: nowIso(),
    last_registered_chat_jid: chatJid,
  });
}

export function markInboundPersisted(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_inbound_persisted_at: nowIso(),
    last_registered_chat_jid: chatJid,
  });
}

export function markMessageRouted(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_routed_at: nowIso(),
    last_routed_chat_jid: chatJid,
  });
}

export function markRunStarted(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_run_started_at: nowIso(),
    last_run_chat_jid: chatJid,
  });
}

export function markRunFinished(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_run_finished_at: nowIso(),
    last_run_chat_jid: chatJid,
  });
}

export function markReplySent(chatJid: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_reply_sent_at: nowIso(),
    last_reply_chat_jid: chatJid,
  });
}

export function markPipelineFailure(code: string, message: string): void {
  writeSection<PipelineHealth>('pipeline', {
    last_reply_failed_at: nowIso(),
    last_failure_code: code,
    last_failure_message: message,
  });
}

export function markWardenRunStarted(
  groupFolder: string,
  chatJid: string,
  jailRunId: string,
): void {
  writeSection<WardenHealth>('warden', {
    last_run_started_at: nowIso(),
    last_group_folder: groupFolder,
    last_chat_jid: chatJid,
    last_run_id: jailRunId,
  });
}

export function markWardenRunFinished(
  groupFolder: string,
  chatJid: string,
  jailRunId: string,
  result: {
    success: boolean;
    durationMs: number;
    errorCode?: string;
    errorMessage?: string;
    exitCode?: number | null;
  },
): void {
  const timestamp = nowIso();
  writeSection<WardenHealth>('warden', {
    last_run_finished_at: timestamp,
    last_group_folder: groupFolder,
    last_chat_jid: chatJid,
    last_run_id: jailRunId,
    last_duration_ms: result.durationMs,
    last_exit_code: result.exitCode ?? null,
    ...(result.success
      ? {
          last_success_at: timestamp,
        }
      : {
          last_failure_at: timestamp,
          last_error_code: result.errorCode,
          last_error_message: result.errorMessage,
        }),
  });
}

function ageMs(timestamp?: string): number | null {
  if (!timestamp) return null;
  const value = Date.parse(timestamp);
  if (Number.isNaN(value)) return null;
  return Date.now() - value;
}

export interface HealthAssessment {
  status: 'ok' | 'warn' | 'error';
  issues: string[];
  snapshot: FullHealthSnapshot;
}

export function assessHealth(snapshot = readHealthSnapshot()): HealthAssessment {
  const issues: string[] = [];
  let status: HealthAssessment['status'] = 'ok';

  const messageLoopAge = ageMs(snapshot.host.message_loop_heartbeat_at);
  const schedulerAge = ageMs(snapshot.host.scheduler_heartbeat_at);

  if (!snapshot.host.process_started_at) {
    issues.push('host process has not recorded startup');
    status = 'error';
  }

  if (messageLoopAge === null || messageLoopAge > POLL_INTERVAL * 5) {
    issues.push('message loop heartbeat is stale');
    status = 'error';
  }

  if (
    schedulerAge === null ||
    schedulerAge > Math.max(SCHEDULER_POLL_INTERVAL * 2, 120000)
  ) {
    issues.push('scheduler heartbeat is stale');
    status = status === 'error' ? 'error' : 'warn';
  }

  if (!snapshot.pipeline.telegram_connected_at) {
    issues.push('Telegram channel has not recorded a successful connect');
    status = 'error';
  }

  if (
    snapshot.pipeline.last_failure_code &&
    snapshot.pipeline.last_reply_failed_at
  ) {
    issues.push(
      `last pipeline failure: ${snapshot.pipeline.last_failure_code} (${snapshot.pipeline.last_failure_message || 'no message'})`,
    );
    if (status === 'ok') status = 'warn';
  }

  return { status, issues, snapshot };
}
