import { describe, expect, it, vi, beforeEach } from "vitest";
import os from "node:os";
import path from "node:path";
import fs from "node:fs";

// Mock resolvePaperclipInstanceRoot to return a temp dir
const tmpDir = path.join(os.tmpdir(), `test-memory-${Date.now()}-${Math.random().toString(36).slice(2)}`);

vi.mock("../home-paths.js", () => ({
  resolvePaperclipInstanceRoot: () => tmpDir,
}));

// Import after mock is set up
const { assistantMemoryService } = await import("../services/assistant-memory.js");

describe("assistantMemoryService", () => {
  const service = assistantMemoryService();

  beforeEach(() => {
    // Clean up the temp dir between tests
    const memDir = path.join(tmpDir, "data", "assistant-memory");
    if (fs.existsSync(memDir)) {
      fs.rmSync(memDir, { recursive: true, force: true });
    }
  });

  it("returns default empty memory when no file exists", async () => {
    const result = await service.get("company-a");
    expect(result.facts).toEqual([]);
    expect(result.updatedAt).toBeNull();
  });

  it("appends a fact and returns updated memory", async () => {
    const result = await service.append("company-b", "I prefer TypeScript");
    expect(result.facts).toContain("I prefer TypeScript");
    expect(result.updatedAt).not.toBeNull();
  });

  it("persists facts across get calls", async () => {
    await service.append("company-c", "I prefer TypeScript");
    const result = await service.get("company-c");
    expect(result.facts).toContain("I prefer TypeScript");
  });

  it("sanitizes credential facts at write time", async () => {
    const result = await service.append("company-d", "My API key is sk-abc123def456ghijklmnopqrst");
    expect(result.facts.some((f) => f.includes("sk-abc123"))).toBe(false);
    expect(result.facts.some((f) => f.includes("[REDACTED]"))).toBe(true);
  });

  it("clears all facts", async () => {
    await service.append("company-e", "I prefer TypeScript");
    await service.clear("company-e");
    const result = await service.get("company-e");
    expect(result.facts).toEqual([]);
    expect(result.updatedAt).toBeNull();
  });

  it("enforces 50-fact FIFO cap", async () => {
    const companyId = "company-f";
    for (let i = 0; i < 51; i++) {
      await service.append(companyId, `Fact number ${i}`);
    }
    const result = await service.get(companyId);
    expect(result.facts).toHaveLength(50);
    // Oldest fact (0) should be evicted
    expect(result.facts).not.toContain("Fact number 0");
    // Newest fact (50) should be present
    expect(result.facts).toContain("Fact number 50");
  });

  it("uses separate files for different companyIds", async () => {
    await service.append("co-x", "Fact for X");
    await service.append("co-y", "Fact for Y");

    const memX = await service.get("co-x");
    const memY = await service.get("co-y");

    expect(memX.facts).toContain("Fact for X");
    expect(memX.facts).not.toContain("Fact for Y");
    expect(memY.facts).toContain("Fact for Y");
    expect(memY.facts).not.toContain("Fact for X");
  });
});
