/**
 * Bash installer script generation.
 * Supports interactive and --yes (silent) modes.
 */

import { type FrontmcpExecConfig } from './config';
import { sanitizeShellLiteral } from './runner-script';

export interface InstallerScriptContext {
  /** Build target this installer was generated for. Drives binary copying and Node version checks. */
  target: 'node' | 'cli';
  /** True when SEA binaries were emitted (CLI mode). Skips the Node version check at install time. */
  seaEnabled: boolean;
}

export function generateInstallerScript(
  config: FrontmcpExecConfig,
  context: InstallerScriptContext = { target: 'node', seaEnabled: false },
): string {
  const name = config.name;
  const nativeAddons = config.dependencies?.nativeAddons || [];
  const storageType = config.storage?.type || 'none';
  // Sanitize before interpolating into bash echo / error messages — the
  // user-controlled `nodeVersion` ends up in command lines verbatim.
  const nodeVersion = sanitizeShellLiteral(config.nodeVersion || '>=22.0.0');
  const minNodeMajor = extractMinMajor(nodeVersion);

  // CLI/SEA installs ship a self-contained binary — no Node runtime required
  // at install or run time. Skip the Node check entirely; the binary is the
  // whole point of `--target cli`.
  const seaCli = context.target === 'cli' && context.seaEnabled;
  const nodeCheck = seaCli
    ? '# CLI/SEA install: Node.js not required at runtime — skipping version check.'
    : `# Check Node.js
if ! command -v node &> /dev/null; then
  echo "Error: Node.js is required."
  echo "Install Node.js ${nodeVersion}: https://nodejs.org"
  exit 1
fi

NODE_MAJOR=$(node -e "console.log(process.versions.node.split('.')[0])")
if [ "\${NODE_MAJOR}" -lt "${minNodeMajor}" ]; then
  echo "Error: Node.js ${nodeVersion} required, found v$(node -v)"
  exit 1
fi`;

  // For SEA-CLI installs, also copy the CLI binary the runner exec's.
  // Without this the installer leaves a runner pointing at a missing file.
  const seaBinaryCopy = seaCli
    ? `# Copy SEA binary that the runner exec's
cp "\${SCRIPT_DIR}/${name}-cli-bin" "\${INSTALL_DIR}/"
chmod +x "\${INSTALL_DIR}/${name}-cli-bin"`
    : '# No SEA binary to copy (JS bundle mode)';

  return `#!/usr/bin/env bash
set -euo pipefail

# install-${name}.sh — FrontMCP App Installer
# Generated by frontmcp build --target ${context.target}

SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
APP_NAME="${name}"
INSTALL_DIR="\${HOME}/.frontmcp/apps/\${APP_NAME}"
SILENT=false

# Parse args
for arg in "$@"; do
  case "\${arg}" in
    --yes|-y) SILENT=true ;;
  esac
done

echo "Installing \${APP_NAME}..."
echo ""

${nodeCheck}

# Create install directory
mkdir -p "\${INSTALL_DIR}"

# Copy files
cp "\${SCRIPT_DIR}/${name}.bundle.js" "\${INSTALL_DIR}/"
cp "\${SCRIPT_DIR}/${name}.manifest.json" "\${INSTALL_DIR}/"
cp "\${SCRIPT_DIR}/${name}" "\${INSTALL_DIR}/"
chmod +x "\${INSTALL_DIR}/${name}"

${seaBinaryCopy}

${nativeAddons.length > 0 ? generateNativeAddonInstall(nativeAddons) : '# No native addons required'}

${storageType === 'sqlite' ? generateSqliteSetup(name) : '# No storage setup required'}

# Run questionnaire if manifest has setup and not silent
if [ "\${SILENT}" = false ] && command -v frontmcp &> /dev/null; then
  if node -e "const m=require('\${INSTALL_DIR}/${name}.manifest.json'); process.exit(m.setup ? 0 : 1)" 2>/dev/null; then
    echo ""
    echo "Running setup questionnaire..."
    frontmcp configure "\${APP_NAME}"
  fi
fi

echo ""
echo "Installed to \${INSTALL_DIR}"
echo ""
echo "Start with:"
echo "  frontmcp start \${APP_NAME}"
echo ""
`;
}

function generateNativeAddonInstall(addons: string[]): string {
  const addonList = addons.map((a) => `"${a}"`).join(' ');
  return `# Install native addons
echo "Installing native dependencies..."
(cd "\${INSTALL_DIR}" && npm init -y --silent 2>/dev/null && npm install ${addonList} --save --silent)
`;
}

function generateSqliteSetup(name: string): string {
  return `# Create SQLite data directory
DATA_DIR="\${HOME}/.frontmcp/data/${name}"
mkdir -p "\${DATA_DIR}"
echo "FRONTMCP_SQLITE_PATH=\${DATA_DIR}/${name}.sqlite" >> "\${INSTALL_DIR}/.env"
`;
}

function extractMinMajor(version: string): number {
  const match = version.match(/(\d+)/);
  return match ? parseInt(match[1], 10) : 22;
}
