import { describe, expect, test } from "vitest";
import { resolveMainSessionKeyFromConfig } from "../config/sessions.js";
import { drainSystemEvents, peekSystemEvents } from "../infra/system-events.js";
import {
  cronIsolatedRun,
  getFreePort,
  installGatewayTestHooks,
  startGatewayServer,
  testState,
  waitForSystemEvent,
} from "./test-helpers.js";

installGatewayTestHooks();

const resolveMainKey = () => resolveMainSessionKeyFromConfig();

describe("gateway server hooks", () => {
  test("hooks wake requires auth", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: "Ping" }),
    });
    expect(res.status).toBe(401);
    await server.close();
  });

  test("hooks wake enqueues system event", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({ text: "Ping", mode: "next-heartbeat" }),
    });
    expect(res.status).toBe(200);
    const events = await waitForSystemEvent();
    expect(events.some((e) => e.includes("Ping"))).toBe(true);
    drainSystemEvents(resolveMainKey());
    await server.close();
  });

  test("hooks agent posts summary to main", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    cronIsolatedRun.mockResolvedValueOnce({
      status: "ok",
      summary: "done",
    });
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/agent`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({ message: "Do it", name: "Email" }),
    });
    expect(res.status).toBe(202);
    const events = await waitForSystemEvent();
    expect(events.some((e) => e.includes("Hook Email: done"))).toBe(true);
    drainSystemEvents(resolveMainKey());
    await server.close();
  });

  test("hooks agent forwards model override", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    cronIsolatedRun.mockClear();
    cronIsolatedRun.mockResolvedValueOnce({
      status: "ok",
      summary: "done",
    });
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/agent`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({
        message: "Do it",
        name: "Email",
        model: "openai/gpt-4.1-mini",
      }),
    });
    expect(res.status).toBe(202);
    await waitForSystemEvent();
    const call = cronIsolatedRun.mock.calls[0]?.[0] as {
      job?: { payload?: { model?: string } };
    };
    expect(call?.job?.payload?.model).toBe("openai/gpt-4.1-mini");
    drainSystemEvents(resolveMainKey());
    await server.close();
  });

  test("hooks wake accepts query token", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(
      `http://127.0.0.1:${port}/hooks/wake?token=hook-secret`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ text: "Query auth" }),
      },
    );
    expect(res.status).toBe(200);
    const events = await waitForSystemEvent();
    expect(events.some((e) => e.includes("Query auth"))).toBe(true);
    drainSystemEvents(resolveMainKey());
    await server.close();
  });

  test("hooks agent rejects invalid provider", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/agent`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({ message: "Nope", provider: "sms" }),
    });
    expect(res.status).toBe(400);
    expect(peekSystemEvents(resolveMainKey()).length).toBe(0);
    await server.close();
  });

  test("hooks wake accepts x-clawdbot-token header", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-clawdbot-token": "hook-secret",
      },
      body: JSON.stringify({ text: "Header auth" }),
    });
    expect(res.status).toBe(200);
    const events = await waitForSystemEvent();
    expect(events.some((e) => e.includes("Header auth"))).toBe(true);
    drainSystemEvents(resolveMainKey());
    await server.close();
  });

  test("hooks rejects non-post", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "GET",
      headers: { Authorization: "Bearer hook-secret" },
    });
    expect(res.status).toBe(405);
    await server.close();
  });

  test("hooks wake requires text", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({ text: " " }),
    });
    expect(res.status).toBe(400);
    await server.close();
  });

  test("hooks agent requires message", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/agent`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: JSON.stringify({ message: " " }),
    });
    expect(res.status).toBe(400);
    await server.close();
  });

  test("hooks rejects invalid json", async () => {
    testState.hooksConfig = { enabled: true, token: "hook-secret" };
    const port = await getFreePort();
    const server = await startGatewayServer(port);
    const res = await fetch(`http://127.0.0.1:${port}/hooks/wake`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer hook-secret",
      },
      body: "{",
    });
    expect(res.status).toBe(400);
    await server.close();
  });
});
