// index.ts
import axios from "axios";

const DEFAULT_BASE_URL = "http://192.168.0.200:8420";
const DEFAULT_TIMEOUT_MS = 10000;
const DEFAULT_KNOWLEDGE_USER_ID = "knowledge_base";

interface MemoryOptions {
  text?: string;
  query?: string;
  userId?: string;
  limit?: number;
}

interface KnowledgeOptions {
  query?: string;
  userId?: string;
  limit?: number;
  sourceFile?: string;
  sourceFiles?: string[];
}

function getBaseUrl(plugin: any): string {
  return (
    plugin?.config?.baseUrl ||
    process.env.MEM0_BASE_URL ||
    DEFAULT_BASE_URL
  );
}

function getDefaultUserId(plugin: any): string {
  return (
    plugin?.runtime?.agent?.id ||
    (globalThis as any)?.openclaw?.agent?.id ||
    plugin?.config?.userId ||
    "default"
  );
}

function getKnowledgeUserId(plugin: any): string {
  return (
    plugin?.config?.knowledgeUserId ||
    process.env.MEM0_KNOWLEDGE_USER_ID ||
    DEFAULT_KNOWLEDGE_USER_ID
  );
}

function http(plugin: any) {
  return axios.create({
    baseURL: getBaseUrl(plugin),
    timeout: DEFAULT_TIMEOUT_MS,
  });
}

function normalizeSourceFile(value?: string): string | undefined {
  if (!value) return undefined;
  const trimmed = value.trim();
  return trimmed.length > 0 ? trimmed : undefined;
}

function buildSourceQuery(sourceFile: string): string {
  return sourceFile
    .replace(/\.pdf$/i, "")
    .replace(/[-_]/g, " ")
    .trim();
}

function filterBySource(results: any[], sourceFile: string) {
  return results.filter(
    (r) => r?.metadata?.source_file && r.metadata.source_file === sourceFile
  );
}

function summarizeKnowledgeResults(results: any[]) {
  const pages = results
    .map((r) => r?.metadata?.page)
    .filter((v) => typeof v === "number") as number[];
  const chapters = results
    .map((r) => r?.metadata?.chapter)
    .filter((v) => typeof v === "number") as number[];
  const created = results
    .map((r) => r?.created_at)
    .filter((v) => typeof v === "string") as string[];

  return {
    count: results.length,
    pageRange: pages.length ? [Math.min(...pages), Math.max(...pages)] : null,
    chapterRange: chapters.length
      ? [Math.min(...chapters), Math.max(...chapters)]
      : null,
    createdAtRange: created.length
      ? [created.sort()[0], created.sort()[created.length - 1]]
      : null,
  };
}

export const activate = (plugin: any) => {
  const client = http(plugin);

  // write conversational memory
  plugin.write = async ({ text, userId }: MemoryOptions) => {
    const finalUserId = userId || getDefaultUserId(plugin);
    if (!text) throw new Error("Missing text for memory write");
    const res = await client.post(`/memories`, { text, userId: finalUserId });
    return res.data;
  };

  // search conversational memory
  plugin.search = async ({ query, userId }: MemoryOptions) => {
    const finalUserId = userId || getDefaultUserId(plugin);
    if (!query) throw new Error("Missing query for memory search");
    const res = await client.post(`/memories/search`, {
      query,
      userId: finalUserId,
    });
    return res.data;
  };

  // search knowledge base
  plugin.searchKnowledge = async ({ query, userId, limit }: KnowledgeOptions) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    if (!query) throw new Error("Missing query for knowledge search");
    const res = await client.post(`/knowledge/search`, {
      query,
      userId: finalUserId,
      limit,
    });
    return res.data;
  };

  // list knowledge sources (books)
  plugin.listKnowledgeSources = async ({ userId }: KnowledgeOptions = {}) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    const res = await client.post(`/knowledge/sources`, {
      user_id: finalUserId,
    });
    return res.data;
  };

  // describe a single book by source_file (metadata summary)
  plugin.describeKnowledgeBook = async ({ sourceFile, userId }: KnowledgeOptions) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    const normalized = normalizeSourceFile(sourceFile);
    if (!normalized) throw new Error("Missing sourceFile for knowledge describe");

    const res = await client.post(`/knowledge/search`, {
      query: buildSourceQuery(normalized),
      userId: finalUserId,
      limit: 200,
    });

    const filtered = filterBySource(res.data?.results || [], normalized);
    const summary = summarizeKnowledgeResults(filtered);

    return {
      sourceFile: normalized,
      summary,
      sample: filtered.slice(0, 5),
      hintQuery: buildSourceQuery(normalized),
    };
  };

  // search within a single book (client-side filtered)
  plugin.searchKnowledgeBook = async ({ query, sourceFile, userId, limit = 8 }: KnowledgeOptions) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    const normalized = normalizeSourceFile(sourceFile);
    if (!query) throw new Error("Missing query for knowledge search");
    if (!normalized) throw new Error("Missing sourceFile for book search");

    const res = await client.post(`/knowledge/search`, {
      query,
      userId: finalUserId,
      limit: Math.max(limit, 20),
    });

    const filtered = filterBySource(res.data?.results || [], normalized);
    return {
      sourceFile: normalized,
      results: filtered.slice(0, limit),
    };
  };

  // search across a set of books
  plugin.searchKnowledgeBooks = async ({ query, sourceFiles, userId, limit = 8 }: KnowledgeOptions) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    if (!query) throw new Error("Missing query for knowledge search");
    if (!Array.isArray(sourceFiles) || sourceFiles.length === 0) {
      throw new Error("Missing sourceFiles for multi-book search");
    }

    const res = await client.post(`/knowledge/search`, {
      query,
      userId: finalUserId,
      limit: Math.max(limit * 3, 20),
    });

    const normalizedSources = sourceFiles
      .map((s) => normalizeSourceFile(s))
      .filter(Boolean) as string[];

    const filtered = (res.data?.results || []).filter((r: any) =>
      normalizedSources.includes(r?.metadata?.source_file)
    );

    return {
      sourceFiles: normalizedSources,
      results: filtered.slice(0, limit),
    };
  };

  // write to knowledge base
  plugin.writeKnowledge = async ({ text, userId }: MemoryOptions) => {
    const finalUserId = userId || getKnowledgeUserId(plugin);
    if (!text) throw new Error("Missing text for knowledge write");
    const res = await client.post(`/knowledge`, { text, userId: finalUserId });
    return res.data;
  };

  // read/recall recent memories (requires /memories/recent on server)
  plugin.read = async ({ userId, limit = 5 }: MemoryOptions) => {
    const finalUserId = userId || getDefaultUserId(plugin);
    const res = await client.post(`/memories/recent`, {
      userId: finalUserId,
      limit,
    });
    return res.data.results || [];
  };
};