import path from 'node:path';
import fs from 'node:fs';
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { loadProjectEnv } from '../lib/load-env.js';

const envResult = loadProjectEnv();
if (envResult.loaded) console.log(`[whatsapp-daemon] loaded env from ${envResult.file}`);
else console.log(`[whatsapp-daemon] no .env.local found — ANTHROPIC_API_KEY etc. must be set externally`);

import { getDb } from '../lib/db.js';
import { appendAudit } from '../lib/runtime.js';
import { pollAndDispatch } from '../lib/whatsapp-router.js';
import { getBridgeHealth } from '../lib/whatsapp.js';
import { WHATSAPP_BRIDGE_DIR, WHATSAPP_SESSION_DIR } from '../lib/paths.js';

const here = path.dirname(fileURLToPath(import.meta.url));
const bridgeDir = WHATSAPP_BRIDGE_DIR || path.join(here, 'whatsapp-bridge');
const port = Number(process.env.WHATSAPP_BRIDGE_PORT || 3210);
const host = process.env.WHATSAPP_BRIDGE_HOST || '127.0.0.1';
const pollIntervalMs = Number(process.env.WHATSAPP_POLL_INTERVAL_MS || 1500);
const onceMode = process.env.WHATSAPP_DAEMON_ONCE === '1';

const BACKOFF_BASE_MS = 1000;
const BACKOFF_MAX_MS = 60_000;
let crashes = 0;
let stopped = false;
let bridgeProc = null;
let lockAcquired = false;

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

// Single-instance lock. Two daemons sharing one session dir spawn two Baileys
// sockets on the same WhatsApp creds, which the server rejects as a duplicate
// linked device — producing an endless "Stream Errored (conflict)" reconnect
// ping-pong. The lock makes a second daemon refuse to start instead.
const LOCK_FILE = path.join(path.dirname(WHATSAPP_SESSION_DIR), 'daemon.lock');

function pidAlive(pid) {
  if (!Number.isInteger(pid) || pid <= 0) return false;
  try {
    process.kill(pid, 0);
    return true;
  } catch (err) {
    // ESRCH = no such process (stale); EPERM = exists but not ours (alive).
    return err.code === 'EPERM';
  }
}

function acquireLock() {
  if (fs.existsSync(LOCK_FILE)) {
    const existing = Number(fs.readFileSync(LOCK_FILE, 'utf8').trim());
    if (pidAlive(existing)) {
      console.error(`[whatsapp-daemon] another daemon is already running (pid ${existing}). Refusing to start a second instance.`);
      console.error(`[whatsapp-daemon] if that pid is dead, remove ${LOCK_FILE} and retry.`);
      process.exit(1);
    }
    console.warn(`[whatsapp-daemon] removing stale lock from dead pid ${existing}`);
  }
  fs.writeFileSync(LOCK_FILE, String(process.pid), 'utf8');
  lockAcquired = true;
}

function releaseLock() {
  if (!lockAcquired) return;
  try {
    const owner = Number(fs.readFileSync(LOCK_FILE, 'utf8').trim());
    if (owner === process.pid) fs.unlinkSync(LOCK_FILE);
  } catch {}
  lockAcquired = false;
}

function bridgeNodeModules() {
  return path.join(bridgeDir, 'node_modules');
}

async function ensureBridgeDeps() {
  if (fs.existsSync(bridgeNodeModules())) return;
  console.log('[whatsapp-daemon] installing bridge dependencies (one-time)…');
  await new Promise((resolve, reject) => {
    const proc = spawn('npm', ['install', '--no-audit', '--no-fund', '--loglevel=warn'], {
      cwd: bridgeDir,
      stdio: 'inherit',
      env: process.env,
    });
    proc.on('error', reject);
    proc.on('exit', (code) => {
      if (code === 0) resolve();
      else reject(new Error(`npm install exited with code ${code}`));
    });
  });
}

function spawnBridge() {
  const env = {
    ...process.env,
    WHATSAPP_SESSION_DIR,
    WHATSAPP_BRIDGE_PORT: String(port),
    WHATSAPP_BRIDGE_HOST: host,
  };
  bridgeProc = spawn(process.execPath, ['bridge.js'], {
    cwd: bridgeDir,
    stdio: ['ignore', 'inherit', 'inherit'],
    env,
  });
  bridgeProc.on('exit', (code, signal) => {
    bridgeProc = null;
    if (stopped) return;
    crashes += 1;
    const wait = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * Math.pow(2, Math.min(crashes - 1, 6)));
    console.error(`[whatsapp-daemon] bridge exited code=${code} signal=${signal}; restarting in ${wait}ms (crash #${crashes})`);
    appendAudit({
      type: 'whatsapp.bridge.crashed',
      summary: 'WhatsApp bridge subprocess exited',
      status: 'crashed',
      meta: { code, signal, crashes, restart_in_ms: wait },
    });
    setTimeout(() => {
      if (!stopped) spawnBridge();
    }, wait);
  });
  appendAudit({ type: 'whatsapp.bridge.spawn', summary: 'WhatsApp bridge spawned', status: 'starting', meta: { port, host } });
  console.log(`[whatsapp-daemon] bridge spawned (pid ${bridgeProc.pid}) on ${host}:${port}`);
}

async function waitForBridgeReady({ timeoutMs = 30000 } = {}) {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    if (stopped) return false;
    const health = await getBridgeHealth();
    if (health?.ok) {
      crashes = 0;
      return true;
    }
    await new Promise((resolve) => setTimeout(resolve, 500));
  }
  return false;
}

function shutdown(signal) {
  if (stopped) return;
  stopped = true;
  console.log(`[whatsapp-daemon] ${signal} received, shutting down`);
  appendAudit({ type: 'whatsapp.daemon.stop', summary: 'WhatsApp daemon stopped', status: 'stopped', meta: { signal } });
  try { bridgeProc?.kill('SIGTERM'); } catch {}
  releaseLock();
  setTimeout(() => process.exit(0), 500);
}
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));

try {
  acquireLock();
  getDb();
  await ensureBridgeDeps();

  if (onceMode) {
    spawnBridge();
    const ready = await waitForBridgeReady({ timeoutMs: 60_000 });
    console.log(`[whatsapp-daemon] once-mode ready=${ready}`);
    shutdown('once-mode-complete');
    process.exit(ready ? 0 : 1);
  }

  spawnBridge();
  await waitForBridgeReady();
  appendAudit({ type: 'whatsapp.daemon.start', summary: 'WhatsApp daemon started', status: 'running', meta: { port, host, pollIntervalMs } });

  while (!stopped) {
    try {
      await pollAndDispatch();
    } catch (err) {
      console.error('[whatsapp-daemon] poll error:', err.message);
    }
    await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
  }
} catch (error) {
  console.error('[whatsapp-daemon] fatal:', error);
  appendAudit({ type: 'whatsapp.daemon.fatal', summary: `WhatsApp daemon fatal: ${error.message}`, status: 'failed' });
  releaseLock();
  process.exit(1);
}
