const fs = require('fs');
const os = require('os');
const path = require('path');
const http = require('http');
const { execFile, spawn } = require('child_process');
const { promisify } = require('util');
const {
  getMachineProfile,
  getOllamaRecommendedChatModel,
  getEmbeddedRuntimeRecommendation,
  getPreferredLocalRuntime
} = require('./runtime-profile.cjs');

const execFileAsync = promisify(execFile);
const OLLAMA_URL = 'http://127.0.0.1:11434';
const OLLAMA_BINARY_CANDIDATES = [
  '/opt/homebrew/bin/ollama',
  '/usr/local/bin/ollama',
  'ollama'
];

function getAugmentedPath() {
  const current = process.env.PATH || '';
  const extras = ['/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', '/bin'];
  return Array.from(new Set([...current.split(':'), ...extras].filter(Boolean))).join(':');
}

function getExecOptions() {
  return {
    env: {
      ...process.env,
      PATH: getAugmentedPath()
    },
    maxBuffer: 16 * 1024 * 1024
  };
}

async function resolveOllamaCommand() {
  for (const candidate of OLLAMA_BINARY_CANDIDATES) {
    try {
      await execFileAsync(candidate, ['--version'], getExecOptions());
      return candidate;
    } catch (_) {
      // try next candidate
    }
  }
  throw new Error('Ollama binary not found. Install Ollama and open it once, or check that the binary exists in /opt/homebrew/bin or /usr/local/bin.');
}

function getRecommendedSetup() {
  const profile = getMachineProfile();
  return {
    totalMemoryGb: profile.totalMemoryGb,
    cpuCount: profile.cpuCount,
    platform: profile.platform,
    arch: profile.arch,
    chatModel: getOllamaRecommendedChatModel(profile),
    embeddingModel: 'nomic-embed-text'
  };
}

async function isOllamaInstalled() {
  try {
    await resolveOllamaCommand();
    return true;
  } catch (_) {
    const macPath = '/Applications/Ollama.app';
    return process.platform === 'darwin' && fs.existsSync(macPath);
  }
}

async function isOllamaRunning() {
  return new Promise((resolve) => {
    const req = http.get(`${OLLAMA_URL}/api/tags`, (res) => {
      res.resume();
      resolve(res.statusCode === 200);
    });
    req.on('error', () => resolve(false));
    req.setTimeout(2500, () => {
      req.destroy();
      resolve(false);
    });
  });
}

async function listModels() {
  try {
    const response = await new Promise((resolve, reject) => {
      const req = http.get(`${OLLAMA_URL}/api/tags`, (res) => {
        let data = '';
        res.on('data', (chunk) => { data += chunk; });
        res.on('end', () => resolve({ statusCode: res.statusCode, body: data }));
      });
      req.on('error', reject);
      req.setTimeout(3000, () => {
        req.destroy(new Error('Ollama model list timeout'));
      });
    });
    if (response.statusCode !== 200) return [];
    const payload = JSON.parse(response.body || '{}');
    return (payload.models || []).map((model) => model.name).filter(Boolean);
  } catch (_) {
    return [];
  }
}

async function getSystemStatus() {
  const recommended = getRecommendedSetup();
  const profile = getMachineProfile();
  const installed = await isOllamaInstalled();
  const running = installed ? await isOllamaRunning() : false;
  const models = running ? await listModels() : [];
  return {
    ...recommended,
    ollamaInstalled: installed,
    ollamaRunning: running,
    installedModels: models,
    preferredLocalRuntime: getPreferredLocalRuntime(profile),
    embeddedRuntime: getEmbeddedRuntimeRecommendation(profile)
  };
}

async function startOllama() {
  if (await isOllamaRunning()) {
    return { ok: true, alreadyRunning: true };
  }

  const ollamaCommand = await resolveOllamaCommand();
  if (process.platform === 'darwin' && fs.existsSync('/Applications/Ollama.app')) {
    spawn('open', ['-a', 'Ollama'], { detached: true, stdio: 'ignore' }).unref();
  } else {
    spawn(ollamaCommand, ['serve'], { detached: true, stdio: 'ignore', env: getExecOptions().env }).unref();
  }

  const start = Date.now();
  while (Date.now() - start < 30000) {
    if (await isOllamaRunning()) {
      return { ok: true, alreadyRunning: false };
    }
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }

  throw new Error('Ollama did not start within 30 seconds');
}

async function pullModel(modelName) {
  if (!modelName || !String(modelName).trim()) {
    throw new Error('Model name is required');
  }
  const normalizedModel = String(modelName).trim();
  await startOllama();

  const installed = await listModels();
  if (installed.includes(normalizedModel) || installed.includes(`${normalizedModel}:latest`)) {
    return {
      ok: true,
      model: normalizedModel,
      alreadyInstalled: true
    };
  }

  const ollamaCommand = await resolveOllamaCommand();
  await execFileAsync(ollamaCommand, ['pull', normalizedModel], getExecOptions());
  return {
    ok: true,
    model: normalizedModel
  };
}

module.exports = {
  getSystemStatus,
  startOllama,
  pullModel
};
