import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { parse } from "yaml";
import { envString } from "../env.js";

export type RagChunkStrategy = "heading" | "paragraph" | "fixed";

export type RagConfig = {
  endpoint: string;
  authToken: string;
  timeoutMs: number;
  fallbackToLocal: boolean;
  chunkStrategy: RagChunkStrategy;
  chunkSizeChars: number;
  topK: number;
  minRelevance: number;
  includeTags: string[];
  excludeTags: string[];
};

export type PublicRagConfig = Omit<RagConfig, "authToken"> & {
  // Never expose the auth token over the public API; show only whether
  // one is configured.
  hasAuthToken: boolean;
};

const defaultConfig = (): RagConfig => ({
  endpoint: "",
  authToken: "",
  timeoutMs: 10_000,
  fallbackToLocal: true,
  chunkStrategy: "heading",
  chunkSizeChars: 1500,
  topK: 5,
  minRelevance: 0,
  includeTags: [],
  excludeTags: [],
});

const expandEnv = (value: string): string =>
  value.replace(/\$\{([A-Z0-9_]+):?\}/g, (_m, name: string) => process.env[name] ?? "");

const normalize = (raw: unknown): RagConfig => {
  if (!raw || typeof raw !== "object") return defaultConfig();
  const r = raw as Partial<RagConfig>;
  const chunkStrategy: RagChunkStrategy =
    r.chunkStrategy === "paragraph" || r.chunkStrategy === "fixed" ? r.chunkStrategy : "heading";
  return {
    endpoint: expandEnv(String(r.endpoint ?? "").trim()),
    authToken: expandEnv(String(r.authToken ?? "").trim()),
    timeoutMs: Math.max(100, Number(r.timeoutMs ?? 10_000)),
    fallbackToLocal: r.fallbackToLocal !== false,
    chunkStrategy,
    chunkSizeChars: Math.max(200, Number(r.chunkSizeChars ?? 1500)),
    topK: Math.max(1, Number(r.topK ?? 5)),
    minRelevance: Math.max(0, Math.min(1, Number(r.minRelevance ?? 0))),
    includeTags: Array.isArray(r.includeTags) ? r.includeTags.map(String) : [],
    excludeTags: Array.isArray(r.excludeTags) ? r.excludeTags.map(String) : [],
  };
};

const defaultPath = (): string =>
  envString(process.env.RAG_CONFIG_PATH, resolve(process.cwd(), "../../config/rag.yml"));

export const loadRagConfig = (configPath: string = defaultPath()): RagConfig => {
  try {
    const raw = readFileSync(configPath, "utf8");
    const parsed = parse(raw) as { rag?: unknown } | null;
    return normalize(parsed?.rag);
  } catch (error) {
    const code = (error as NodeJS.ErrnoException).code;
    if (code === "ENOENT") return defaultConfig();
    throw error;
  }
};

export const toPublicRagConfig = (config: RagConfig): PublicRagConfig => {
  const { authToken: _auth, ...rest } = config;
  return { ...rest, hasAuthToken: Boolean(config.authToken) };
};
