import type { Config } from '../config/index.js';
import type { Lifecycle } from './lifecycle.js';
import { MemoryStore } from '../memory/index.js';
import { VectorStore, HybridSearch, createEmbeddingProvider, chunkText, contentHash } from '../memory/index.js';
import type { EmbeddingProvider as EmbeddingProviderInterface } from '../memory/index.js';
import { createMemoryTools } from '../tools/builtin/index.js';
import type { ToolRegistry } from '../tools/index.js';
import { resolve } from 'path';
import { mkdirSync } from 'fs';

export interface MemoryDeps {
  config: Config;
  dataDir: string;
  lifecycle: Lifecycle;
  toolRegistry: ToolRegistry;
}

export interface MemoryResult {
  memoryStore?: MemoryStore;
  hybridSearch?: HybridSearch;
  memoryDir: string;
}

export async function initMemory(deps: MemoryDeps): Promise<MemoryResult> {
  const { config, dataDir, lifecycle, toolRegistry } = deps;

  // Initialize memory store
  const memoryDir = config.memory.dir ?? resolve(dataDir, 'memory');
  mkdirSync(memoryDir, { recursive: true });
  const memoryStore = config.memory.enabled
    ? new MemoryStore({ dir: memoryDir, maxContextTokens: config.memory.max_context_tokens })
    : undefined;

  // Register memory tools if memory is enabled
  let hybridSearch: HybridSearch | undefined;

  if (memoryStore && config.memory.embedding.enabled) {
    try {
      const embeddingProvider: EmbeddingProviderInterface = createEmbeddingProvider(config.memory.embedding);
      const vectorStore = new VectorStore(resolve(dataDir, 'vectors.db'));
      hybridSearch = new HybridSearch(
        memoryStore,
        vectorStore,
        embeddingProvider,
        config.memory.embedding.hybrid_weight,
      );

      // Background indexer: re-embed dirty namespaces every 30 seconds
      const indexerInterval = setInterval(async () => {
        const dirtyNamespaces = memoryStore.getDirtyNamespaces();
        for (const ns of dirtyNamespaces) {
          try {
            const content = memoryStore.read(ns);
            if (content.length === 0) {
              vectorStore.deleteNamespace(ns);
              continue;
            }

            const hash = contentHash(content);
            if (vectorStore.hasContentHash(ns, hash)) {continue;}

            const chunks = chunkText(content, ns, {
              chunkSize: config.memory.embedding.chunk_size,
              chunkOverlap: config.memory.embedding.chunk_overlap,
            });

            if (chunks.length > 0) {
              const embeddings = await embeddingProvider.embed(chunks.map((c) => c.text));
              vectorStore.upsertChunks(chunks, embeddings, hash);
            }
          } catch (err) {
            console.error(`Failed to index namespace "${ns}":`, err);
          }
        }
      }, 30_000);

      // Initial full index — mark all existing namespaces as dirty
      memoryStore.markAllDirty();

      lifecycle.onShutdown(async () => {
        clearInterval(indexerInterval);
        vectorStore.close();
        console.log('Vector store closed');
      });

      console.log(`Vector memory search enabled (provider=${config.memory.embedding.provider}, model=${config.memory.embedding.model})`);
    } catch (err) {
      console.error('Failed to initialize vector search:', err);
    }
  }

  if (memoryStore) {
    for (const tool of createMemoryTools(memoryStore, hybridSearch)) {
      toolRegistry.register(tool);
    }
  }

  return { memoryStore, hybridSearch, memoryDir };
}
