#!/usr/bin/env node
/**
 * Find the packaged Electron executable built by Electron Forge.
 * This script locates the executable in the out/ directory structure.
 */

import { readdirSync, statSync, existsSync, readFileSync } from 'fs';
import { isAbsolute, join, resolve } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = resolve(__filename, '..');

const platform = process.platform;
const repoRoot = resolve(__dirname, '..');
const packageJson = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf-8'));
const appName = (packageJson.build?.productName || packageJson.name || '5chan').toLowerCase();

const resolveOutDir = (dir) => (isAbsolute(dir) ? dir : join(repoRoot, dir));

const envOutDir = process.env.ELECTRON_FORGE_OUT_DIR;

const candidateRoots = [
  envOutDir ? resolveOutDir(envOutDir) : null,
  join(repoRoot, 'out'),
  join(repoRoot, 'out', 'make'),
  join(repoRoot, '..', 'out'),
  join(repoRoot, '..', '..', 'out'),
  join(repoRoot, 'electron', 'out'),
].filter(Boolean);

// Skip directories that contain helper binaries/app code, not the main executable
// 'resources' contains the app bundle with IPFS binaries in bin/ - don't recurse there
const skipDirs = new Set(['node_modules', '.git', 'bin', 'app', 'resources']);

function findExecutable(dir, platform) {
  const entries = readdirSync(dir, { withFileTypes: true });
  let fallback = null; // Track a valid candidate that doesn't match appName

  for (const entry of entries) {
    const fullPath = join(dir, entry.name);

    if (entry.isDirectory()) {
      if (skipDirs.has(entry.name)) continue;

      if (platform === 'darwin' && entry.name.endsWith('.app')) {
        const appPath = fullPath;
        const exePath = join(appPath, 'Contents', 'MacOS', entry.name.replace('.app', ''));
        if (existsSync(exePath)) {
          const lowerAppName = entry.name.toLowerCase();
          if (lowerAppName.includes(appName)) {
            return exePath; // Exact match, return immediately
          }
          fallback = fallback || exePath; // Store as fallback, continue searching
        }
      }

      const result = findExecutable(fullPath, platform);
      if (result) {
        const resultName = result.split('/').pop().toLowerCase();
        if (resultName.includes(appName)) {
          return result; // Exact match from recursion
        }
        fallback = fallback || result; // Store as fallback
      }
    } else if (entry.isFile()) {
      // Check if it's an executable
      if (platform === 'win32') {
        const lowerName = entry.name.toLowerCase();
        if (lowerName.endsWith('.exe') && !lowerName.includes('electron') && !lowerName.includes('crashpad')) {
          if (lowerName.includes(appName)) {
            return fullPath; // Exact match, return immediately
          }
          fallback = fallback || fullPath; // Store as fallback, continue searching
        }
      } else if (platform === 'darwin') {
        const stat = statSync(fullPath);
        if (stat.isFile() && stat.mode & parseInt('111', 8)) {
          const lowerName = entry.name.toLowerCase();
          if (!lowerName.includes('helper') && !lowerName.includes('crashpad')) {
            if (lowerName.includes(appName)) {
              return fullPath; // Exact match, return immediately
            }
            fallback = fallback || fullPath; // Store as fallback, continue searching
          }
        }
      } else if (platform === 'linux') {
        // Linux executables (AppImage or unpacked)
        if (entry.name.endsWith('.AppImage')) {
          const lowerName = entry.name.toLowerCase();
          if (lowerName.includes(appName)) {
            return fullPath; // Exact match, return immediately
          }
          fallback = fallback || fullPath; // Store as fallback, continue searching
        }
        // Check for executable files (not .so libraries)
        const stat = statSync(fullPath);
        if (stat.isFile() && stat.mode & parseInt('111', 8) && !entry.name.includes('.so')) {
          // Skip helper binaries
          const lowerName = entry.name.toLowerCase();
          if (!lowerName.includes('chrome') && !lowerName.includes('crashpad')) {
            if (lowerName.includes(appName)) {
              return fullPath; // Exact match, return immediately
            }
            fallback = fallback || fullPath; // Store as fallback, continue searching
          }
        }
      }
    }
  }

  return fallback; // Return fallback candidate if no exact appName match found
}

let executable = null;
const checkedDirs = [];

for (const root of candidateRoots) {
  if (!existsSync(root)) {
    checkedDirs.push(`${root} (missing)`);
    continue;
  }

  const result = findExecutable(root, platform);
  checkedDirs.push(root);
  if (result) {
    executable = result;
    break;
  }
}

if (!executable) {
  console.error('Error: Could not find packaged executable.');
  console.error('Platform:', platform);
  console.error('Checked directories:', checkedDirs.join(', '));
  process.exit(1);
}

console.log(executable);
