import type { KnowledgeDoc, KnowledgeSearchResult } from "../docs/repository.js";
import type { RagConfig } from "./config.js";

export type RagSearchResponse = {
  items: Array<{
    id: string;
    title?: string;
    source?: string;
    tags?: string[];
    relevance?: number;
    excerpt?: string;
    content?: string;
  }>;
};

export type RagGetResponse = Partial<KnowledgeDoc> & { id: string };

const ensureTrailing = (url: string) => url.replace(/\/$/, "");

export const isRagRemote = (config: RagConfig): boolean => config.endpoint.trim().length > 0;

const buildHeaders = (config: RagConfig): Record<string, string> => {
  const headers: Record<string, string> = {
    "content-type": "application/json",
    accept: "application/json",
  };
  if (config.authToken) {
    headers.authorization = `Bearer ${config.authToken}`;
  }
  return headers;
};

export const searchViaRag = async (
  config: RagConfig,
  query: string,
  limit: number,
): Promise<KnowledgeSearchResult[]> => {
  const url = `${ensureTrailing(config.endpoint)}/search`;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: buildHeaders(config),
      body: JSON.stringify({
        query,
        limit,
        min_relevance: config.minRelevance,
        include_tags: config.includeTags,
        exclude_tags: config.excludeTags,
      }),
      signal: controller.signal,
    });
    if (!response.ok) {
      throw new Error(`rag_search_failed:${response.status}`);
    }
    const data = (await response.json()) as RagSearchResponse;
    if (!data || !Array.isArray(data.items)) return [];
    return data.items.map((item) => ({
      id: String(item.id),
      title: String(item.title ?? item.id),
      source: String(item.source ?? ""),
      tags: Array.isArray(item.tags) ? item.tags.map(String) : [],
      relevance: Number(item.relevance ?? 0),
      excerpt: String(item.excerpt ?? ""),
      headings: [],
    }));
  } finally {
    clearTimeout(timeout);
  }
};

export const getViaRag = async (
  config: RagConfig,
  id: string,
): Promise<KnowledgeDoc | undefined> => {
  const url = `${ensureTrailing(config.endpoint)}/docs/${encodeURIComponent(id)}`;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
  try {
    const response = await fetch(url, {
      method: "GET",
      headers: buildHeaders(config),
      signal: controller.signal,
    });
    if (response.status === 404) return undefined;
    if (!response.ok) {
      throw new Error(`rag_get_failed:${response.status}`);
    }
    const data = (await response.json()) as RagGetResponse;
    if (!data || !data.id) return undefined;
    return {
      id: String(data.id),
      title: String(data.title ?? data.id),
      source: String(data.source ?? ""),
      tags: Array.isArray(data.tags) ? data.tags.map(String) : [],
      owner: typeof data.owner === "string" ? data.owner : undefined,
      updated: typeof data.updated === "string" ? data.updated : undefined,
      headings: Array.isArray(data.headings) ? data.headings.map(String) : [],
      content: String(data.content ?? ""),
    };
  } finally {
    clearTimeout(timeout);
  }
};
