/**
 * LLM Gate — Zero-Cost Enforcement Layer
 * 
 * Controls whether agents and The Hearth can call external (cloud) LLMs.
 * Default: BLOCKED. All agent reasoning uses RAG + deterministic rules.
 * The gate can be opened per-domain or globally via Configuration.
 * 
 * Standing Order: "Block external LLM calls by default for agent interactions."
 */

import { prisma } from '@/lib/db';

export type LLMGateScope = 
  | 'agents'        // Agent hierarchy interactions/chat
  | 'dark-factory'  // The Hearth code generation
  | 'perception'    // Perception/signal detection
  | 'self-mod'      // Self-modification proposals
  | 'observer'      // Observer action recommendations
  | 'companion'     // Tes companion chat
  | 'global';       // Master override

export interface LLMGateConfig {
  /** Master switch — if false, ALL external LLM calls are blocked */
  globalEnabled: boolean;
  /** Per-scope overrides */
  scopes: Record<LLMGateScope, boolean>;
  /** Reason for current configuration */
  lastModifiedReason: string;
  /** Who last modified */
  lastModifiedBy: string;
  /** Timestamp */
  lastModifiedAt: string;
}

const DEFAULT_GATE_CONFIG: LLMGateConfig = {
  globalEnabled: false,
  scopes: {
    'agents': false,
    'dark-factory': false,
    'perception': false,
    'self-mod': false,
    'observer': false,
    'companion': false,
    'global': false,
  },
  lastModifiedReason: 'Default zero-cost configuration',
  lastModifiedBy: 'system',
  lastModifiedAt: new Date().toISOString(),
};

// In-memory cache
let _cachedConfig: LLMGateConfig | null = null;
let _cacheTime = 0;
const CACHE_TTL_MS = 30_000; // 30 seconds

/**
 * Get the current LLM gate configuration.
 * Reads from database (SystemConfig table), falls back to defaults.
 */
export async function getLLMGateConfig(): Promise<LLMGateConfig> {
  const now = Date.now();
  if (_cachedConfig && (now - _cacheTime) < CACHE_TTL_MS) {
    return _cachedConfig;
  }

  try {
    const record = await prisma.systemConfig.findUnique({
      where: { key: 'llm_gate_config' },
    });

    if (record?.value) {
      _cachedConfig = record.value as unknown as LLMGateConfig;
      _cacheTime = now;
      return _cachedConfig;
    }
  } catch (e) {
    // Table might not exist yet, use defaults
    console.warn('[LLM Gate] Could not read config from DB, using defaults');
  }

  _cachedConfig = DEFAULT_GATE_CONFIG;
  _cacheTime = now;
  return _cachedConfig;
}

/**
 * Check if external LLM calls are allowed for a given scope.
 * Returns { allowed: boolean, reason: string }.
 */
export async function checkLLMGate(scope: LLMGateScope): Promise<{ allowed: boolean; reason: string }> {
  const config = await getLLMGateConfig();

  // Global switch overrides everything
  if (!config.globalEnabled) {
    return {
      allowed: false,
      reason: `External LLM blocked: global gate is OFF (zero-cost mode). Enable in Configuration → AI & LLM.`,
    };
  }

  // Check scope-specific toggle
  const scopeAllowed = config.scopes[scope] ?? false;
  if (!scopeAllowed) {
    return {
      allowed: false,
      reason: `External LLM blocked for scope '${scope}'. Enable in Configuration → AI & LLM → ${scope}.`,
    };
  }

  return {
    allowed: true,
    reason: `External LLM allowed for scope '${scope}'.`,
  };
}

/**
 * Update the LLM gate configuration.
 * Persists to database and invalidates cache.
 */
export async function updateLLMGateConfig(
  updates: Partial<LLMGateConfig>,
  modifiedBy: string = 'operator'
): Promise<LLMGateConfig> {
  const current = await getLLMGateConfig();
  const newConfig: LLMGateConfig = {
    ...current,
    ...updates,
    scopes: {
      ...current.scopes,
      ...(updates.scopes || {}),
    },
    lastModifiedBy: modifiedBy,
    lastModifiedAt: new Date().toISOString(),
    lastModifiedReason: updates.lastModifiedReason || current.lastModifiedReason,
  };

  try {
    await prisma.systemConfig.upsert({
      where: { key: 'llm_gate_config' },
      create: {
        key: 'llm_gate_config',
        value: newConfig as any,
      },
      update: {
        value: newConfig as any,
      },
    });
  } catch (e) {
    console.error('[LLM Gate] Failed to persist config:', e);
  }

  // Invalidate cache
  _cachedConfig = newConfig;
  _cacheTime = Date.now();

  return newConfig;
}

/**
 * Get a human-readable status summary of the LLM gate.
 */
export async function getLLMGateStatus(): Promise<{
  mode: 'zero-cost' | 'selective' | 'unrestricted';
  summary: string;
  config: LLMGateConfig;
}> {
  const config = await getLLMGateConfig();

  if (!config.globalEnabled) {
    return {
      mode: 'zero-cost',
      summary: 'All external LLM calls blocked. Agents use RAG + deterministic rules only.',
      config,
    };
  }

  const enabledScopes = Object.entries(config.scopes)
    .filter(([, v]) => v)
    .map(([k]) => k);

  if (enabledScopes.length === Object.keys(config.scopes).length) {
    return {
      mode: 'unrestricted',
      summary: 'All scopes may use external LLMs.',
      config,
    };
  }

  return {
    mode: 'selective',
    summary: `External LLM enabled for: ${enabledScopes.join(', ') || 'none'}`,
    config,
  };
}
