import {
  app,
  BrowserWindow,
  ipcMain
} from 'electron';
import { collectAppMetricsSnapshot } from '../app-metrics';
import { sumWorkingSetKb } from './process-metrics.rules';
import { isPerfDiagEnabled } from './diagnostics.flags';
import type { PerfDiagEntry } from './diagnostics.models';
import { PerfDiagWriter } from './diagnostics.writer';

const PROCESS_POLL_INTERVAL_MS = 5_000;

let activeWriter: PerfDiagWriter | null = null;
let processPollTimer: NodeJS.Timeout | null = null;
let diagnosticsEnabled = false;
let ipcRegistered = false;

export function isPerfDiagActive(): boolean {
  return diagnosticsEnabled;
}

export function ensurePerfDiagIpcRegistered(): void {
  if (ipcRegistered) {
    return;
  }

  ipcRegistered = true;

  ipcMain.handle('perf-diag-is-enabled', () => diagnosticsEnabled);

  ipcMain.handle('perf-diag-report', (_event, entry: PerfDiagEntry) => {
    const writer = activeWriter;

    if (!diagnosticsEnabled || !writer) {
      return false;
    }

    try {
      writer.append(normalizeRendererEntry(entry));
      return true;
    } catch {
      return false;
    }
  });
}

export function getActivePerfDiagWriter(): PerfDiagWriter | null {
  return activeWriter;
}

export function startPerfDiagnostics(): PerfDiagWriter | null {
  ensurePerfDiagIpcRegistered();
  diagnosticsEnabled = isPerfDiagEnabled(process.env, app.isPackaged);

  if (!diagnosticsEnabled) {
    return null;
  }

  const sessionId = `${Date.now().toString(36)}-${process.pid}`;
  const writer = new PerfDiagWriter({
    userDataPath: app.getPath('userData'),
    sessionId
  });

  activeWriter = writer;
  registerProcessCrashHandlers(writer);
  startProcessMetricsPolling(writer);

  writer.append({
    collectedAt: Date.now(),
    source: 'main',
    type: 'session',
    payload: {
      event: 'started',
      sessionId,
      filePath: writer.snapshotFilePath
    }
  });

  return writer;
}

export function attachRendererDiagnosticsHooks(window: BrowserWindow): void {
  const writer = activeWriter;

  if (!writer) {
    return;
  }

  window.webContents.on('render-process-gone', (_event, details) => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'crash',
      payload: {
        reason: details.reason,
        exitCode: details.exitCode
      }
    });

    void writer.flushSnapshot('render-process-gone');
  });

  window.webContents.on('unresponsive', () => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'unresponsive',
      payload: {}
    });
  });

  window.webContents.on('responsive', () => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'session',
      payload: { event: 'renderer-responsive' }
    });
  });
}

export async function shutdownPerfDiagnostics(): Promise<void> {
  if (!activeWriter) {
    return;
  }

  await activeWriter.flushSnapshot('shutdown');

  if (processPollTimer) {
    clearInterval(processPollTimer);
    processPollTimer = null;
  }

  activeWriter = null;
  diagnosticsEnabled = false;
}

function registerProcessCrashHandlers(writer: PerfDiagWriter): void {
  app.on('child-process-gone', (_event, details) => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'crash',
      payload: {
        type: details.type,
        reason: details.reason,
        exitCode: details.exitCode,
        serviceName: details.serviceName ?? null,
        name: details.name ?? null
      }
    });
  });

  process.on('uncaughtException', (error) => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'crash',
      payload: {
        scope: 'main-uncaughtException',
        message: error.message
      }
    });

    void writer.flushSnapshot('uncaughtException');
  });

  process.on('unhandledRejection', (reason) => {
    writer.append({
      collectedAt: Date.now(),
      source: 'main',
      type: 'crash',
      payload: {
        scope: 'main-unhandledRejection',
        reason: String(reason)
      }
    });
  });
}

function startProcessMetricsPolling(writer: PerfDiagWriter): void {
  const sample = (): void => {
    try {
      const metrics = collectAppMetricsSnapshot();
      const totalKb = sumWorkingSetKb(metrics.processes);

      writer.append({
        collectedAt: metrics.collectedAt,
        source: 'main',
        type: 'process',
        payload: {
          totalWorkingSetKb: totalKb,
          processes: metrics.processes
        }
      });
    } catch {
      // Collector failures must never affect the app.
    }
  };

  sample();
  processPollTimer = setInterval(sample, PROCESS_POLL_INTERVAL_MS);
}

function normalizeRendererEntry(entry: PerfDiagEntry): PerfDiagEntry {
  return {
    collectedAt: Number(entry.collectedAt) || Date.now(),
    source: 'renderer',
    type: entry.type,
    payload: entry.payload ?? {}
  };
}
