import { describe, expect, it, vi, beforeEach } from "vitest";

vi.mock("fs/promises", () => ({
  readFile: vi.fn(),
}));

import { readFile } from "fs/promises";

const mockedReadFile = vi.mocked(readFile);

const MOCK_INVESTIGATION = `## The Five Phases
Overview of methodology

## 1. Reading
Read the ticket carefully`;

const MOCK_ALERTS = `## Cluster Availability & Health

### Cluster Shutdown & Failed Restart
- Symptoms: API pods offline
- Fix: Restart VMs

### Missing Master Node / Disturbed Indexing
- Symptoms: master-2 missing
- Fix: Redeploy machine

## Networking & Security

### DNS Issues
- Symptoms: DNS resolution failing
- Fix: Check CoreDNS`;

const MOCK_COMMUNITY = `## Networking

### Route Returns 503 After Applying NetworkPolicy
- Symptoms: route returns 503
- Root Cause: deny-by-default NetworkPolicy

## Node Health

### Node NotReady -- Kubelet or CRI-O Failure
- Symptoms: node NotReady
- Root Cause: kubelet crash`;

const MOCK_AKS_PLATFORM = `## AKS Investigation Notes

### AKS managed cluster boundaries
- Focus on AKS nodepool evidence and managed cluster symptoms.

### kubectl guidance
- Use kubectl against workloads and nodes first.`;

const MOCK_CLASSIC_PLATFORM = `## ARO Classic Investigation Notes

### Machine API guidance
- Use oc against machines and cluster operators.`;

const MOCK_HCP_PLATFORM = `## ARO HCP Investigation Notes

### Guest cluster boundary
- Use oc in the guest cluster and escalate management-plane evidence.`;

function setupMocks() {
  mockedReadFile.mockImplementation(async (filePath) => {
    const p = String(filePath).replace(/\\/g, "/");
    if (p.includes("sre-investigation-techniques")) return MOCK_INVESTIGATION;
    if (p.includes("Openshift-clusters-alerts-resolutions")) return MOCK_ALERTS;
    if (p.includes("Community-reported-issues")) return MOCK_COMMUNITY;
    if (p.includes("platforms/aks/platform-operations.md")) return MOCK_AKS_PLATFORM;
    if (p.includes("platforms/aro-classic/platform-operations.md")) {
      return MOCK_CLASSIC_PLATFORM;
    }
    if (p.includes("platforms/aro-hcp/platform-operations.md")) {
      return MOCK_HCP_PLATFORM;
    }
    throw new Error("Unknown file");
  });
}

describe("loadKnowledgeBase", () => {
  beforeEach(() => {
    vi.resetModules();
    mockedReadFile.mockReset();
  });

  it("loads only shared and AKS knowledge for AKS sessions", async () => {
    setupMocks();

    const { loadKnowledgeBase: freshLoad } = await import("./knowledge");
    const result = await freshLoad("aks");

    expect(result).toContain("sre investigation techniques");
    expect(result).toContain("The Five Phases");
    expect(result).toContain("AKS Investigation Notes");
    expect(result).not.toContain("Cluster Availability");
    expect(result).not.toContain("Route Returns 503");
    expect(result).not.toContain("ARO Classic Investigation Notes");
  });

  it("loads classic issue files only for ARO Classic sessions", async () => {
    setupMocks();

    const { loadKnowledgeBase: freshLoad } = await import("./knowledge");
    const result = await freshLoad("aro-classic");

    expect(result).toContain("Cluster Availability");
    expect(result).toContain("Route Returns 503");
    expect(result).toContain("ARO Classic Investigation Notes");
    expect(result).not.toContain("AKS Investigation Notes");
  });

  it("does not load classic issue files for ARO HCP sessions", async () => {
    setupMocks();

    const { loadKnowledgeBase: freshLoad } = await import("./knowledge");
    const result = await freshLoad("aro-hcp");

    expect(result).toContain("ARO HCP Investigation Notes");
    expect(result).not.toContain("Cluster Availability");
    expect(result).not.toContain("Route Returns 503");
  });

  it("handles missing files gracefully", async () => {
    mockedReadFile.mockRejectedValue(new Error("ENOENT"));

    const { loadKnowledgeBase: freshLoad } = await import("./knowledge");
    const result = await freshLoad();

    expect(result).toBe("");
  });
});

describe("loadKnowledgeSections", () => {
  beforeEach(() => {
    vi.resetModules();
    mockedReadFile.mockReset();
  });

  it("parses KB files into sections with titles and keywords", async () => {
    setupMocks();

    const { loadKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");

    expect(sections.length).toBeGreaterThan(0);

    const titles = sections.map((s) => s.title);
    expect(titles).toContain("The Five Phases");
    expect(titles).toContain("AKS managed cluster boundaries");
    expect(titles).not.toContain("Cluster Shutdown & Failed Restart");
    expect(titles).not.toContain("Route Returns 503 After Applying NetworkPolicy");
  });

  it("caches sections after first load", async () => {
    setupMocks();

    const { loadKnowledgeSections } = await import("./knowledge");
    const first = await loadKnowledgeSections("aks");
    const second = await loadKnowledgeSections("aks");

    expect(first).toBe(second);
  });
});

describe("queryKnowledgeSections", () => {
  beforeEach(() => {
    vi.resetModules();
    mockedReadFile.mockReset();
  });

  it("always includes investigation methodology sections", async () => {
    setupMocks();

    const { loadKnowledgeSections, queryKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");
    const result = queryKnowledgeSections(sections, ["unrelated query"], 8000);

    expect(result).toContain("The Five Phases");
    expect(result).toContain("Reading");
  });

  it("selects sections matching scenario keywords", async () => {
    setupMocks();

    const { loadKnowledgeSections, queryKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");
    const result = queryKnowledgeSections(
      sections,
      ["nodepool managed cluster"],
      8000,
    );

    expect(result).toContain("AKS managed cluster boundaries");
    expect(result).not.toContain("Route Returns 503");
  });

  it("combines shared and platform-specific knowledge files for aks sessions", async () => {
    setupMocks();

    const { loadKnowledgeSections, queryKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");
    const result = queryKnowledgeSections(sections, ["nodepool", "managed cluster"], 8000);

    expect(result).toContain("The Five Phases");
    expect(result).toContain("AKS");
  });

  it("respects maxChars limit", async () => {
    setupMocks();

    const { loadKnowledgeSections, queryKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");
    const result = queryKnowledgeSections(sections, ["everything"], 500);

    expect(result.length).toBeLessThanOrEqual(500);
  });

  it("truncates investigation text when it alone exceeds maxChars", async () => {
    setupMocks();

    const { loadKnowledgeSections, queryKnowledgeSections } = await import("./knowledge");
    const sections = await loadKnowledgeSections("aks");
    const result = queryKnowledgeSections(sections, ["NetworkPolicy 503"], 30);

    expect(result.length).toBeLessThanOrEqual(30);
  });
});
