"use strict";

const test = require("node:test");
const assert = require("node:assert/strict");
const childProcess = require("node:child_process");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const {
  HoloLakeAiGateway,
  HoloLakeKnowledgeProvider,
} = require("./hololake-capabilities");

function createBareKnowledgeRepository() {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "hololake-knowledge-"));
  const work = path.join(root, "work");
  const bare = path.join(root, "knowledge.git");
  fs.mkdirSync(work);
  childProcess.execFileSync("git", ["init", "-b", "main"], { cwd: work });
  childProcess.execFileSync("git", ["config", "user.email", "test@example.invalid"], { cwd: work });
  childProcess.execFileSync("git", ["config", "user.name", "HoloLake Test"], { cwd: work });
  fs.writeFileSync(path.join(work, "INDEX.md"), "# HoloLake\n");
  childProcess.execFileSync("git", ["add", "INDEX.md"], { cwd: work });
  childProcess.execFileSync("git", ["commit", "-m", "knowledge baseline"], { cwd: work });
  childProcess.execFileSync("git", ["clone", "--bare", work, bare]);
  return { root, bare };
}

test("knowledge provider exposes only the fixed main commit and archive", () => {
  const repository = createBareKnowledgeRepository();
  const provider = new HoloLakeKnowledgeProvider({
    repositoryId: "bingshuo/hololake-knowledge-base",
    repositoryPath: repository.bare,
    maxArchiveBytes: 2 * 1024 * 1024,
  });

  const manifest = provider.manifest();
  assert.equal(
    manifest.repository,
    "bingshuo/hololake-knowledge-base",
  );
  assert.match(manifest.commit, /^[0-9a-f]{40}$/);
  assert.equal(manifest.ref, "refs/heads/main");

  const archive = provider.archive(manifest.commit);
  assert.equal(archive.commit, manifest.commit);
  assert.equal(archive.content_type, "application/zip");
  assert.equal(archive.body.subarray(0, 2).toString("ascii"), "PK");
  assert.equal(archive.sha256.length, 64);

  assert.throws(
    () => provider.archive("0000000000000000000000000000000000000000"),
    /knowledge_commit_not_current/,
  );
});

test("AI gateway accepts only registered provider models and never leaks the key", async () => {
  const requests = [];
  const gateway = new HoloLakeAiGateway({
    providers: {
      default: {
        baseUrl: "https://models.example.invalid/v1",
        apiKey: "server-secret-key",
        models: ["gpt-test"],
      },
    },
    fetchImpl: async (url, options) => {
      requests.push({ url, options });
      return new Response(JSON.stringify({
        id: "response-1",
        choices: [{ message: { role: "assistant", content: "ok" } }],
        usage: { total_tokens: 3 },
      }), {
        status: 200,
        headers: { "content-type": "application/json" },
      });
    },
  });

  const result = await gateway.execute({
    provider: "default",
    model: "gpt-test",
    messages: [{ role: "user", content: "hello" }],
    max_tokens: 32,
  });
  assert.equal(result.ok, true);
  assert.equal(result.response.choices[0].message.content, "ok");
  assert.equal(result.receipt.provider, "default");
  assert.equal(requests.length, 1);
  assert.equal(
    requests[0].options.headers.authorization,
    "Bearer server-secret-key",
  );
  assert.doesNotMatch(JSON.stringify(result), /server-secret-key/);
  const catalog = gateway.catalog();
  assert.deepEqual(catalog.providers[0].models, ["gpt-test"]);
  assert.doesNotMatch(JSON.stringify(catalog), /server-secret-key|models\\.example/);

  await assert.rejects(
    () => gateway.execute({
      provider: "default",
      model: "not-registered",
      messages: [{ role: "user", content: "hello" }],
    }),
    /ai_model_not_registered/,
  );
  await assert.rejects(
    () => gateway.execute({
      provider: "https://attacker.invalid/v1",
      model: "gpt-test",
      messages: [{ role: "user", content: "hello" }],
    }),
    /ai_provider_not_registered/,
  );
  assert.equal(requests.length, 1);
});

test("AI gateway bounds message shape, count, text, and requested output", async () => {
  const gateway = new HoloLakeAiGateway({
    providers: {
      default: {
        baseUrl: "https://models.example.invalid/v1",
        apiKey: "server-secret-key",
        models: ["gpt-test"],
      },
    },
    fetchImpl: async () => new Response("{}", { status: 200 }),
    maxMessages: 2,
    maxInputCharacters: 10,
    maxOutputTokens: 64,
  });

  await assert.rejects(
    () => gateway.execute({
      provider: "default",
      model: "gpt-test",
      messages: [{ role: "user", content: "12345678901" }],
    }),
    /ai_input_too_large/,
  );
  await assert.rejects(
    () => gateway.execute({
      provider: "default",
      model: "gpt-test",
      messages: [{ role: "tool", content: "hello" }],
    }),
    /ai_message_invalid/,
  );
  await assert.rejects(
    () => gateway.execute({
      provider: "default",
      model: "gpt-test",
      messages: [{ role: "user", content: "hello" }],
      max_tokens: 65,
    }),
    /ai_output_limit_exceeded/,
  );
});
