'use strict';

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const loadTs = require('./load-ts.cjs');

const { resolveCommand, windowsFallbackCandidates } = loadTs('src/main/shellEnv.ts');
const { PtyManager } = loadTs('src/main/pty.ts');

const WINDOWS_ENV = {
  APPDATA: 'C:\\Users\\Tester\\AppData\\Roaming',
  LOCALAPPDATA: 'C:\\Users\\Tester\\AppData\\Local',
  USERPROFILE: 'C:\\Users\\Tester'
};

function genericCandidates(command, env = WINDOWS_ENV) {
  const appData = env.APPDATA ?? '';
  const localAppData = env.LOCALAPPDATA ?? '';
  const home = env.USERPROFILE ?? env.HOME ?? '';
  return [
    `${appData}\\npm\\${command}.cmd`,
    `${appData}\\npm\\${command}`,
    `${localAppData}\\Programs\\claude\\${command}.exe`,
    `${home}\\.claude\\local\\${command}.cmd`,
    `${home}\\.claude\\local\\${command}`
  ];
}

test('agy adds its official LOCALAPPDATA location after existing fallbacks', () => {
  assert.deepEqual(windowsFallbackCandidates('agy', WINDOWS_ENV), [
    ...genericCandidates('agy'),
    'C:\\Users\\Tester\\AppData\\Local\\agy\\bin\\agy.exe'
  ]);
});

test('Antigravity fallback lookup normalizes command case and an exe suffix', () => {
  for (const command of ['AGY', 'agy.exe', 'AGY.EXE']) {
    assert.equal(
      windowsFallbackCandidates(command, WINDOWS_ENV).at(-1),
      'C:\\Users\\Tester\\AppData\\Local\\agy\\bin\\agy.exe',
      command
    );
  }
});

test('fallbacks do not map agy to the unverified antigravity.cmd launcher', () => {
  assert.ok(
    windowsFallbackCandidates('agy', WINDOWS_ENV).every((candidate) => !candidate.endsWith('antigravity.cmd'))
  );
});

test('Codex, OpenCode, and Claude retain the existing generic fallback order', () => {
  for (const command of ['codex', 'opencode', 'claude']) {
    assert.deepEqual(windowsFallbackCandidates(command, WINDOWS_ENV), genericCandidates(command));
  }
});

test('missing or invalid LOCALAPPDATA does not add an Antigravity candidate', () => {
  for (const localAppData of [
    undefined,
    '',
    'relative-path',
    '\\Users\\Tester\\AppData\\Local',
    '/c/Users/Tester/AppData/Local'
  ]) {
    const env = { ...WINDOWS_ENV, LOCALAPPDATA: localAppData };
    assert.deepEqual(windowsFallbackCandidates('agy', env), genericCandidates('agy', env));
  }
});

test('Antigravity fallback preserves spaces and Unicode in LOCALAPPDATA', () => {
  for (const localAppData of [
    'C:\\Users\\Test User\\AppData\\Local',
    'C:\\使用者\\測試\\AppData\\Local',
    '\\\\server\\share\\Profile'
  ]) {
    const candidates = windowsFallbackCandidates('agy', { ...WINDOWS_ENV, LOCALAPPDATA: localAppData });
    assert.equal(candidates.at(-1), path.win32.join(localAppData, 'agy', 'bin', 'agy.exe'));
  }
});

test('resolveCommand finds agy in its Windows LOCALAPPDATA install location', { skip: process.platform !== 'win32' }, (t) => {
  const localAppData = fs.mkdtempSync(path.join(os.tmpdir(), 'munder-agy-'));
  const executable = path.join(localAppData, 'agy', 'bin', 'agy.exe');
  const previousLocalAppData = process.env.LOCALAPPDATA;
  const previousPath = process.env.PATH;

  t.after(() => {
    if (previousLocalAppData === undefined) delete process.env.LOCALAPPDATA;
    else process.env.LOCALAPPDATA = previousLocalAppData;
    if (previousPath === undefined) delete process.env.PATH;
    else process.env.PATH = previousPath;
    fs.rmSync(localAppData, { recursive: true, force: true });
  });

  fs.mkdirSync(path.dirname(executable), { recursive: true });
  fs.writeFileSync(executable, '');
  process.env.LOCALAPPDATA = localAppData;
  process.env.PATH = path.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32');

  assert.equal(resolveCommand('agy'), executable);
  assert.equal(new PtyManager().commandPath('agy'), executable);
});