import assert from "node:assert/strict";
import test from "node:test";

import { createSupportPassportModelAdapter, parseConfig } from "@remnic/core";
import { FallbackLlmClient } from "@remnic/core/fallback-llm";

import { createOpenClawSupportPassportModelRoute } from "./support-passport-model-route.js";

test("OpenClaw gateway models draft support cards without a direct OpenAI key", { concurrency: false }, async () => {
  const originalFetch = globalThis.fetch;
  const models: string[] = [];
  const authorizationHeaders: Array<string | null> = [];
  globalThis.fetch = (async (_url, init) => {
    assert.ok(init?.body, "The gateway request must include a body.");
    const body = JSON.parse(String(init.body)) as { model: string };
    models.push(body.model);
    authorizationHeaders.push(new Headers(init?.headers).get("authorization"));
    const content =
      body.model === "primary"
        ? JSON.stringify({ cards: [{ sourceMemoryIds: ["not-selected"] }] })
        : JSON.stringify({
            cards: [
              {
                title: "Plan changes",
                statement: "Tell me before plans change.",
                category: "transitions",
                sourceMemoryIds: ["memory-1"],
              },
            ],
          });
    return new Response(JSON.stringify({ choices: [{ message: { content } }] }), {
      status: 200,
      headers: { "content-type": "application/json" },
    });
  }) as typeof fetch;

  try {
    const config = parseConfig({
      modelSource: "gateway",
      openaiApiKey: false,
      taskModelChain: {
        primary: "gateway/primary",
        fallbacks: ["gateway/fallback"],
      },
      gatewayConfig: {
        agents: { defaults: { model: { primary: "gateway/default" } } },
        models: {
          providers: {
            gateway: {
              baseUrl: "http://127.0.0.1:11434/v1",
              api: "openai-completions",
              models: [
                { id: "primary", name: "primary" },
                { id: "fallback", name: "fallback" },
              ],
            },
          },
        },
      },
    });
    const client = new FallbackLlmClient(config.gatewayConfig);
    const gatewayRoute = createOpenClawSupportPassportModelRoute(config, client);
    const adapter = createSupportPassportModelAdapter(config, { gatewayRoute });

    const result = await adapter.draftCards({
      consent: true,
      memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
    });

    assert.deepEqual(models, ["primary", "fallback"]);
    assert.deepEqual(authorizationHeaders, [null, null]);
    assert.equal(result.route, "gateway");
    assert.equal(result.modelUsed, "gateway/fallback");
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("OpenClaw support passport routes preserve private structured model options", async () => {
  let optionsSeen: Record<string, unknown> | undefined;
  const config = parseConfig({
    modelSource: "gateway",
    openaiApiKey: false,
    taskModelChain: { primary: "gateway/private-model" },
  });
  const client = {
    chatCompletion: async (_messages: unknown, options: Record<string, unknown>) => {
      optionsSeen = options;
      return {
        content: JSON.stringify({
          cards: [
            {
              title: "Plan changes",
              statement: "Tell me before plans change.",
              category: "transitions",
              sourceMemoryIds: ["memory-1"],
            },
          ],
        }),
        modelUsed: "gateway/private-model",
      };
    },
  };
  const gatewayRoute = createOpenClawSupportPassportModelRoute(config, client);
  const adapter = createSupportPassportModelAdapter(config, { gatewayRoute });

  await adapter.draftCards({
    consent: true,
    memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
  });

  assert.equal(optionsSeen?.store, false);
  assert.equal(optionsSeen?.redactProviderErrors, true);
  assert.equal(optionsSeen?.includeDefaultModelFallback, false);
  assert.deepEqual(optionsSeen?.modelChain, { primary: "gateway/private-model" });
  assert.equal((optionsSeen?.responsesJsonSchema as { name?: string } | undefined)?.name, "support_passport_drafts");
});

test("OpenClaw support passport models never spill into the gateway default chain", { concurrency: false }, async () => {
  const originalFetch = globalThis.fetch;
  const models: string[] = [];
  globalThis.fetch = (async (_url, init) => {
    const body = JSON.parse(String(init?.body)) as { model: string };
    models.push(body.model);
    const content = body.model === "default"
      ? JSON.stringify({
          cards: [{
            title: "Private fallback",
            statement: "This response must not be used.",
            category: "other",
            sourceMemoryIds: ["memory-1"],
          }],
        })
      : JSON.stringify({ cards: [{ sourceMemoryIds: ["not-selected"] }] });
    return new Response(JSON.stringify({ choices: [{ message: { content } }] }), {
      status: 200,
      headers: { "content-type": "application/json" },
    });
  }) as typeof fetch;

  try {
    const config = parseConfig({
      modelSource: "gateway",
      openaiApiKey: false,
      taskModelChain: { primary: "gateway/private" },
      gatewayConfig: {
        agents: { defaults: { model: { primary: "gateway/default" } } },
        models: {
          providers: {
            gateway: {
              baseUrl: "http://127.0.0.1:11434/v1",
              api: "openai-completions",
              models: [
                { id: "private", name: "private" },
                { id: "default", name: "default" },
              ],
            },
          },
        },
      },
    });
    const client = new FallbackLlmClient(config.gatewayConfig);
    const adapter = createSupportPassportModelAdapter(config, {
      gatewayRoute: createOpenClawSupportPassportModelRoute(config, client),
    });

    await assert.rejects(
      adapter.draftCards({
        consent: true,
        memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
      }),
      (error: unknown) => (error as { code?: string }).code === "model_output_invalid",
    );
    assert.deepEqual(models, ["private"]);
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("OpenClaw support passport models reject a missing explicit gateway agent", async () => {
  let callCount = 0;
  const config = parseConfig({
    modelSource: "gateway",
    openaiApiKey: false,
    gatewayAgentId: "missing-agent",
    gatewayConfig: {
      agents: { defaults: { model: { primary: "gateway/default" } } },
    },
  });
  const gatewayRoute = createOpenClawSupportPassportModelRoute(config, {
    chatCompletion: async () => {
      callCount += 1;
      return {
        content: JSON.stringify({ cards: [] }),
        modelUsed: "gateway/default",
      };
    },
  });
  const adapter = createSupportPassportModelAdapter(config, { gatewayRoute });

  await assert.rejects(
    adapter.draftCards({
      consent: true,
      memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
    }),
    (error: unknown) => (error as { code?: string }).code === "provider_unavailable",
  );
  assert.equal(callCount, 0);
});

test("OpenClaw plugin models ignore a stale gateway agent and use gateway defaults", { concurrency: false }, async () => {
  const originalFetch = globalThis.fetch;
  const models: string[] = [];
  globalThis.fetch = (async (_url, init) => {
    const body = JSON.parse(String(init?.body)) as { model: string };
    models.push(body.model);
    const content = JSON.stringify({
      cards: [
        {
          title: "Plan changes",
          statement: "Tell me before plans change.",
          category: "transitions",
          sourceMemoryIds: ["memory-1"],
        },
      ],
    });
    return new Response(JSON.stringify({ choices: [{ message: { content } }] }), {
      status: 200,
      headers: { "content-type": "application/json" },
    });
  }) as typeof fetch;

  try {
    const config = parseConfig({
      modelSource: "plugin",
      openaiApiKey: false,
      localLlmEnabled: false,
      gatewayAgentId: "missing-agent",
      gatewayConfig: {
        agents: { defaults: { model: { primary: "gateway/default" } } },
        models: {
          providers: {
            gateway: {
              baseUrl: "http://127.0.0.1:11434/v1",
              api: "openai-completions",
              models: [{ id: "default", name: "default" }],
            },
          },
        },
      },
    });
    const gatewayRoute = createOpenClawSupportPassportModelRoute(
      config,
      new FallbackLlmClient(config.gatewayConfig),
    );
    const adapter = createSupportPassportModelAdapter(config, { gatewayRoute });

    const result = await adapter.draftCards({
      consent: true,
      memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
    });

    assert.deepEqual(models, ["default"]);
    assert.equal(result.route, "gateway");
    assert.equal(result.modelUsed, "gateway/default");
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("OpenClaw support passport models trim an explicit gateway agent before dispatch", async () => {
  let agentId: string | undefined;
  const config = parseConfig({
    modelSource: "gateway",
    openaiApiKey: false,
    gatewayAgentId: " passport-agent ",
    gatewayConfig: {
      agents: {
        defaults: { model: { primary: "gateway/default" } },
        list: [{ id: "passport-agent", model: { primary: "gateway/private" } }],
      },
    },
  });
  const gatewayRoute = createOpenClawSupportPassportModelRoute(config, {
    chatCompletion: async (_messages, options) => {
      agentId = options?.agentId;
      return {
        content: JSON.stringify({
          cards: [
            {
              title: "Plan changes",
              statement: "Tell me before plans change.",
              category: "transitions",
              sourceMemoryIds: ["memory-1"],
            },
          ],
        }),
        modelUsed: "gateway/private",
      };
    },
  });
  const adapter = createSupportPassportModelAdapter(config, { gatewayRoute });

  const result = await adapter.draftCards({
    consent: true,
    memories: [{ memoryId: "memory-1", content: "Tell me before plans change." }],
  });

  assert.equal(agentId, "passport-agent");
  assert.equal(result.modelUsed, "gateway/private");
});
