import { spawn } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { ROOT } from './paths.js';
import { getSecret } from './secrets.js';

const DEFAULT_TIMEOUT_MS = 600000;

export function resolveHermesDir() {
  const fromVault = getSecret('HERMES_AGENT_DIR');
  return path.resolve(process.env.HERMES_AGENT_DIR || fromVault || path.join(ROOT, '..', 'hermes-agent'));
}

export function isHermesAvailable() {
  try {
    const hermesDir = resolveHermesDir();
    const script = path.join(ROOT, 'scripts', 'hermes-command.py');
    return fs.existsSync(hermesDir) && fs.existsSync(script);
  } catch {
    return false;
  }
}

export function getHermesStatus() {
  const hermesDir = resolveHermesDir();
  const script = path.join(ROOT, 'scripts', 'hermes-command.py');
  const dirExists = fs.existsSync(hermesDir);
  const scriptExists = fs.existsSync(script);
  return {
    available: dirExists && scriptExists,
    hermes_dir: hermesDir,
    dir_exists: dirExists,
    script_exists: scriptExists,
    env_override: Boolean(process.env.HERMES_AGENT_DIR || getSecret('HERMES_AGENT_DIR')),
  };
}

export function readLocalEnv() {
  const file = path.join(ROOT, '.env.local');
  if (!fs.existsSync(file)) return {};
  const values = {};
  const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('#') || !trimmed.includes('=')) continue;
    const idx = trimmed.indexOf('=');
    const key = trimmed.slice(0, idx).trim();
    let value = trimmed.slice(idx + 1).trim();
    if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
      value = value.slice(1, -1);
    }
    values[key] = value;
  }
  return values;
}

export function parseJsonOutput(stdout) {
  const trimmed = String(stdout || '').trim();
  if (!trimmed) throw new Error('Hermes returned no output');
  const lastLine = trimmed.split('\n').filter(Boolean).at(-1);
  try {
    return JSON.parse(lastLine);
  } catch {
    throw new Error(`Hermes returned non-JSON output: ${trimmed.slice(-500)}`);
  }
}

export function resolvePythonCommand(hermesDir = resolveHermesDir()) {
  const candidates = [
    process.env.HERMES_PYTHON,
    process.env.PYTHON,
    path.join(hermesDir, '.venv', 'bin', 'python'),
    '/opt/homebrew/bin/python3.12',
    '/opt/homebrew/bin/python3.11',
    '/opt/homebrew/bin/python3.10',
    path.join(process.env.HOME || '', '.local', 'bin', 'python3.12'),
    path.join(process.env.HOME || '', '.local', 'bin', 'python3.11'),
    path.join(process.env.HOME || '', '.local', 'bin', 'python3.10'),
    'python3.12',
    'python3.11',
    'python3.10',
    'python3',
  ].filter(Boolean);

  for (const candidate of candidates) {
    if (candidate.includes('/') && !fs.existsSync(candidate)) continue;
    return candidate;
  }
  return 'python3';
}

export function buildHermesChildEnv(extra = {}) {
  const hermesDir = resolveHermesDir();
  const python = resolvePythonCommand(hermesDir);
  return {
    hermesDir,
    python,
    env: { ...readLocalEnv(), ...process.env, ...extra, HERMES_AGENT_DIR: hermesDir, HERMES_PYTHON_USED: python },
  };
}

export function buildHermesCommandArgs({ prompt, session_id, model, cwd } = {}) {
  const hermesDir = resolveHermesDir();
  const script = path.join(ROOT, 'scripts', 'hermes-command.py');
  const args = [
    script,
    '--hermes-dir', hermesDir,
    '--prompt', prompt,
  ];

  if (session_id) args.push('--session-id', session_id);
  if (model) args.push('--model', model);
  if (cwd) args.push('--cwd', cwd);
  return args;
}

export function runHermesCommand({ prompt, session_id, model, timeout_ms, cwd } = {}) {
  if (!prompt || typeof prompt !== 'string') throw new Error('prompt is required');

  if (process.env.HERMES_MOCK_RESPONSE) {
    return Promise.resolve({
      ok: true,
      response: process.env.HERMES_MOCK_RESPONSE,
      session_id: session_id || 'madarx-hermes-mock',
      model: model || null,
      mocked: true,
    });
  }

  const { hermesDir, python, env } = buildHermesChildEnv();
  const timeout = Number(timeout_ms || process.env.HERMES_TIMEOUT_MS || DEFAULT_TIMEOUT_MS);
  const args = buildHermesCommandArgs({ prompt, session_id, model, cwd });

  return new Promise((resolve, reject) => {
    const child = spawn(python, args, {
      cwd: hermesDir,
      env,
      stdio: ['ignore', 'pipe', 'pipe'],
    });

    let stdout = '';
    let stderr = '';
    let settled = false;
    const timer = setTimeout(() => {
      settled = true;
      child.kill('SIGTERM');
      reject(new Error(`Hermes timed out after ${Math.round(timeout / 1000)}s`));
    }, timeout);

    child.stdout.on('data', chunk => { stdout += chunk.toString(); });
    child.stderr.on('data', chunk => { stderr += chunk.toString(); });
    child.on('error', error => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      reject(error);
    });
    child.on('close', code => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      if (code !== 0) {
        reject(new Error((stderr || stdout || `Hermes exited with code ${code}`).trim()));
        return;
      }
      try {
        const result = parseJsonOutput(stdout);
        if (!result.ok) throw new Error(result.error || 'Hermes command failed');
        if (!String(result.response || '').trim()) {
          throw new Error('Hermes completed without a final response');
        }
        resolve(result);
      } catch (error) {
        reject(error);
      }
    });
  });
}
