// Interaction Awareness — Shared library for Layer 2 hooks (Node.js)
// Imported by all hook scripts. Cross-platform: macOS, Linux, Windows.
// Zero npm dependencies — Node.js stdlib only.

import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, unlinkSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';

// --- Stdin ---

let _input = {};

export function readStdin() {
  try {
    const raw = readFileSync(0, 'utf8');
    _input = JSON.parse(raw);
  } catch {
    _input = {};
  }
}

export function getField(key) {
  return _input[key] ?? '';
}

export function getSessionId() {
  return getField('session_id');
}

export function getToolName() {
  return getField('tool_name');
}

export function getInput() {
  return _input;
}

// --- Paths ---

const PLUGIN_DATA = process.env.CLAUDE_PLUGIN_DATA
  || join(homedir(), '.claude', 'plugins', 'data', 'ai-psychosis');

export const DATA_DIR = PLUGIN_DATA;
export const SESSIONS_LOG = join(DATA_DIR, 'sessions.jsonl');
export const EVENTS_LOG = join(DATA_DIR, 'events.jsonl');
export const STATE_DIR = join(DATA_DIR, 'state');

// --- Layer configuration ---

let LAYER2_ENABLED = true;
let LAYER3_ENABLED = false;
let LAYER4_ENABLED = false;

export function initConfig() {
  const cwd = getField('cwd');

  // Project-level config takes precedence over global
  const candidates = [];
  if (cwd) candidates.push(join(cwd, '.claude', 'ai-psychosis.local.md'));
  candidates.push(join(homedir(), '.claude', 'ai-psychosis.local.md'));

  let content;
  for (const configFile of candidates) {
    try {
      content = readFileSync(configFile, 'utf8');
      break;
    } catch { /* try next */ }
  }
  if (!content) return;

  const match = content.match(/^---\n([\s\S]*?)\n---/);
  if (!match) return;

  const frontmatter = match[1];
  for (const line of frontmatter.split('\n')) {
    const m = line.match(/^(layer[234]):\s*(.+)/);
    if (!m) continue;
    const val = m[2].trim().replace(/^["']|["']$/g, '');
    if (m[1] === 'layer2') LAYER2_ENABLED = val === 'true';
    if (m[1] === 'layer3') LAYER3_ENABLED = val === 'true';
    if (m[1] === 'layer4') LAYER4_ENABLED = val === 'true';
  }
}

export function requireLayer(n) {
  let enabled = false;
  if (n === 2) enabled = LAYER2_ENABLED;
  if (n === 3) enabled = LAYER3_ENABLED;
  if (n === 4) enabled = LAYER4_ENABLED;

  if (!enabled) {
    outputContinue();
    process.exit(0);
  }
}

// --- Time helpers ---

export function nowIso() {
  return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
}

export function nowEpoch() {
  return Math.floor(Date.now() / 1000);
}

export function currentHour() {
  return new Date().getHours();
}

export function isLateNight() {
  const h = currentHour();
  return h >= 23 || h < 5;
}

// --- Thresholds ---

export const THRESHOLD_SOFT_DURATION = 90;
export const THRESHOLD_HARD_DURATION = 180;
export const THRESHOLD_SOFT_SESSIONS = 6;
export const THRESHOLD_HARD_SESSIONS = 10;
export const THRESHOLD_SOFT_BURST = 5;
export const THRESHOLD_HARD_BURST = 10;
export const THRESHOLD_BURST_INTERVAL = 30;
export const THRESHOLD_LOW_EDIT_RATIO = 10;
export const THRESHOLD_LOW_EDIT_MIN_DURATION = 30;
export const THRESHOLD_SOFT_DEP_FLAGS = 2;
export const THRESHOLD_HARD_DEP_FLAGS = 5;
export const COOLDOWN_SOFT = 1800;
export const COOLDOWN_HARD = 3600;
// v1.1.0 — counting threshold; tier-reduction logic is v1.2 scope
export const THRESHOLD_PUSHBACK_FLAGS = 2;

// --- v1.2 thresholds and domain-stakes table ---
//
// Sources: Anthropic guidance paper Appendix (April 2026), Figure A1 (stakes),
// Figure A4 (domain pushback rates). All domain identifiers are SINGULAR to
// match v1.1.0's `state.domain_context = 'relationship'` convention.

export const TIER1_TURN_THRESHOLD = 15;
export const TIER2_SESSION_THRESHOLD = 3;
export const THRESHOLD_VALSEEK_FLAGS = 3;

// Domain stakes weights — Figure A1 high/very-high stakes domains carry
// higher multipliers; consumer/personal_dev are baseline 1.0.
export const DOMAIN_STAKES = Object.freeze({
  legal: 1.5,
  parenting: 1.5,
  health: 1.5,
  financial: 1.5,
  relationship: 1.3,
  spirituality: 1.2,
  professional: 1.1,
  wellbeing: 1.2,
  lifepath: 1.1,
  values: 1.2,
  personal_dev: 1.0,
  consumer: 1.0,
  default: 1.0
});

// Pushback in these domains signals validation-pressing (Figure A4 — relationships
// 21%, spirituality 19%); pushback alert fires.
export const HIGH_SYCOPHANCY_DOMAINS = Object.freeze(['relationship', 'spirituality']);

// High-stakes guidance domains (Figure A1 high/very-high). Tier-1 user-info
// alert fires only when domain_context intersects this set.
export const HIGH_STAKES_DOMAINS = Object.freeze(['legal', 'parenting', 'health', 'financial']);

// Info-seeking domains where pushback signals healthy self-advocacy (Figure A4 —
// parenting 7.9%, legal/health/financial 80–94% pushback rate). Pushback alert
// is suppressed when domain_context is entirely within this set.
export const INFO_DOMAINS = Object.freeze(['legal', 'parenting', 'health', 'financial', 'professional']);

// --- Session counting ---

export function sessionsToday() {
  const today = new Date().toISOString().slice(0, 10);
  if (!existsSync(SESSIONS_LOG)) return 0;

  try {
    const lines = readFileSync(SESSIONS_LOG, 'utf8').split('\n').filter(Boolean);
    const ids = new Set();
    for (const line of lines) {
      try {
        const rec = JSON.parse(line);
        if (rec.start && rec.start.startsWith(today)) {
          ids.add(rec.session_id);
        }
      } catch { /* skip malformed lines */ }
    }
    return ids.size;
  } catch {
    return 0;
  }
}

// Tail-first scan: return the N most recent end records (records with
// duration_min defined) in chronological order. Cost is bounded by N, not
// by total file size — a 50K-record sessions.jsonl is read once but only
// the last few hundred lines are JSON-parsed before N is satisfied.
export function readRecentEndRecords(n) {
  if (!Number.isFinite(n) || n <= 0) return [];
  if (!existsSync(SESSIONS_LOG)) return [];

  let lines;
  try {
    lines = readFileSync(SESSIONS_LOG, 'utf8').split('\n');
  } catch {
    return [];
  }

  const collected = [];
  for (let i = lines.length - 1; i >= 0 && collected.length < n; i--) {
    const line = lines[i];
    if (!line) continue;
    try {
      const rec = JSON.parse(line);
      if (rec.duration_min !== undefined) {
        collected.push(rec);
      }
    } catch { /* skip malformed */ }
  }

  // Reverse so caller receives oldest-first (chronological order).
  return collected.reverse();
}

// --- State file management ---

export function sessionStateFile(sid) {
  sid = sid || getSessionId();
  return join(STATE_DIR, `${sid}.json`);
}

export function readState(sid) {
  const sf = sessionStateFile(sid);
  try {
    return JSON.parse(readFileSync(sf, 'utf8'));
  } catch {
    return {};
  }
}

export function getStateField(key, sid) {
  const state = readState(sid);
  return state[key] ?? '';
}

export function getStateInt(key, sid) {
  const state = readState(sid);
  return Math.floor(Number(state[key]) || 0);
}

export function writeState(obj, sid) {
  const sf = sessionStateFile(sid);
  writeFileSync(sf, JSON.stringify(obj, null, 2) + '\n');
}

export function updateStateField(key, value, sid) {
  const state = readState(sid);
  state[key] = value;
  writeState(state, sid);
}

export function incrementStateField(key, sid) {
  const state = readState(sid);
  state[key] = (Number(state[key]) || 0) + 1;
  writeState(state, sid);
}

// --- Cooldown ---

export function checkCooldown(cooldownSecs, sid) {
  const lastWarning = getStateInt('last_warning_epoch', sid);
  const now = nowEpoch();
  return (now - lastWarning) >= cooldownSecs;
}

export function recordWarning(sid) {
  const state = readState(sid);
  state.last_warning_epoch = nowEpoch();
  writeState(state, sid);
}

// --- Output helpers ---

export function outputContinue() {
  process.stdout.write(JSON.stringify({ continue: true }) + '\n');
}

export function outputWithContext(message) {
  process.stdout.write(JSON.stringify({
    continue: true,
    hookSpecificOutput: {
      additionalContext: message
    }
  }) + '\n');
}

// --- File helpers ---

export function ensureDir(dir) {
  mkdirSync(dir, { recursive: true });
}

export function appendJsonl(file, obj) {
  appendFileSync(file, JSON.stringify(obj) + '\n');
}

export function removeFile(file) {
  try { unlinkSync(file); } catch { /* ignore if missing */ }
}
