#!/usr/bin/env node
'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');

const args = process.argv.slice(2);

function readArg(name) {
  const index = args.indexOf(name);

  if (index === -1) {
    return null;
  }

  return args[index + 1] ?? null;
}

function hasFlag(name) {
  return args.includes(name);
}

function resolveCandidateUserDataDirs() {
  const home = os.homedir();
  const appNames = ['Toju', 'metoyou'];

  if (process.platform === 'win32') {
    const base = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');

    return appNames.map((name) => path.join(base, name));
  }

  if (process.platform === 'darwin') {
    const base = path.join(home, 'Library', 'Application Support');

    return appNames.map((name) => path.join(base, name));
  }

  const base = process.env.XDG_CONFIG_HOME || path.join(home, '.config');

  return appNames.map((name) => path.join(base, name));
}

function resolveDefaultDiagnosticsDir() {
  const candidates = resolveCandidateUserDataDirs()
    .map((userDataDir) => path.join(userDataDir, 'diagnostics'))
    .filter((dir) => fs.existsSync(dir));

  if (candidates.length === 0) {
    return path.join(resolveCandidateUserDataDirs()[0], 'diagnostics');
  }

  return candidates
    .map((dir) => ({ dir, mtimeMs: fs.statSync(dir).mtimeMs }))
    .sort((left, right) => right.mtimeMs - left.mtimeMs)[0].dir;
}

function parseJsonLines(content) {
  return content
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean)
    .map((line) => {
      try {
        return JSON.parse(line);
      } catch {
        return null;
      }
    })
    .filter(Boolean);
}

function formatKb(kb) {
  if (kb == null) {
    return 'n/a';
  }

  return `${Math.round(kb / 1024)} MB`;
}

function summarize(entries) {
  const latestProcess = [...entries].reverse().find((entry) => entry.type === 'process');
  const latestStore = [...entries].reverse().find((entry) => entry.type === 'store');
  const latestComponents = [...entries].reverse().find((entry) => entry.type === 'components');
  const latestHeap = [...entries].reverse().find((entry) => entry.type === 'heap');

  if (latestProcess) {
    console.log(`Process RSS total: ${formatKb(latestProcess.payload.totalWorkingSetKb)}`);
  }

  if (latestHeap) {
    console.log(`Renderer JS heap: ${latestHeap.payload.usedJsHeapMb ?? 'n/a'} MB`);
  }

  if (latestStore?.payload?.domains) {
    console.log('Store domains (estimated bytes):');

    for (const [domain, bytes] of Object.entries(latestStore.payload.domains).sort((a, b) => b[1] - a[1])) {
      console.log(`  ${domain}: ${bytes}`);
    }
  }

  if (latestComponents?.payload?.domains) {
    console.log('Live components by domain:');

    for (const [domain, count] of Object.entries(latestComponents.payload.domains).sort((a, b) => b[1] - a[1])) {
      console.log(`  ${domain}: ${count}`);
    }
  }

  const leaks = latestComponents?.payload?.suspectedLeaks;

  if (Array.isArray(leaks) && leaks.length > 0) {
    console.log('Suspected component leaks:');

    for (const leak of leaks) {
      console.log(`  ${leak.name}: ${leak.count} (expected ${leak.expected})`);
    }
  }
}

function main() {
  const dir = readArg('--dir') || resolveDefaultDiagnosticsDir();
  const file = readArg('--file');

  if (!fs.existsSync(dir) && !file) {
    console.error(`Diagnostics directory not found: ${dir}`);
    process.exit(1);
  }

  const targetFile = file || findLatestFile(dir);

  if (!targetFile) {
    console.error(`No diagnostics files found in ${dir}`);
    process.exit(1);
  }

  console.log(`Reading ${targetFile}`);

  if (hasFlag('--tail')) {
    tailFile(targetFile);
    return;
  }

  const entries = parseJsonLines(fs.readFileSync(targetFile, 'utf8'));
  summarize(entries);
}

function findLatestFile(dir) {
  if (!fs.existsSync(dir)) {
    return null;
  }

  return fs.readdirSync(dir)
    .filter((name) => name.startsWith('perf-') && name.endsWith('.jsonl'))
    .map((name) => path.join(dir, name))
    .sort((left, right) => fs.statSync(right).mtimeMs - fs.statSync(left).mtimeMs)[0] ?? null;
}

function tailFile(targetFile) {
  let position = 0;

  const printNewLines = () => {
    const stats = fs.statSync(targetFile);
    const nextSize = stats.size;

    if (nextSize < position) {
      position = 0;
    }

    if (nextSize === position) {
      return;
    }

    const fd = fs.openSync(targetFile, 'r');
    const length = nextSize - position;
    const buffer = Buffer.alloc(length);

    fs.readSync(fd, buffer, 0, length, position);
    fs.closeSync(fd);
    position = nextSize;

    for (const line of buffer.toString('utf8').split('\n')) {
      if (!line.trim()) {
        continue;
      }

      try {
        const entry = JSON.parse(line);
        console.log(`[${entry.type}] ${JSON.stringify(entry.payload)}`);
      } catch {
        console.log(line);
      }
    }
  };

  printNewLines();
  setInterval(printNewLines, 1000);
}

main();
