'use strict';
// Glue module: SessionStateBus + hook-executor + status-hooks-config.
//
// This is the "public API" that server.js imports. Server wires PTY output
// events, approval events, and session close events to the corresponding
// bus methods, and the hooks module takes care of resolving configuration
// and running the shell commands on each transition.
//
// Per-session context is stored internally so hook-env has the branch/cwd
// at fire time (without touching sessions/db from here).

const { SessionStateBus } = require('./session-state-bus');
const { runHook, buildHookEnv } = require('./hook-executor');
const { resolveHooks, commandForState } = require('./status-hooks-config');
const { listStateDetectors } = require('../workers/state-detectors');

const _sessionMeta = new Map(); // sessionId -> { providerId, cwd, branch, tabTitle }

const bus = new SessionStateBus();
// Provider detectors own their idle debounce. Claude and Codex both need wider
// holds for sparse TUI progress frames; the shell default remains short.
for (const detector of listStateDetectors()) {
  if (detector && Number.isFinite(detector.idleDebounceMs)) {
    bus.setProviderDebounce(detector.providerId, detector.idleDebounceMs);
  }
}
bus.setDefaultDebounce(1500);

let _dbModule = null;
let _getSessionMeta = null; // optional late-resolver for fresh meta

function configure({ dbModule, getSessionMeta } = {}) {
  _dbModule = dbModule || null;
  _getSessionMeta = typeof getSessionMeta === 'function' ? getSessionMeta : null;
}

function registerSession(sessionId, { providerId, cwd, branch, tabTitle } = {}) {
  _sessionMeta.set(sessionId, { providerId: providerId || '', cwd: cwd || '', branch: branch || '', tabTitle: tabTitle || '' });
  bus.register(sessionId, providerId || '');
}

function updateSessionMeta(sessionId, patch) {
  const cur = _sessionMeta.get(sessionId) || {};
  _sessionMeta.set(sessionId, { ...cur, ...patch });
}

function unregisterSession(sessionId) {
  _sessionMeta.delete(sessionId);
  bus.reset(sessionId);
}

function onOutput(sessionId) {
  const meta = _sessionMeta.get(sessionId) || {};
  bus.onOutput(sessionId, meta.providerId);
}
function onApprovalDetected(sessionId) {
  const meta = _sessionMeta.get(sessionId) || {};
  bus.onApprovalDetected(sessionId, meta.providerId);
}
function onApprovalResolved(sessionId) {
  bus.onApprovalResolved(sessionId);
}
function clearWaitingInput(sessionId) {
  bus.clearWaitingInput(sessionId);
}

// Wire bus events → hook-executor.
bus.on('statechange', async (ev) => {
  try {
    let meta = _sessionMeta.get(ev.sessionId);
    if (_getSessionMeta) {
      try { const fresh = _getSessionMeta(ev.sessionId); if (fresh) meta = { ...meta, ...fresh }; } catch {}
    }
    if (!meta) return;
    const cfg = resolveHooks({ cwd: meta.cwd, dbModule: _dbModule });
    if (!cfg) return;
    const cmd = commandForState(cfg, ev.newState);
    if (!cmd) return;
    const env = buildHookEnv({
      oldState: ev.oldState,
      newState: ev.newState,
      sessionId: ev.sessionId,
      providerId: meta.providerId || '',
      worktreePath: meta.cwd || '',
      worktreeBranch: meta.branch || '',
      tabTitle: meta.tabTitle || '',
    });
    const result = await runHook({ command: cmd, env, timeoutMs: cfg.timeoutMs, cwd: meta.cwd });
    if (!result.ok) {
      console.warn(`[status-hook] ${ev.newState} command failed (${result.error || 'unknown'}) in session ${ev.sessionId.slice(0, 8)}`);
    } else {
      console.log(`[status-hook] ${ev.oldState}→${ev.newState} session=${ev.sessionId.slice(0, 8)} duration=${result.durationMs}ms`);
    }
  } catch (e) {
    console.error('[status-hook] executor error:', e.message);
  }
});

module.exports = {
  configure,
  registerSession,
  updateSessionMeta,
  unregisterSession,
  onOutput,
  onApprovalDetected,
  onApprovalResolved,
  clearWaitingInput,
  // Introspection / tests
  _bus: bus,
  _sessionMeta,
};
