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

function canWriteDirectory(dir) {
  try {
    fs.accessSync(dir, fs.constants.W_OK);
    return true;
  } catch (error) {
    return false;
  }
}

function getDefaultInstallDir(envPath = process.env.PATH || '') {
  const pathDirs = envPath.split(path.delimiter).filter(Boolean);
  const preferredDirs = [
    '/opt/homebrew/bin',
    '/usr/local/bin',
    path.join(os.homedir(), '.local', 'bin')
  ];

  const writablePathDir = preferredDirs.find(dir => pathDirs.includes(dir) && canWriteDirectory(dir));
  if (writablePathDir) return writablePathDir;

  const localBin = path.join(os.homedir(), '.local', 'bin');
  if (pathDirs.includes(localBin)) return localBin;

  return localBin;
}

function getDefaultLinkPath(envPath = process.env.PATH || '') {
  return path.join(getDefaultInstallDir(envPath), 'envmenu');
}

function getCliTargetPath({ isPackaged = false, resourcesPath, appRoot = process.cwd() } = {}) {
  if (isPackaged) {
    return path.join(resourcesPath, 'bin', 'envmenu');
  }

  return path.join(appRoot, 'bin', 'envmenu.js');
}

function isPathInShellPath(targetDir, envPath = process.env.PATH || '') {
  return envPath.split(path.delimiter).includes(targetDir);
}

function getCliInstallStatus(options = {}) {
  const linkPath = options.linkPath || getDefaultLinkPath(options.envPath);
  const installDir = path.dirname(linkPath);
  const targetPath = options.targetPath || getCliTargetPath(options);
  const inPath = isPathInShellPath(installDir, options.envPath);

  if (!fs.existsSync(linkPath)) {
    return {
      installed: false,
      linkPath,
      targetPath,
      installDir,
      inPath,
      conflict: false
    };
  }

  let currentTarget = null;
  let conflict = false;

  try {
    const stat = fs.lstatSync(linkPath);
    if (stat.isSymbolicLink()) {
      currentTarget = fs.readlinkSync(linkPath);
      conflict = path.resolve(currentTarget) !== path.resolve(targetPath);
    } else {
      conflict = true;
    }
  } catch (error) {
    conflict = true;
  }

  return {
    installed: !conflict,
    linkPath,
    targetPath,
    installDir,
    inPath,
    conflict,
    currentTarget
  };
}

function installCliCommand(options = {}) {
  const linkPath = options.linkPath || getDefaultLinkPath(options.envPath);
  const installDir = path.dirname(linkPath);
  const targetPath = options.targetPath || getCliTargetPath(options);
  const status = getCliInstallStatus({ ...options, linkPath, targetPath });

  if (status.conflict && !options.overwrite) {
    const error = new Error(`${linkPath} already exists and is not managed by Env Menu.`);
    error.code = 'CLI_INSTALL_CONFLICT';
    error.status = status;
    throw error;
  }

  fs.mkdirSync(installDir, { recursive: true });

  if (fs.existsSync(linkPath)) {
    fs.unlinkSync(linkPath);
  }

  fs.symlinkSync(targetPath, linkPath);

  return getCliInstallStatus({ ...options, linkPath, targetPath });
}

function uninstallCliCommand(options = {}) {
  const linkPath = options.linkPath || getDefaultLinkPath(options.envPath);
  const targetPath = options.targetPath || getCliTargetPath(options);
  const status = getCliInstallStatus({ ...options, linkPath, targetPath });

  if (!fs.existsSync(linkPath)) {
    return status;
  }

  if (status.conflict) {
    const error = new Error(`${linkPath} is not managed by Env Menu.`);
    error.code = 'CLI_UNINSTALL_CONFLICT';
    error.status = status;
    throw error;
  }

  fs.unlinkSync(linkPath);
  return getCliInstallStatus({ ...options, linkPath, targetPath });
}

module.exports = {
  getCliInstallStatus,
  getCliTargetPath,
  canWriteDirectory,
  getDefaultInstallDir,
  getDefaultLinkPath,
  installCliCommand,
  isPathInShellPath,
  uninstallCliCommand
};
