import { beforeEach, describe, expect, it, vi } from "vitest";
import {
  applyMemoryWikiMutation,
  normalizeMemoryWikiMutationInput,
  type ApplyMemoryWikiMutation,
} from "./apply.js";
import { registerMemoryWikiGatewayMethods } from "./gateway.js";
import { listMemoryWikiImportInsights } from "./import-insights.js";
import { listMemoryWikiImportRuns } from "./import-runs.js";
import { ingestMemoryWikiSource } from "./ingest.js";
import { listMemoryWikiPalace } from "./memory-palace.js";
import { searchMemoryWiki } from "./query.js";
import { syncMemoryWikiImportedSources } from "./source-sync.js";
import { resolveMemoryWikiStatus } from "./status.js";
import { createMemoryWikiTestHarness } from "./test-helpers.js";

vi.mock("./apply.js", () => ({
  applyMemoryWikiMutation: vi.fn(),
  normalizeMemoryWikiMutationInput: vi.fn(),
}));

vi.mock("./compile.js", () => ({
  compileMemoryWikiVault: vi.fn(),
}));

vi.mock("./ingest.js", () => ({
  ingestMemoryWikiSource: vi.fn(),
}));

vi.mock("./import-insights.js", () => ({
  listMemoryWikiImportInsights: vi.fn(),
}));

vi.mock("./import-runs.js", () => ({
  listMemoryWikiImportRuns: vi.fn(),
}));

vi.mock("./lint.js", () => ({
  lintMemoryWikiVault: vi.fn(),
}));

vi.mock("./memory-palace.js", () => ({
  listMemoryWikiPalace: vi.fn(),
}));

vi.mock("./obsidian.js", () => ({
  probeObsidianCli: vi.fn(),
  runObsidianCommand: vi.fn(),
  runObsidianDaily: vi.fn(),
  runObsidianOpen: vi.fn(),
  runObsidianSearch: vi.fn(),
}));

vi.mock("./query.js", () => ({
  getMemoryWikiPage: vi.fn(),
  searchMemoryWiki: vi.fn(),
}));

vi.mock("./source-sync.js", () => ({
  syncMemoryWikiImportedSources: vi.fn(),
}));

vi.mock("./status.js", () => ({
  buildMemoryWikiDoctorReport: vi.fn(),
  resolveMemoryWikiStatus: vi.fn(),
}));

vi.mock("./vault.js", () => ({
  initializeMemoryWikiVault: vi.fn(),
}));

const { createPluginApi, createVault } = createMemoryWikiTestHarness();

function findGatewayHandler(
  registerGatewayMethod: ReturnType<typeof vi.fn>,
  method: string,
):
  | ((ctx: {
      params: Record<string, unknown>;
      respond: (ok: boolean, payload?: unknown, error?: unknown) => void;
    }) => Promise<void>)
  | undefined {
  return registerGatewayMethod.mock.calls.find((call) => call[0] === method)?.[1];
}

describe("memory-wiki gateway methods", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(syncMemoryWikiImportedSources).mockResolvedValue({
      importedCount: 0,
      updatedCount: 0,
      skippedCount: 0,
      removedCount: 0,
      artifactCount: 0,
      workspaces: 0,
      pagePaths: [],
      indexesRefreshed: false,
      indexUpdatedFiles: [],
      indexRefreshReason: "no-import-changes",
    });
    vi.mocked(resolveMemoryWikiStatus).mockResolvedValue({
      vaultMode: "isolated",
      vaultExists: true,
    } as never);
    vi.mocked(ingestMemoryWikiSource).mockResolvedValue({
      pagePath: "sources/alpha-notes.md",
    } as never);
    vi.mocked(listMemoryWikiImportRuns).mockResolvedValue({
      runs: [],
      totalRuns: 0,
      activeRuns: 0,
      rolledBackRuns: 0,
    } as never);
    vi.mocked(listMemoryWikiImportInsights).mockResolvedValue({
      sourceType: "chatgpt",
      totalItems: 0,
      totalClusters: 0,
      clusters: [],
    } as never);
    vi.mocked(listMemoryWikiPalace).mockResolvedValue({
      totalItems: 0,
      totalClaims: 0,
      totalQuestions: 0,
      totalContradictions: 0,
      clusters: [],
    } as never);
    vi.mocked(normalizeMemoryWikiMutationInput).mockReturnValue({
      op: "create_synthesis",
      title: "Gateway Alpha",
      body: "Gateway summary.",
      sourceIds: ["source.alpha"],
    } satisfies ApplyMemoryWikiMutation);
    vi.mocked(applyMemoryWikiMutation).mockResolvedValue({
      operation: "create_synthesis",
      pagePath: "syntheses/gateway-alpha.md",
    } as never);
    vi.mocked(searchMemoryWiki).mockResolvedValue({
      items: [],
      total: 0,
    } as never);
  });

  it("returns wiki status over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.status");
    if (!handler) {
      throw new Error("wiki.status handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {},
      respond,
    });

    expect(syncMemoryWikiImportedSources).toHaveBeenCalledWith({ config, appConfig: undefined });
    expect(resolveMemoryWikiStatus).toHaveBeenCalledWith(config, {
      appConfig: undefined,
    });
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        vaultMode: "isolated",
        vaultExists: true,
      }),
    );
  });

  it("returns recent import runs over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();
    vi.mocked(listMemoryWikiImportRuns).mockResolvedValue({
      runs: [
        {
          runId: "chatgpt-abc123",
          importType: "chatgpt",
          appliedAt: "2026-04-10T10:00:00.000Z",
          exportPath: "/tmp/chatgpt",
          sourcePath: "/tmp/chatgpt/conversations.json",
          conversationCount: 12,
          createdCount: 4,
          updatedCount: 2,
          skippedCount: 6,
          status: "applied",
          pagePaths: ["sources/chatgpt-2026-04-10-alpha.md"],
          samplePaths: ["sources/chatgpt-2026-04-10-alpha.md"],
        },
      ],
      totalRuns: 1,
      activeRuns: 1,
      rolledBackRuns: 0,
    } as never);

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.importRuns");
    if (!handler) {
      throw new Error("wiki.importRuns handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {
        limit: 5,
      },
      respond,
    });

    expect(listMemoryWikiImportRuns).toHaveBeenCalledWith(config, { limit: 5 });
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        totalRuns: 1,
        activeRuns: 1,
      }),
    );
  });

  it("returns import insights over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();
    vi.mocked(listMemoryWikiImportInsights).mockResolvedValue({
      sourceType: "chatgpt",
      totalItems: 2,
      totalClusters: 1,
      clusters: [
        {
          key: "topic/travel",
          label: "Travel",
          itemCount: 2,
          highRiskCount: 1,
          withheldCount: 1,
          preferenceSignalCount: 0,
          updatedAt: "2026-04-10T10:00:00.000Z",
          items: [
            {
              pagePath: "sources/chatgpt-2026-04-10-alpha.md",
              title: "BA flight receipts process",
              riskLevel: "low",
              labels: ["domain/personal", "area/travel", "topic/travel"],
              topicKey: "topic/travel",
              topicLabel: "Travel",
              digestStatus: "available",
              firstUserLine: "how do i get receipts?",
              lastUserLine: "that option does not exist",
              preferenceSignals: [],
            },
          ],
        },
      ],
    } as never);

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.importInsights");
    if (!handler) {
      throw new Error("wiki.importInsights handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {},
      respond,
    });

    expect(syncMemoryWikiImportedSources).toHaveBeenCalledWith({ config, appConfig: undefined });
    expect(listMemoryWikiImportInsights).toHaveBeenCalledWith(config);
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        sourceType: "chatgpt",
        totalItems: 2,
        totalClusters: 1,
      }),
    );
  });

  it("returns memory palace overview over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();
    vi.mocked(listMemoryWikiPalace).mockResolvedValue({
      totalItems: 3,
      totalClaims: 4,
      totalQuestions: 1,
      totalContradictions: 1,
      clusters: [
        {
          key: "synthesis",
          label: "Syntheses",
          itemCount: 1,
          claimCount: 2,
          questionCount: 1,
          contradictionCount: 0,
          items: [
            {
              pagePath: "syntheses/travel-system.md",
              title: "Travel system",
              kind: "synthesis",
              claimCount: 2,
              questionCount: 1,
              contradictionCount: 0,
              claims: ["prefers direct receipts"],
              questions: ["should this become a playbook?"],
              contradictions: [],
            },
          ],
        },
      ],
    } as never);

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.palace");
    if (!handler) {
      throw new Error("wiki.palace handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {},
      respond,
    });

    expect(syncMemoryWikiImportedSources).toHaveBeenCalledWith({ config, appConfig: undefined });
    expect(listMemoryWikiPalace).toHaveBeenCalledWith(config);
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        totalItems: 3,
        totalClaims: 4,
      }),
    );
  });

  it("validates required query params for wiki.search", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.search");
    if (!handler) {
      throw new Error("wiki.search handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {},
      respond,
    });

    expect(searchMemoryWiki).not.toHaveBeenCalled();
    expect(respond).toHaveBeenCalledWith(
      false,
      undefined,
      expect.objectContaining({ message: "query is required." }),
    );
  });

  it("forwards ingest requests over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.ingest");
    if (!handler) {
      throw new Error("wiki.ingest handler missing");
    }
    const respond = vi.fn();

    await handler({
      params: {
        inputPath: "/tmp/alpha-notes.txt",
        title: "Alpha",
      },
      respond,
    });

    expect(ingestMemoryWikiSource).toHaveBeenCalledWith({
      config,
      inputPath: "/tmp/alpha-notes.txt",
      title: "Alpha",
    });
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        pagePath: "sources/alpha-notes.md",
      }),
    );
  });

  it("applies wiki mutations over the gateway", async () => {
    const { config } = await createVault({ prefix: "memory-wiki-gateway-" });
    const { api, registerGatewayMethod } = createPluginApi();

    registerMemoryWikiGatewayMethods({ api, config });
    const handler = findGatewayHandler(registerGatewayMethod, "wiki.apply");
    if (!handler) {
      throw new Error("wiki.apply handler missing");
    }
    const respond = vi.fn();
    const params = {
      op: "create_synthesis",
      title: "Gateway Alpha",
      body: "Gateway summary.",
      sourceIds: ["source.alpha"],
    };

    await handler({
      params,
      respond,
    });

    expect(normalizeMemoryWikiMutationInput).toHaveBeenCalledWith(params);
    expect(applyMemoryWikiMutation).toHaveBeenCalledWith({
      config,
      mutation: expect.objectContaining({
        op: "create_synthesis",
        title: "Gateway Alpha",
      }),
    });
    expect(respond).toHaveBeenCalledWith(
      true,
      expect.objectContaining({
        operation: "create_synthesis",
        pagePath: "syntheses/gateway-alpha.md",
      }),
    );
  });
});
