'use strict';
// Resolve hook configuration for a session.
//
// Two layers:
//   1. Per-project:  <cwd>/.ctm-hooks.json   (optional, winner for matching keys)
//   2. Global:       dbModule.getSetting('ctm_status_hooks', {}) (fallback)
//
// Schema:
//   {
//     "onIdle": "<shell command>",            // fires when state -> idle
//     "onBusy": "<shell command>",            // fires when state -> busy
//     "onWaitingInput": "<shell command>",    // fires when state -> waiting_input
//     "timeoutMs": 10000,                     // optional, default 10s
//     "enabled": true                         // optional, default true
//   }
//
// Missing commands are simply skipped. Null / unset / empty string all disable
// the corresponding transition hook.

const fs = require('fs');
const path = require('path');

const PROJECT_FILE = '.ctm-hooks.json';

function _readJsonSafe(p) {
  try {
    const s = fs.readFileSync(p, 'utf8');
    return JSON.parse(s);
  } catch { return null; }
}

// Simple in-proc cache keyed by absolute path + mtime — avoids disk reads on
// every state transition.
const _projectCache = new Map(); // absPath -> { mtime, data }

function _readProjectConfig(cwd) {
  if (!cwd) return null;
  const abs = path.join(cwd, PROJECT_FILE);
  let stat;
  try { stat = fs.statSync(abs); } catch { _projectCache.delete(abs); return null; }
  const cached = _projectCache.get(abs);
  if (cached && cached.mtime === stat.mtimeMs) return cached.data;
  const data = _readJsonSafe(abs);
  _projectCache.set(abs, { mtime: stat.mtimeMs, data });
  return data;
}

function _normalize(cfg) {
  if (!cfg || typeof cfg !== 'object') return null;
  const out = {
    enabled: cfg.enabled !== false,
    timeoutMs: (typeof cfg.timeoutMs === 'number' && cfg.timeoutMs > 0) ? cfg.timeoutMs : null,
  };
  for (const key of ['onIdle', 'onBusy', 'onWaitingInput']) {
    const v = cfg[key];
    if (typeof v === 'string' && v.trim()) out[key] = v.trim();
  }
  return out;
}

// Resolve the merged hook config for a session.
// projectCfg overrides globalCfg for present keys (commands, timeout, enabled).
function resolveHooks({ cwd, dbModule }) {
  const proj = _normalize(_readProjectConfig(cwd));
  let glob = null;
  if (dbModule && typeof dbModule.getSetting === 'function') {
    try { glob = _normalize(dbModule.getSetting('ctm_status_hooks', null)); } catch { glob = null; }
  }
  if (!proj && !glob) return null;
  const merged = { enabled: true, timeoutMs: 10000 };
  if (glob) {
    if (glob.onIdle) merged.onIdle = glob.onIdle;
    if (glob.onBusy) merged.onBusy = glob.onBusy;
    if (glob.onWaitingInput) merged.onWaitingInput = glob.onWaitingInput;
    if (glob.timeoutMs) merged.timeoutMs = glob.timeoutMs;
    if (glob.enabled === false) merged.enabled = false;
  }
  if (proj) {
    if (proj.onIdle) merged.onIdle = proj.onIdle;
    if (proj.onBusy) merged.onBusy = proj.onBusy;
    if (proj.onWaitingInput) merged.onWaitingInput = proj.onWaitingInput;
    if (proj.timeoutMs) merged.timeoutMs = proj.timeoutMs;
    if (proj.enabled === false) merged.enabled = false; // project can explicitly disable
    else if (proj.enabled === true) merged.enabled = true;
  }
  if (!merged.onIdle && !merged.onBusy && !merged.onWaitingInput) return null;
  return merged;
}

// Map from state name (emitted by session-state-bus) to hook key.
function commandForState(cfg, newState) {
  if (!cfg || !cfg.enabled) return null;
  if (newState === 'idle') return cfg.onIdle || null;
  if (newState === 'busy') return cfg.onBusy || null;
  if (newState === 'waiting_input') return cfg.onWaitingInput || null;
  return null;
}

function _clearCache() { _projectCache.clear(); }

module.exports = { resolveHooks, commandForState, PROJECT_FILE, _clearCache };
