/**
 * Function/Windows/path-checker.js
 * ---------------------------------------------------------------------------
 * PATH CHECKER
 *
 * Analyses the PATH environment variable for missing folders, duplicates,
 * invalid entries and length problems, and recommends what to remove.
 *
 * PATH is never modified automatically - the module can only produce a
 * cleaned-up value for you to review and apply yourself.
 * ---------------------------------------------------------------------------
 */

'use strict';

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

/** The classic PATH length limit for the user/machine variable. */
const LENGTH_LIMIT = 2047;

const QUERY = [
  '$ErrorActionPreference = "SilentlyContinue"',
  '[ordered]@{',
  '  user = [Environment]::GetEnvironmentVariable("Path", "User");',
  '  machine = [Environment]::GetEnvironmentVariable("Path", "Machine");',
  '  process = $env:Path',
  '} | ConvertTo-Json -Compress',
].join('\n');

async function collect() {
  const result = await system.runPowerShellJson(QUERY, { timeoutMs: 30000 });
  if (!result.ok || !result.data) return null;
  return {
    user: String(result.data.user || ''),
    machine: String(result.data.machine || ''),
    process: String(result.data.process || ''),
  };
}

/** Analyse one PATH string. */
function analyse(value, scope) {
  const raw = String(value || '')
    .split(';')
    .map((entry) => entry.trim())
    .filter((entry) => entry.length);

  const seen = new Map();
  const entries = raw.map((entry, index) => {
    const expanded = entry.replace(/%([^%]+)%/g, (match, name) => process.env[name] || match);
    let exists = false;
    let isDirectory = false;
    try {
      const stat = fs.statSync(expanded);
      exists = true;
      isDirectory = stat.isDirectory();
    } catch {
      exists = false;
    }

    const key = expanded.replace(/\\+$/, '').toLowerCase();
    const duplicate = seen.has(key);
    if (!duplicate) seen.set(key, index);

    return {
      index: index + 1,
      raw: entry,
      expanded,
      exists,
      isDirectory,
      duplicate,
      scope,
      quoted: /^"|"$/.test(entry),
      relative: !path.isAbsolute(expanded) && !entry.startsWith('%'),
    };
  });

  return {
    scope,
    value,
    length: String(value || '').length,
    entries,
    missing: entries.filter((entry) => !entry.exists),
    duplicates: entries.filter((entry) => entry.duplicate),
    notFolders: entries.filter((entry) => entry.exists && !entry.isDirectory),
    invalid: entries.filter((entry) => entry.relative || entry.quoted),
  };
}

/** PATH with the problems removed, keeping the original order. */
function cleaned(analysis) {
  const kept = [];
  const seen = new Set();
  for (const entry of analysis.entries) {
    const key = entry.expanded.replace(/\\+$/, '').toLowerCase();
    if (!entry.exists || !entry.isDirectory || seen.has(key)) continue;
    seen.add(key);
    kept.push(entry.raw);
  }
  return kept.join(';');
}

function renderAnalysis(ui, analysis) {
  ui.blank();
  ui.section(`${analysis.scope.toUpperCase()} PATH`);
  ui.blank();
  kit.kv('Entries', String(analysis.entries.length));
  kit.kv('Total Length', `${analysis.length} characters`, analysis.length > LENGTH_LIMIT ? 'brightRed' : 'brightWhite');
  kit.kv('Missing Directories', String(analysis.missing.length), analysis.missing.length ? 'brightYellow' : 'brightGreen');
  kit.kv('Duplicate Entries', String(analysis.duplicates.length), analysis.duplicates.length ? 'brightYellow' : 'brightGreen');
  kit.kv('Not A Folder', String(analysis.notFolders.length), analysis.notFolders.length ? 'brightYellow' : 'brightGreen');
  kit.kv('Invalid Format', String(analysis.invalid.length), analysis.invalid.length ? 'brightYellow' : 'brightGreen');

  if (analysis.entries.length) {
    ui.blank();
    kit.table(
      ['#', 'ENTRY', 'STATUS'],
      analysis.entries.map((entry) => [
        ui.c.gray(String(entry.index)),
        ui.c.brightWhite(ui.truncate(entry.raw, 40)),
        entry.duplicate
          ? ui.c.brightYellow('duplicate')
          : !entry.exists
          ? ui.c.brightRed('missing')
          : !entry.isDirectory
          ? ui.c.brightYellow('not a folder')
          : entry.quoted || entry.relative
          ? ui.c.brightYellow('invalid format')
          : ui.c.brightGreen('ok'),
      ]),
      { align: ['right', 'left', 'left'], limit: 30 }
    );
  }
}

module.exports = {
  name: 'PATH Checker',
  menuName: 'PATH Checker',
  description: 'Finds missing, duplicate and invalid entries in the PATH variable and recommends fixes.',
  version: '1.0.0',
  author: 'InfinitySniper HFX',
  category: 'Windows',
  order: 70,
  requiredPermissions: 'normal',
  destructive: false,
  dependencies: [],
  minimumAppVersion: '1.0.0',
  permissions: ['system:read', 'filesystem:read', 'filesystem:write(reports)'],
  tags: ['path', 'environment', 'variables', 'cleanup'],
  documentation:
    'Splits the user and machine PATH into entries and checks each one: does the folder exist, is it ' +
    'actually a folder, does it appear twice, is it quoted or relative, and is the total length near ' +
    'the 2047-character limit that silently truncates PATH. It can show a cleaned-up value with the ' +
    'problems removed, but it never writes PATH itself - applying the change stays your decision, ' +
    'through the System Properties dialog or setx.',

  async selfTest(ctx) {
    if (!ctx.system.IS_WINDOWS) return { ok: false, message: 'Windows only' };
    const data = await collect();
    return data ? { ok: true, message: 'PATH readable' } : { ok: false, message: 'PATH query failed' };
  },

  async execute(ctx) {
    const { ui, prompt } = ctx;
    kit.header('PATH Checker', ['Analyses the PATH variable.', 'PATH is never modified automatically.']);
    if (!kit.requireWindows('The PATH checker')) return { ok: false, reason: 'unsupported-platform' };

    const spin = ui.spinner('Reading and checking PATH...');
    const data = await collect();
    spin.stop();
    if (!data) return kit.noData('PATH information');

    const user = analyse(data.user, 'User');
    const machine = analyse(data.machine, 'Machine');
    const problems =
      user.missing.length + user.duplicates.length + user.invalid.length +
      machine.missing.length + machine.duplicates.length + machine.invalid.length;

    renderAnalysis(ui, machine);
    renderAnalysis(ui, user);

    ui.blank();
    ui.section('RECOMMENDATIONS');
    ui.blank();
    if (!problems) {
      ui.completed('PATH is clean - every entry exists, is a folder and appears once.');
    } else {
      for (const analysis of [machine, user]) {
        for (const entry of analysis.missing.slice(0, 6)) {
          ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white(`Remove from ${analysis.scope} PATH (folder does not exist): ${ui.truncate(entry.raw, 34)}`));
        }
        for (const entry of analysis.duplicates.slice(0, 6)) {
          ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white(`Remove duplicate from ${analysis.scope} PATH: ${ui.truncate(entry.raw, 34)}`));
        }
        for (const entry of analysis.invalid.slice(0, 4)) {
          ui.println('  ' + ui.c.yellow(ui.glyphs().bullet + ' ') + ui.c.white(`Fix format in ${analysis.scope} PATH: ${ui.truncate(entry.raw, 34)}`));
        }
        if (analysis.length > LENGTH_LIMIT) {
          ui.println('  ' + ui.c.brightRed(ui.glyphs().bullet + ' ') + ui.c.white(`${analysis.scope} PATH is ${analysis.length} characters - over the ${LENGTH_LIMIT} limit, entries may be lost.`));
        }
      }
    }

    for (;;) {
      ui.blank();
      ui.menu(
        [
          { key: '1', label: 'Show a cleaned-up User PATH' },
          { key: '2', label: 'Show a cleaned-up System PATH' },
          { key: '3', label: 'Export the analysis' },
          { separator: true },
          { key: '0', label: 'Back', color: 'gray' },
        ],
        { title: 'PATH CHECKER' }
      );
      ui.blank();

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

      if (answer === '1' || answer === '2') {
        const analysis = answer === '1' ? user : machine;
        const value = cleaned(analysis);
        ui.blank();
        ui.section(`CLEANED ${analysis.scope.toUpperCase()} PATH`);
        ui.blank();
        kit.kv('Entries Before', String(analysis.entries.length));
        kit.kv('Entries After', String(value ? value.split(';').length : 0));
        kit.kv('Length Before', `${analysis.length} characters`);
        kit.kv('Length After', `${value.length} characters`);
        ui.blank();
        for (const entry of value.split(';').filter(Boolean)) {
          ui.println('  ' + ui.c.white(ui.truncate(entry, ui.getWidth() - 4)));
        }
        ui.blank();
        ui.println('  ' + ui.c.gray('Apply it yourself if you agree - for example with:'));
        ui.println('  ' + ui.c.brightCyan(`setx PATH "<the value above>"${analysis.scope === 'Machine' ? ' /M   (needs an elevated prompt)' : ''}`));
        ui.println('  ' + ui.c.gray('Export the analysis first so you can restore the original value.'));
        await prompt.pause();
      } else if (answer === '3') {
        const report = kit.report('PATH Analysis Report');
        for (const analysis of [machine, user]) {
          const section = report.section(`${analysis.scope.toUpperCase()} PATH`);
          section.add('Entries', String(analysis.entries.length));
          section.add('Total Length', `${analysis.length} characters`);
          section.add('Missing Directories', String(analysis.missing.length));
          section.add('Duplicate Entries', String(analysis.duplicates.length));
          const list = section.group('Entries');
          analysis.entries.forEach((entry) => {
            list.add(
              `${entry.index}. ${entry.raw}`,
              entry.duplicate ? 'duplicate' : !entry.exists ? 'missing' : !entry.isDirectory ? 'not a folder' : 'ok'
            );
          });
          section.spacer();
          section.add('Original Value', analysis.value);
          section.add('Cleaned Value', cleaned(analysis));
        }
        kit.saveReport(report, 'PathAnalysis');
        await prompt.pause();
      } else {
        ui.warning('Enter one of the numbers shown above, or 0 to go back.');
        await prompt.pause();
      }
    }

    return kit.done('PATH analysis finished.');
  },
};
