import { existsSync, mkdtempSync, mkdirSync, writeFileSync, rmSync, realpathSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { afterEach, describe, expect, test } from "vitest";
import { findLocalConfigPath, getLocalDbPath } from "../src/collections.js";

function cliCommandArgs(command: string): { bin: string; args: string[] } {
  const cliPath = join(process.cwd(), "src/cli/qmd.ts");
  if (process.versions.bun) {
    return { bin: process.execPath, args: [cliPath, command] };
  }
  return {
    bin: process.execPath,
    args: [join(process.cwd(), "node_modules/tsx/dist/cli.mjs"), cliPath, command],
  };
}

const roots: string[] = [];

function tempProject(): string {
  const root = mkdtempSync(join(tmpdir(), "qmd-local-config-"));
  roots.push(root);
  return root;
}

afterEach(() => {
  for (const root of roots.splice(0)) {
    rmSync(root, { recursive: true, force: true });
  }
});

describe("local .qmd project config", () => {
  test("finds .qmd/index.yaml from nested working directories", () => {
    const root = tempProject();
    const configPath = join(root, ".qmd", "index.yaml");
    mkdirSync(join(root, ".qmd"), { recursive: true });
    writeFileSync(configPath, "collections: {}\n");
    const nested = join(root, "wiki", "Shopify");
    mkdirSync(nested, { recursive: true });

    expect(findLocalConfigPath(nested)).toBe(configPath);
  });

  test("prefers index.yaml over index.yml when both exist", () => {
    const root = tempProject();
    mkdirSync(join(root, ".qmd"), { recursive: true });
    const yaml = join(root, ".qmd", "index.yaml");
    const yml = join(root, ".qmd", "index.yml");
    writeFileSync(yaml, "collections: {}\n");
    writeFileSync(yml, "collections: {}\n");

    expect(findLocalConfigPath(root)).toBe(yaml);
  });

  test("uses .qmd/index.sqlite next to the local config", () => {
    const root = tempProject();
    mkdirSync(join(root, ".qmd"), { recursive: true });
    const configPath = join(root, ".qmd", "index.yaml");
    writeFileSync(configPath, "collections: {}\n");

    expect(getLocalDbPath(configPath)).toBe(join(root, ".qmd", "index.sqlite"));
  });

  test("CLI uses local .qmd config and index instead of global cache", () => {
    const root = tempProject();
    mkdirSync(join(root, ".qmd"), { recursive: true });
    mkdirSync(join(root, "docs"), { recursive: true });
    writeFileSync(join(root, "docs", "a.md"), "# A\n\nLocal test document.\n");
    writeFileSync(join(root, ".qmd", "index.yaml"), `collections:\n  docs:\n    path: ${JSON.stringify(join(root, "docs"))}\n    pattern: "**/*.md"\n    context:\n      /: Local test docs\nmodels:\n  embed: local-embed-model\n  rerank: local-rerank-model\n  generate: local-generate-model\n`);

    const home = join(root, "home");
    const { bin, args } = cliCommandArgs("status");
    const output = execFileSync(bin, args, {
      cwd: root,
      encoding: "utf-8",
      env: {
        ...process.env,
        HOME: home,
        XDG_CONFIG_HOME: join(home, ".config"),
        XDG_CACHE_HOME: join(home, ".cache"),
        QMD_EMBED_MODEL: "env-embed-model",
        QMD_RERANK_MODEL: "env-rerank-model",
        QMD_GENERATE_MODEL: "env-generate-model",
      },
    });

    const localIndex = join(root, ".qmd", "index.sqlite");
    expect(output).toContain(`Index: ${realpathSync(localIndex)}`);
    expect(output).toContain("docs (qmd://docs/)");
    expect(output).toContain("Embedding:   local-embed-model");
    expect(output).toContain("Reranking:   local-rerank-model");
    expect(output).toContain("Generation:  local-generate-model");
    expect(output).not.toContain("env-embed-model");
    expect(existsSync(localIndex)).toBe(true);
    expect(existsSync(join(home, ".cache", "qmd", "index.sqlite"))).toBe(false);
  });
});