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

vi.mock("../lib/audit.js", () => ({
  createAuditEntry: vi.fn().mockResolvedValue(undefined),
}));

vi.mock("../router/assistant-approvals.js", () => ({
  clearPendingAssistantApproval: vi.fn().mockResolvedValue(undefined),
  consumePendingAssistantApproval: vi.fn(),
  toApprovalPayload: vi.fn((approval: { id: string; toolName: string; summary: string }, status: string) => ({
    id: approval.id,
    toolName: approval.toolName,
    summary: approval.summary,
    status,
  })),
}));

vi.mock("../router/assistant-confirmation.js", () => ({
  canExecuteMutationTool: vi.fn(),
  isCancellationReply: vi.fn(),
  parseToolArguments: vi.fn(() => ({ name: "Apollo" })),
}));

vi.mock("../router/assistant-insights.js", () => ({
  buildAssistantInsight: vi.fn(),
}));

vi.mock("../router/assistant-tool-results.js", () => ({
  readToolError: vi.fn(),
  readToolSuccessMessage: vi.fn(),
}));

vi.mock("../router/assistant-tools.js", () => ({
  executeTool: vi.fn(),
}));

import { createAuditEntry } from "../lib/audit.js";
import {
  clearPendingAssistantApproval,
  consumePendingAssistantApproval,
} from "../router/assistant-approvals.js";
import {
  canExecuteMutationTool,
  isCancellationReply,
} from "../router/assistant-confirmation.js";
import { buildAssistantInsight } from "../router/assistant-insights.js";
import { handlePendingAssistantApproval } from "../router/assistant-chat-response.js";
import {
  readToolError,
  readToolSuccessMessage,
} from "../router/assistant-tool-results.js";
import { executeTool } from "../router/assistant-tools.js";

function createPendingApproval() {
  return {
    id: "approval_1",
    userId: "user_1",
    conversationId: "conv_1",
    toolName: "create_project",
    toolArguments: "{\"name\":\"Apollo\"}",
    summary: "create project (name=Apollo)",
    createdAt: Date.now(),
    expiresAt: Date.now() + 60_000,
  };
}

function createHandleInput(overrides: Partial<Parameters<typeof handlePendingAssistantApproval>[0]> = {}) {
  return {
    db: {} as never,
    dbUserId: "user_1",
    toolCtx: {
      db: {} as never,
      userId: "user_1",
      userRole: "USER",
      permissions: new Set(),
      session: null,
      dbUser: null,
      roleDefaults: null,
    },
    conversationId: "conv_1",
    pendingApproval: createPendingApproval(),
    lastUserMessage: { role: "user" as const, content: "ja" },
    messages: [
      { role: "assistant" as const, content: "__CAPAKRAKEN_CONFIRM__ create project (name=Apollo). Bitte bestätigen." },
      { role: "user" as const, content: "ja" },
    ],
    collectedActions: [],
    collectedInsights: [],
    ...overrides,
  };
}

describe("assistant pending approval handling", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(isCancellationReply).mockReturnValue(false);
    vi.mocked(canExecuteMutationTool).mockReturnValue(true);
    vi.mocked(readToolError).mockReturnValue(null);
    vi.mocked(readToolSuccessMessage).mockReturnValue(null);
    vi.mocked(buildAssistantInsight).mockReturnValue(undefined);
  });

  it("cancels pending approvals when the user aborts", async () => {
    vi.mocked(isCancellationReply).mockReturnValue(true);

    const result = await handlePendingAssistantApproval(createHandleInput({
      lastUserMessage: { role: "user", content: "nein, abbrechen" },
    }));

    expect(result).toMatchObject({
      response: {
        content: "Aktion verworfen: create project (name=Apollo)",
        approval: {
          id: "approval_1",
          status: "cancelled",
        },
      },
    });
    expect(clearPendingAssistantApproval).toHaveBeenCalledWith({}, "user_1", "conv_1");
    expect(consumePendingAssistantApproval).not.toHaveBeenCalled();
    expect(executeTool).not.toHaveBeenCalled();
  });

  it("executes the confirmed mutation and returns its success response", async () => {
    vi.mocked(consumePendingAssistantApproval).mockResolvedValue({
      ...createPendingApproval(),
      summary: "create project (name=Apollo, status=DRAFT)",
    } as never);
    vi.mocked(executeTool).mockResolvedValue({
      content: "{\"message\":\"Projekt Apollo angelegt\"}",
      data: { message: "Projekt Apollo angelegt" },
      action: { type: "refresh" },
    } as never);
    vi.mocked(buildAssistantInsight).mockReturnValue({
      kind: "holiday_region",
      title: "Berlin",
      metrics: [{ label: "Resolved holidays", value: "1" }],
    });
    vi.mocked(readToolSuccessMessage).mockReturnValue("Projekt Apollo angelegt");

    const result = await handlePendingAssistantApproval(createHandleInput());

    expect(result).toMatchObject({
      response: {
        content: "Ausgeführt: Projekt Apollo angelegt",
        approval: {
          id: "approval_1",
          status: "approved",
        },
        actions: [{ type: "refresh" }],
        insights: [{
          kind: "holiday_region",
          title: "Berlin",
        }],
      },
    });
    expect(executeTool).toHaveBeenCalledWith(
      "create_project",
      "{\"name\":\"Apollo\"}",
      expect.objectContaining({ userId: "user_1" }),
    );
    expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({
      entityName: "create_project",
      summary: "AI executed previously approved tool: create_project",
    }));
  });

  it("does nothing when the user reply is not a valid confirmation", async () => {
    vi.mocked(canExecuteMutationTool).mockReturnValue(false);

    const result = await handlePendingAssistantApproval(createHandleInput({
      lastUserMessage: { role: "user", content: "vielleicht" },
    }));

    expect(result).toBeNull();
    expect(consumePendingAssistantApproval).not.toHaveBeenCalled();
    expect(executeTool).not.toHaveBeenCalled();
  });
});
