/**
 * Unit tests for multi-collection filter logic (PR #191).
 *
 * Tests the filterByCollections post-filter and the resolveCollectionFilter
 * behavior for single-collection vs multi-collection search.
 */

import { describe, test, expect } from "vitest";
import { parseArgs } from "node:util";

// Reproduce the filterByCollections logic from qmd.ts for testing
// (the function is private in qmd.ts)
function filterByCollections<T extends { filepath?: string; file?: string }>(
  results: T[],
  collectionNames: string[],
): T[] {
  if (collectionNames.length <= 1) return results;
  const prefixes = collectionNames.map((n) => `qmd://${n}/`);
  return results.filter((r) => {
    const path = r.filepath || r.file || "";
    return prefixes.some((p) => path.startsWith(p));
  });
}

describe("filterByCollections", () => {
  const results = [
    { filepath: "qmd://docs/readme.md", file: "qmd://docs/readme.md" },
    { filepath: "qmd://notes/todo.md", file: "qmd://notes/todo.md" },
    { filepath: "qmd://journals/2024/jan.md", file: "qmd://journals/2024/jan.md" },
    { filepath: "qmd://docs/api.md", file: "qmd://docs/api.md" },
  ];

  test("returns all results when no collections specified", () => {
    expect(filterByCollections(results, [])).toEqual(results);
  });

  test("returns all results for single collection (no-op, handled by SQL filter)", () => {
    expect(filterByCollections(results, ["docs"])).toEqual(results);
  });

  test("filters to matching collections when multiple specified", () => {
    const filtered = filterByCollections(results, ["docs", "journals"]);
    expect(filtered).toHaveLength(3);
    expect(filtered.map((r) => r.filepath)).toEqual([
      "qmd://docs/readme.md",
      "qmd://journals/2024/jan.md",
      "qmd://docs/api.md",
    ]);
  });

  test("filters correctly with two collections", () => {
    const filtered = filterByCollections(results, ["notes", "journals"]);
    expect(filtered).toHaveLength(2);
    expect(filtered.map((r) => r.filepath)).toEqual([
      "qmd://notes/todo.md",
      "qmd://journals/2024/jan.md",
    ]);
  });

  test("returns empty when no results match collections", () => {
    const filtered = filterByCollections(results, ["archive", "trash"]);
    expect(filtered).toHaveLength(0);
  });

  test("uses file field when filepath is missing", () => {
    const fileOnlyResults = [
      { file: "qmd://docs/readme.md" },
      { file: "qmd://notes/todo.md" },
    ];
    const filtered = filterByCollections(fileOnlyResults, ["docs", "notes"]);
    expect(filtered).toHaveLength(2);
  });

  test("uses filepath over file when both present", () => {
    const mixedResults = [
      { filepath: "qmd://docs/readme.md", file: "qmd://notes/todo.md" },
    ];
    const filtered = filterByCollections(mixedResults, ["docs", "notes"]);
    expect(filtered).toHaveLength(1);
    // Should match via filepath (docs), not file (notes)
    expect(filtered[0].filepath).toBe("qmd://docs/readme.md");
  });
});

describe("resolveCollectionFilter input normalization", () => {
  // Test the array normalization logic without the DB dependency
  function normalizeCollectionInput(raw: string | string[] | undefined): string[] {
    if (!raw) return [];
    return Array.isArray(raw) ? raw : [raw];
  }

  test("undefined returns empty array", () => {
    expect(normalizeCollectionInput(undefined)).toEqual([]);
  });

  test("single string returns single-element array", () => {
    expect(normalizeCollectionInput("docs")).toEqual(["docs"]);
  });

  test("array passes through", () => {
    expect(normalizeCollectionInput(["docs", "notes"])).toEqual(["docs", "notes"]);
  });

  test("empty string returns single-element array", () => {
    expect(normalizeCollectionInput("")).toEqual([]);
  });
});

describe("collection option type from parseArgs", () => {
  // Verify that parseArgs with `multiple: true` produces string[]
  test("parseArgs multiple:true produces array for repeated flags", () => {
    const { values } = parseArgs({
      args: ["-c", "docs", "-c", "notes"],
      options: {
        collection: { type: "string", short: "c", multiple: true },
      },
      strict: true,
    });
    expect(values.collection).toEqual(["docs", "notes"]);
  });

  test("parseArgs multiple:true produces array for single flag", () => {
    const { values } = parseArgs({
      args: ["-c", "docs"],
      options: {
        collection: { type: "string", short: "c", multiple: true },
      },
      strict: true,
    });
    expect(values.collection).toEqual(["docs"]);
  });

  test("parseArgs multiple:true produces undefined when flag absent", () => {
    const { values } = parseArgs({
      args: [],
      options: {
        collection: { type: "string", short: "c", multiple: true },
      },
      strict: true,
    });
    expect(values.collection).toBeUndefined();
  });
});