/**
 * Sentinel limits disk persistence
 */

import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import {
  getSentinelLimitsFilePath,
  loadSentinelLimitsFromDisk,
  saveSentinelLimitsToDisk,
  applyPersistedSentinelConfig,
} from '../security/sentinel-config-store.js';
import { Sentinel, policyManager } from '@4runr/sentinel';
import type { RunState } from '@4runr/sentinel';

describe('sentinel-config-store', () => {
  const originalAppData = process.env['APPDATA'];
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), '4runr-sentinel-'));
    process.env['APPDATA'] = tmpDir;
    Sentinel.resetInstance();
  });

  afterEach(() => {
    Sentinel.resetInstance();
    if (originalAppData === undefined) {
      delete process.env['APPDATA'];
    } else {
      process.env['APPDATA'] = originalAppData;
    }
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it('round-trips config to disk', () => {
    const config = {
      enabled: true,
      runMaxDurationMs: 90_000,
      runMaxTokens: 8_000,
      runIdleMs: 45_000,
      loopWindow: 10,
      loopMax: 3,
      runMaxCost: 1.25,
    };
    saveSentinelLimitsToDisk(config);
    expect(fs.existsSync(getSentinelLimitsFilePath())).toBe(true);
    const loaded = loadSentinelLimitsFromDisk();
    expect(loaded).toMatchObject(config);
  });

  it('applyPersistedSentinelConfig merges when env field unset', () => {
    delete process.env['RUN_IDLE_MS'];
    delete process.env['SENTINEL_IDLE_MS'];
    saveSentinelLimitsToDisk({
      enabled: true,
      runMaxDurationMs: 60_000,
      runMaxTokens: 16_000,
      runIdleMs: 99_000,
      loopWindow: 15,
      loopMax: 5,
      runMaxCost: 2.0,
    });
    const sentinel = Sentinel.getInstance();
    applyPersistedSentinelConfig(sentinel);
    expect(sentinel.getConfig().runIdleMs).toBe(99_000);
  });

  it('simulates Gateway restart: disk limits reload into a fresh Sentinel singleton', () => {
    delete process.env['RUN_IDLE_MS'];
    delete process.env['SENTINEL_IDLE_MS'];

    const distinctiveIdle = 12_345;
    saveSentinelLimitsToDisk({
      enabled: true,
      runMaxDurationMs: 600_000,
      runMaxTokens: 50_000,
      runIdleMs: distinctiveIdle,
      loopWindow: 15,
      loopMax: 5,
      runMaxCost: 3.5,
    });

    Sentinel.resetInstance();
    const sentinel = Sentinel.getInstance();
    expect(sentinel.getConfig().runIdleMs).not.toBe(distinctiveIdle);

    expect(applyPersistedSentinelConfig(sentinel)).toBe(true);
    expect(sentinel.getConfig().runIdleMs).toBe(distinctiveIdle);
    expect(sentinel.getConfig().runMaxCost).toBe(3.5);
  });

  it('persisted limits drive real policy evaluation (not display-only)', () => {
    delete process.env['RUN_IDLE_MS'];
    delete process.env['SENTINEL_IDLE_MS'];

    const idleMs = 8_000;
    saveSentinelLimitsToDisk({
      enabled: true,
      runMaxDurationMs: 600_000,
      runMaxTokens: 50_000,
      runIdleMs: idleMs,
      loopWindow: 15,
      loopMax: 5,
      runMaxCost: 1.0,
    });

    Sentinel.resetInstance();
    const sentinel = Sentinel.getInstance();
    applyPersistedSentinelConfig(sentinel);

    const runState: RunState = {
      runId: 'run-persist-verify',
      createdAt: Date.now(),
      startedAt: Date.now() - 20_000,
      lastTokenAt: Date.now() - 20_000,
      tokenCount: 0,
      status: 'running',
      events: [],
    };

    const violations = policyManager.evaluatePolicies(
      'run-persist-verify',
      runState,
      sentinel.getConfig()
    );
    const idleViolation = violations.find((v) => v.policy === 'idle');
    expect(idleViolation).toBeDefined();
    expect(idleViolation!.threshold).toBe(idleMs);
  });
});
