'use strict';

const os = require('os');
const fs = require('fs');
const path = require('path');
const { execFile } = require('child_process');

// ── Env var → provider type mapping ─────────────────────────────────────

const ENV_MAP = [
  { envVar: 'ANTHROPIC_API_KEY', type: 'anthropic', gateway: false },
  { envVar: 'OPENAI_API_KEY',    type: 'openai',    gateway: false },
  { envVar: 'GOOGLE_API_KEY',    type: 'google',    gateway: false },
  { envVar: 'GEMINI_API_KEY',    type: 'google',    gateway: false },
  { envVar: 'MOONSHOT_API_KEY',  type: 'moonshot',  gateway: false },
];

const GATEWAY_ENV_MAP = [
  { envVar: 'PORTKEY_API_KEY', gatewayType: 'portkey' },
];

// ── Helpers ─────────────────────────────────────────────────────────────

function maskKey(key) {
  if (!key || key.length < 8) return '***';
  return key.slice(0, 7) + '...' + key.slice(-4);
}

function execFileAsync(cmd, args, opts = {}) {
  return new Promise((resolve, reject) => {
    execFile(cmd, args, { timeout: 5000, ...opts }, (err, stdout, stderr) => {
      if (err) return reject(err);
      resolve({ stdout, stderr });
    });
  });
}

// ── detectFromEnv ───────────────────────────────────────────────────────

function detectFromEnv() {
  const seen = new Set();
  const results = [];

  for (const { envVar, type, gateway } of ENV_MAP) {
    const value = process.env[envVar];
    if (!value) continue;
    if (seen.has(type)) continue;
    seen.add(type);
    results.push({
      type,
      source: `env:${envVar}`,
      keyPreview: maskKey(value),
      gateway,
    });
  }

  return results;
}

function detectGatewaysFromEnv() {
  const results = [];
  for (const { envVar, gatewayType } of GATEWAY_ENV_MAP) {
    const value = process.env[envVar];
    if (!value) continue;
    results.push({
      type: gatewayType,
      gatewayType,
      source: `env:${envVar}`,
      keyPreview: maskKey(value),
      requiresProvider: true,
    });
  }
  return results;
}

// ── detectFromConfigFiles ───────────────────────────────────────────────

function detectFromConfigFiles() {
  const home = os.homedir();
  const results = [];

  // Claude config files
  const claudeConfigs = [
    path.join(home, '.claude', 'config.json'),
    path.join(home, '.claude.json'),
  ];
  for (const filePath of claudeConfigs) {
    if (fs.existsSync(filePath)) {
      results.push({ type: 'anthropic', source: `file:${filePath}` });
      break; // only add anthropic once
    }
  }

  // Ollama directory
  const ollamaDir = path.join(home, '.ollama');
  if (fs.existsSync(ollamaDir)) {
    results.push({ type: 'ollama', source: `directory:~/.ollama` });
  }

  return results;
}

// ── detectOllamaStatus ─────────────────────────────────────────────────

async function detectOllamaStatus() {
  const result = { installed: false, running: false, models: [], binaryPath: null };

  // Check if ollama binary exists via `which`
  try {
    const { stdout } = await execFileAsync('which', ['ollama']);
    const bin = stdout.trim();
    if (bin) {
      result.installed = true;
      result.binaryPath = bin;
    }
  } catch (_) {
    // `which` failed — check common paths
  }

  if (!result.installed) {
    const commonPaths = ['/usr/local/bin/ollama', '/opt/homebrew/bin/ollama'];
    for (const p of commonPaths) {
      if (fs.existsSync(p)) {
        result.installed = true;
        result.binaryPath = p;
        break;
      }
    }
  }

  // Check if server is running
  if (result.installed) {
    try {
      const controller = new AbortController();
      const timer = setTimeout(() => controller.abort(), 3000);
      const resp = await fetch('http://localhost:11434/api/tags', {
        signal: controller.signal,
      });
      clearTimeout(timer);
      if (resp.ok) {
        result.running = true;
        const data = await resp.json();
        if (data.models && Array.isArray(data.models)) {
          result.models = data.models.map(m => m.name || m.model).filter(Boolean);
        }
      }
    } catch (_) {
      // Server not running or unreachable
    }
  }

  return result;
}

// ── recommendOllamaModel ───────────────────────────────────────────────

async function recommendOllamaModel() {
  const totalMemGB = os.totalmem() / (1024 ** 3);

  let freeSpaceGB = 0;
  try {
    const dfFlag = process.platform === 'darwin' ? '-g' : '-BG';
    const { stdout } = await execFileAsync('df', [dfFlag, '/']);
    // Parse second line of df output — available column is typically the 4th field
    const lines = stdout.trim().split('\n');
    if (lines.length >= 2) {
      const fields = lines[1].split(/\s+/);
      // On macOS `df -g`, fields: Filesystem 512-blocks Used Available Capacity ...
      // Available is index 3
      const avail = parseInt(fields[3], 10);
      if (!isNaN(avail)) freeSpaceGB = avail;
    }
  } catch (_) {
    freeSpaceGB = 0;
  }

  if (totalMemGB >= 32 && freeSpaceGB >= 20) {
    return { model: 'gemma4:26b', label: 'Gemma 4 26B MoE', size: '~17GB', tier: 'quality' };
  }
  if (freeSpaceGB >= 10) {
    return { model: 'gemma4:e4b', label: 'Gemma 4 E4B', size: '~10GB', tier: 'fast' };
  }

  return null;
}

// ── detectMLXStatus ─────────────────────────────────────────────────────

async function detectMLXStatus() {
  const result = { installed: false, models: [] };
  const mlxPython = path.join(os.homedir(), '.mlx-env', 'bin', 'python3');

  if (!fs.existsSync(mlxPython)) return result;

  try {
    const { stdout } = await execFileAsync(mlxPython, ['-c', 'import mlx_lm; print("ok")'], { timeout: 10000 });
    if (stdout.trim() === 'ok') result.installed = true;
  } catch {
    return result;
  }

  // Find cached MLX models
  const cacheDir = path.join(os.homedir(), '.cache', 'huggingface', 'hub');
  if (fs.existsSync(cacheDir)) {
    try {
      const entries = fs.readdirSync(cacheDir).filter(d => d.startsWith('models--mlx-community--'));
      for (const e of entries) {
        const repo = e.replace('models--', '').replace('--', '/');
        result.models.push(repo);
      }
    } catch {}
  }

  return result;
}

// ── detectAll ───────────────────────────────────────────────────────────

async function detectAll() {
  const envDetected = detectFromEnv();
  const gateways = detectGatewaysFromEnv();
  const fileDetected = detectFromConfigFiles();
  const ollamaStatus = await detectOllamaStatus();
  const ollamaRecommendation = await recommendOllamaModel();
  const mlxStatus = await detectMLXStatus();

  // Merge: env results take priority, then file, then ollama
  const seenTypes = new Set();
  const detected = [];

  for (const item of envDetected) {
    if (!seenTypes.has(item.type)) {
      seenTypes.add(item.type);
      detected.push(item);
    }
  }

  for (const item of fileDetected) {
    if (!seenTypes.has(item.type)) {
      seenTypes.add(item.type);
      detected.push(item);
    }
  }

  // If Ollama is running with models and not already detected via env/file
  if (ollamaStatus.running && ollamaStatus.models.length > 0 && !seenTypes.has('ollama')) {
    detected.push({
      type: 'ollama',
      source: `running:localhost:11434`,
    });
    seenTypes.add('ollama');
  }

  // If MLX is installed with cached models
  if (mlxStatus.installed && mlxStatus.models.length > 0 && !seenTypes.has('mlx')) {
    detected.push({
      type: 'mlx',
      source: `local:mlx-env`,
      models: mlxStatus.models,
    });
    seenTypes.add('mlx');
  }

  // Determine setupAction
  let setupAction;
  if (detected.length > 0) {
    setupAction = 'auto-register-detected';
  } else if (ollamaStatus.running && ollamaStatus.models.length > 0) {
    setupAction = 'ollama-running';
  } else if (ollamaStatus.running && ollamaStatus.models.length === 0) {
    setupAction = 'ollama-pull-model';
  } else if (ollamaStatus.installed) {
    setupAction = 'ollama-start';
  } else if (ollamaRecommendation !== null) {
    // System has enough resources to run Ollama
    setupAction = 'ollama-install';
  } else {
    setupAction = 'needs-api-key';
  }

  return { detected, gateways, ollamaStatus, ollamaRecommendation, mlxStatus, setupAction };
}

module.exports = {
  detectFromEnv,
  detectGatewaysFromEnv,
  detectFromConfigFiles,
  detectOllamaStatus,
  detectMLXStatus,
  recommendOllamaModel,
  detectAll,
};
