/**
 * Function/Reports/cleanup-history.js
 * ---------------------------------------------------------------------------
 * CLEANUP HISTORY
 *
 * Reads the cleanup logs the Cache Cleaner writes and turns them into a
 * history: date, categories cleaned, files removed, space recovered, errors.
 * ---------------------------------------------------------------------------
 */

'use strict';

const fs = require('fs');
const path = require('path');
const kit = require('../../core/kit');
const system = require('../../core/system');

/** Parse one cleanup_*.log into a history entry. */
function parseLog(file) {
  let text;
  try {
    text = fs.readFileSync(file, 'utf8');
  } catch {
    return null;
  }

  const value = (label) => {
    const match = text.match(new RegExp(`${label}\\s*:\\s*(.+)`, 'i'));
    return match ? match[1].trim() : null;
  };

  const started = text.match(/Started\s*:\s*(.+)/i);
  const removed = Number(String(value('Files removed') || '').replace(/[^\d]/g, '')) || 0;
  const skipped = Number(String(value('Files skipped') || '').replace(/[^\d]/g, '')) || 0;
  const failed = Number(String(value('Files not removable') || '').replace(/[^\d]/g, '')) || 0;
  const categories = value('Categories cleaned');
  const recovered = value('Space recovered');
  const dryRun = /Dry run\s*:\s*YES/i.test(text);

  let stat = null;
  try {
    stat = fs.statSync(file);
  } catch {
    stat = null;
  }

  return {
    file,
    name: path.basename(file),
    date: started ? started[1].replace(/^\[|\]$/g, '').trim() : stat ? system.isoLocal(stat.mtime) : null,
    modified: stat ? stat.mtime : new Date(0),
    removed,
    skipped,
    failed,
    categories: categories || '(not recorded)',
    recovered: recovered || '0 B',
    dryRun,
    errors: (text.match(/^FAIL/gm) || []).length,
  };
}

/** All cleanup logs, newest first. */
function history(logsDir) {
  let files = [];
  try {
    files = fs
      .readdirSync(logsDir)
      .filter((name) => /^cleanup_.*\.log$/i.test(name))
      .map((name) => path.join(logsDir, name));
  } catch {
    return [];
  }
  return files
    .map(parseLog)
    .filter(Boolean)
    .sort((a, b) => b.modified - a.modified);
}

/** "1.13 GB" -> bytes, so totals can be summed. */
function toBytes(text) {
  const match = String(text || '').match(/([\d.,]+)\s*(B|KB|MB|GB|TB)/i);
  if (!match) return 0;
  const value = Number(String(match[1]).replace(/,/g, ''));
  const units = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3, tb: 1024 ** 4 };
  return Number.isFinite(value) ? value * (units[match[2].toLowerCase()] || 1) : 0;
}

module.exports = {
  name: 'Cleanup History',
  menuName: 'Cleanup History',
  description: 'Previous cleanup runs with categories, files removed, space recovered and errors.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Reports',
  order: 30,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['filesystem:read(logs)', 'filesystem:write(reports)'],
  tags: ['cleanup', 'history', 'cleaner', 'logs'],
  documentation:
    'Every run of the Cache Cleaner writes a detailed log; this function reads those logs back and ' +
    'presents them as a history - when the run happened, which categories were cleaned, how many ' +
    'files were removed, skipped or locked, how much space came back, and whether it was a dry run. ' +
    'The full log of any run can be opened from here.',

  async selfTest(ctx) {
    const entries = history(ctx.paths.logs);
    return { ok: true, message: `${entries.length} cleanup run(s) recorded` };
  },

  async execute(ctx) {
    const { ui, prompt } = ctx;
    kit.header('Cleanup History', ['Previous runs of the Cache Cleaner.']);

    const entries = history(ctx.paths.logs);

    if (!entries.length) {
      ui.blank();
      ui.info('No cleanup has been run yet.');
      ui.println('  ' + ui.c.gray('Run Maintenance > Cache & Temporary File Cleaner to create one.'));
      await prompt.pause('Press ENTER to go back');
      return { ok: true, runs: 0 };
    }

    const real = entries.filter((entry) => !entry.dryRun);
    const totalRemoved = real.reduce((sum, entry) => sum + entry.removed, 0);
    const totalRecovered = real.reduce((sum, entry) => sum + toBytes(entry.recovered), 0);

    for (;;) {
      ui.blank();
      ui.section('SUMMARY');
      ui.blank();
      kit.kv('Cleanup Runs', system.formatNumber(entries.length));
      kit.kv('Real Runs', system.formatNumber(real.length));
      kit.kv('Dry Runs', system.formatNumber(entries.length - real.length));
      kit.kv('Files Removed (total)', system.formatNumber(totalRemoved), 'brightGreen');
      kit.kv('Space Recovered (total)', system.formatBytes(totalRecovered), 'brightGreen');
      kit.kv('Last Cleanup', entries[0].date);

      ui.blank();
      ui.section('HISTORY');
      ui.blank();
      kit.table(
        ['DATE', 'REMOVED', 'RECOVERED', 'ERRORS', 'MODE'],
        entries.map((entry) => [
          ui.c.brightWhite(ui.truncate(entry.date || entry.name, 20)),
          ui.c.brightGreen(system.formatNumber(entry.removed)),
          ui.c.brightCyan(entry.recovered),
          entry.failed ? ui.c.brightRed(String(entry.failed)) : ui.c.gray('0'),
          entry.dryRun ? ui.c.brightYellow('dry run') : ui.c.gray('real'),
        ]),
        { align: ['left', 'right', 'right', 'right', 'left'], limit: 20 }
      );

      ui.blank();
      ui.menu(
        [
          { key: '1', label: 'Show the details of a run' },
          { key: '2', label: 'Export the history as a TXT report' },
          { separator: true },
          { key: '0', label: 'Back', color: 'gray' },
        ],
        { title: 'CLEANUP HISTORY' }
      );
      ui.blank();

      const answer = await prompt.select();
      if (!answer && prompt.isClosed()) break;
      if (answer === '0') break;

      if (answer === '1') {
        const index = await prompt.choose(
          'Which run?',
          entries.map((entry) => ({ label: entry.date || entry.name, hint: `${system.formatNumber(entry.removed)} files, ${entry.recovered}` }))
        );
        if (index === null) continue;
        const entry = entries[index];

        ui.blank();
        ui.section('CLEANUP RUN');
        ui.blank();
        kit.kv('Date', entry.date);
        kit.kv('Mode', entry.dryRun ? 'Dry run (nothing deleted)' : 'Real cleanup');
        kit.kv('Files Removed', system.formatNumber(entry.removed));
        kit.kv('Files Skipped', system.formatNumber(entry.skipped));
        kit.kv('Could Not Be Removed', system.formatNumber(entry.failed));
        kit.kv('Space Recovered', entry.recovered);
        kit.kv('Errors Logged', system.formatNumber(entry.errors));
        ui.blank();
        ui.println('  ' + ui.c.gray('Categories cleaned:'));
        for (const line of ui.wrap(entry.categories, ui.getWidth() - 4)) {
          ui.println('  ' + ui.c.white(line));
        }
        ui.blank();
        kit.kv('Log File', entry.file);
        await prompt.pause();
      } else if (answer === '2') {
        const report = kit.report('Cleanup History Report');
        const section = report.section('SUMMARY');
        section.add('Cleanup Runs', system.formatNumber(entries.length));
        section.add('Files Removed (total)', system.formatNumber(totalRemoved));
        section.add('Space Recovered (total)', system.formatBytes(totalRecovered));

        const list = report.section('HISTORY');
        entries.forEach((entry, index) => {
          const group = list.group(`${index + 1}. ${entry.date || entry.name}`);
          group.add('Mode', entry.dryRun ? 'Dry run' : 'Real cleanup');
          group.add('Files Removed', system.formatNumber(entry.removed));
          group.add('Files Skipped', system.formatNumber(entry.skipped));
          group.add('Could Not Be Removed', system.formatNumber(entry.failed));
          group.add('Space Recovered', entry.recovered);
          group.add('Categories Cleaned', entry.categories);
          group.add('Log File', entry.file);
        });
        kit.saveReport(report, 'CleanupHistory');
        await prompt.pause();
      } else {
        ui.warning('Enter one of the numbers shown above, or 0 to go back.');
        await prompt.pause();
      }
    }

    return kit.done('Cleanup history closed.');
  },
};
