/*
 * Priority Forge — memory-hub adapter factory + singleton
 *
 * Hands out the right MemoryHub impl for the current config. Lazily
 * constructs, caches by (backend, baseUrl, apiKey), and respects the
 * XMEM_FORCE_NOOP=1 kill switch as defense in depth.
 */

import { NoopMemoryHub } from './adapters/noop-adapter';
import { DryrunMemoryHub } from './adapters/dryrun-adapter';
import { XMemMemoryHub } from './adapters/xmem-adapter';
import { PgvectorMemoryHub } from './adapters/pgvector-adapter';
import { loadMemoryHubConfig } from './config';
import {
  MemoryHub,
  MemoryHubBackend,
  MemoryHubConfig,
  OrchestrationHub,
} from './types';

// Adapters implement both MemoryHub and OrchestrationHub on the same class,
// so a single cached instance serves both planes.
type CombinedHub = MemoryHub & OrchestrationHub;

let cached: { key: string; hub: CombinedHub } | null = null;

function cacheKey(backend: MemoryHubBackend, cfg: MemoryHubConfig): string {
  if (backend === 'xmem') {
    return `xmem::${cfg.xmem.baseUrl}::${cfg.xmem.apiKey ? 'k' : ''}::${cfg.xmem.orgID || ''}::${cfg.xmem.environment ?? 'production'}`;
  }
  if (backend === 'pgvector') {
    return `pgvector::${cfg.pgvector.connectionString}`;
  }
  return backend;
}

function getCombinedHub(): CombinedHub {
  if (process.env.XMEM_FORCE_NOOP === '1') {
    if (!cached || cached.key !== 'noop') {
      cached = { key: 'noop', hub: new NoopMemoryHub() };
    }
    return cached.hub;
  }

  const cfg = loadMemoryHubConfig();
  const effective: MemoryHubBackend = cfg.enabled ? cfg.backend : 'noop';
  const key = cacheKey(effective, cfg);
  if (cached && cached.key === key) return cached.hub;

  let hub: CombinedHub;
  switch (effective) {
    case 'xmem':
      hub = new XMemMemoryHub({
        baseUrl: cfg.xmem.baseUrl,
        apiKey: cfg.xmem.apiKey,
        orgID: cfg.xmem.orgID,
        environment: cfg.xmem.environment,
      });
      break;
    case 'pgvector':
      hub = new PgvectorMemoryHub({
        connectionString: cfg.pgvector.connectionString,
        teamId: cfg.teamId,
        agentId: cfg.agentId,
        agentToken: cfg.pgvector.agentToken,
      });
      break;
    case 'dryrun':
      hub = new DryrunMemoryHub();
      break;
    case 'noop':
    default:
      hub = new NoopMemoryHub();
      break;
  }
  cached = { key, hub };
  return hub;
}

export function getMemoryHub(): MemoryHub {
  return getCombinedHub();
}

export function getOrchestrationHub(): OrchestrationHub {
  return getCombinedHub();
}

export function resetMemoryHubCache(): void {
  cached = null;
}
