const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { AiProvider, groundReadOnlyToolAnswer } = require("../backend/ai_provider");
const { buildPrompt } = require("../backend/prompt_builder");
const { ToolInstaller } = require("../backend/tool_installer");
const { ToolLoader } = require("../backend/tool_loader");
const { ToolManager } = require("../backend/tool_manager");
const { ToolRegistry, parseToolCallResult } = require("../backend/tool_router");

async function run() {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-ai-loading-"));
  try {
    const pluginsDir = path.join(root, "plugins");
    const stateFile = path.join(root, "data", "tools", "enabled.json");
    fs.mkdirSync(pluginsDir, { recursive: true });
    fs.mkdirSync(path.join(pluginsDir, "lumi_ai"), { recursive: true });
    createTool(pluginsDir, "lumi_ai_web_search", { readOnly: true });
    createTool(pluginsDir, "lumi_ai_test_tool", { readOnly: true });
    createInvalidTool(pluginsDir, "lumi_ai_invalid");
    createMissingEntrypointTool(pluginsDir, "lumi_ai_missing_entrypoint");

    const audit = [];
    const registry = new ToolRegistry((entry) => audit.push(entry));
    const installer = new ToolInstaller({
      pluginsDir,
      stagingRoot: path.join(root, "staging"),
      repoClient: {}
    });
    const loader = new ToolLoader({
      registry,
      installer,
      settings: { getSetting: (_key, fallback) => fallback },
      stateFile,
      lumiAiVersion: "0.8.0",
      lumiVersion: "0.1.0"
    });
    const manager = new ToolManager({
      loader,
      installer,
      settings: { describe: () => ({}) },
      repoClient: {
        async discover() {
          return {
            repository: "local",
            branch: "main",
            checked_at: new Date().toISOString(),
            cached: false,
            stale: false,
            tools: []
          };
        }
      }
    });

    assert.deepEqual(
      installer.scanLocal().map((entry) => entry.tool_id).sort(),
      ["lumi_ai_invalid", "lumi_ai_missing_entrypoint", "lumi_ai_test_tool", "lumi_ai_web_search"]
    );

    loader.setEnabled("lumi_ai_web_search", true);
    loader.setEnabled("lumi_ai_test_tool", true);
    loader.setEnabled("lumi_ai_invalid", true);
    loader.setEnabled("lumi_ai_missing_entrypoint", true);
    await loader.loadEnabled();
    assert.equal(registry.has("lumi_ai_web_search.lookup"), true);
    assert.equal(registry.has("lumi_ai_test_tool.lookup"), true);
    assert.equal(loader.status("lumi_ai_invalid").state, "unavailable");
    assert.equal(loader.status("lumi_ai_missing_entrypoint").state, "unavailable");

    await loader.disable("lumi_ai_test_tool");
    assert.equal(registry.has("lumi_ai_test_tool.lookup"), false);
    const disabledDiagnostics = await manager.diagnostics({
      role: "user",
      user: actor("user"),
      context: origin("webui")
    });
    assert.equal(
      disabledDiagnostics.plugins.find((plugin) => plugin.tool_id === "lumi_ai_test_tool").hidden_reason,
      "disabled"
    );
    assert.equal(
      disabledDiagnostics.plugins.find((plugin) => plugin.tool_id === "lumi_ai_invalid").hidden_reason,
      "schema_invalid"
    );

    const webui = registry.inspect({
      role: "user",
      user: actor("user"),
      context: origin("webui")
    });
    assert(webui.exposed.some((tool) => tool.tool_id === "lumi_ai_web_search.lookup"));
    const discord = registry.inspect({
      role: "user",
      user: actor("user"),
      context: origin("discord", false)
    });
    assert(discord.exposed.some((tool) => tool.tool_id === "lumi_ai_web_search.lookup"));

    registry.register(actionDefinition());
    const platformActions = registry.inspect({
      role: "admin",
      user: actor("admin"),
      context: origin("discord", false)
    });
    assert.equal(
      platformActions.considered.find((entry) => entry.tool.tool_id === "test_admin.remove").reason,
      "scope_blocked"
    );

    const prompt = buildPrompt({
      config: promptConfig(),
      role: "user",
      message: "Search for current Lumi information",
      tools: webui.exposed,
      originContext: origin("webui")
    });
    assert(prompt.includes('{"type":"tool_call","tool":"tool_id","arguments":{}}'));
    assert(prompt.includes('"tool_id":"lumi_ai_web_search.lookup"'));
    assert(prompt.includes('"kind":"lookup/read"'));

    assert.equal(parseToolCallResult('{"type":"tool_call","tool":"x","arguments":{}}').status, "valid");
    assert.equal(parseToolCallResult('Use this: {"type":"tool_call","tool":"x","arguments":{}}').status, "malformed");
    assert.equal(
      parseToolCallResult('{"type":"tool_call","tool":"x","arguments":{},"comment":"no"}').status,
      "malformed"
    );

    await verifyReadOnlyFinalization(registry);
    verifyToolSourceGrounding();
    verifyConfirmation(registry);
    assert(audit.some((entry) => entry.kind === "tool" && entry.status === "success"));
  } finally {
    fs.rmSync(root, { recursive: true, force: true });
  }
  console.log("Lumi AI tool loading verification passed.");
}

function verifyToolSourceGrounding() {
  const grounded = groundReadOnlyToolAnswer(
    "The release is current. [Invented](https://invented.example/release)",
    {
      status: "ok",
      results: [{ title: "Official release notes", url: "https://docs.example.com/releases" }]
    },
    { tool_id: "web_search.search" }
  );
  assert.equal(grounded.text.includes("invented.example"), false);
  assert(grounded.text.includes("https://docs.example.com/releases"));
  assert.equal(grounded.diagnostics.removed_url_count, 1);
  assert.equal(grounded.diagnostics.source_appended, true);

  const alreadyCited = groundReadOnlyToolAnswer(
    "See https://docs.example.com/releases for details.",
    {
      status: "ok",
      results: [{ title: "Official release notes", url: "https://docs.example.com/releases" }]
    },
    { tool_id: "web_search.search" }
  );
  assert.equal(alreadyCited.diagnostics.removed_url_count, 0);
  assert.equal(alreadyCited.diagnostics.source_appended, false);
}

async function verifyReadOnlyFinalization(registry) {
  const outputs = [
    '{"type":"tool_call","tool":"lumi_ai_web_search.lookup","arguments":{"query":"current Lumi release"}}',
    "The current result is Lumi 1.2.3 according to the returned source."
  ];
  let inferCalls = 0;
  const provider = new AiProvider({
    getConfig: () => ({
      selected_model_id: "test",
      support_scope: {},
      instructions: {},
      logging: {},
      hard_generation_timeout_ms: 5000,
      output_budgets: { simple_answer: 128 }
    }),
    runtime: {
      infer: async () => ({
        choices: [{ message: { content: outputs[inferCalls++] }, finish_reason: "stop" }],
        usage: { prompt_tokens: 10, completion_tokens: 5 }
      })
    },
    queue: {
      length: 0,
      run: async (_userId, _role, callback) => callback(0)
    },
    tools: registry,
    metrics: { record() {} },
    getContext: () => [],
    lookupRepo: () => null,
    getRepoContext: () => [],
    getCorrections: () => []
  });
  const result = await provider.generate({
    message: "What is the current Lumi release?",
    user: actor("user"),
    sessionId: "tool-test",
    originContext: origin("webui"),
    allowTools: true
  });
  assert.equal(inferCalls, 2);
  assert.match(result.text, /Lumi 1\.2\.3/);
  assert.deepEqual(result.tool_result, {
    status: "ok",
    query: "current Lumi release",
    results: [{ title: "Lumi 1.2.3", url: "https://example.test/lumi" }]
  });
}

function verifyConfirmation(registry) {
  const prepared = registry.prepare({
    tool: "test_admin.remove",
    args: { target: "example" },
    user: actor("admin"),
    role: "admin",
    sessionId: "confirm-test",
    context: origin("webui")
  });
  assert.equal(prepared.execute, false);
  assert(prepared.confirmation.id);
}

function createTool(pluginsDir, toolId, { readOnly }) {
  const directory = path.join(pluginsDir, toolId);
  fs.mkdirSync(directory, { recursive: true });
  const metadata = {
    tool_id: toolId,
    display_name: toolId,
    version: "1.0.0",
    description: "Test lookup tool",
    scope: "assistant",
    permissions: { required_role: "user" },
    capabilities: ["Current information lookup"],
    limitations: ["Test data only"],
    tool_type: "lookup",
    entrypoints: { backend: "index.js" },
    risk_level: "low",
    confirmation_required: false
  };
  fs.writeFileSync(path.join(directory, "tool_info.json"), `${JSON.stringify(metadata, null, 2)}\n`);
  fs.writeFileSync(path.join(directory, "readme.md"), `# ${toolId}\n`);
  fs.writeFileSync(path.join(directory, "index.js"), `
module.exports.register = ({ registerTool }) => registerTool({
  tool_id: "${toolId}.lookup",
  display_name: "Lookup",
  description: "Looks up current test information.",
  required_role: "user",
  required_permission: "${toolId}.use",
  audit_category: "lookup",
  confirmation_required: false,
  risk_level: "low",
  read_only: ${readOnly},
  use_cases: ["Current information"],
  output_expectations: "Structured test results.",
  schema: { query: { type: "string", required: true } },
  permission_check: ({ user }) => Boolean(user && user.id),
  workflow_handler: async ({ arguments: args }) => ({
    status: "ok",
    query: args.query,
    results: [{ title: "Lumi 1.2.3", url: "https://example.test/lumi" }]
  })
});
`);
}

function createInvalidTool(pluginsDir, toolId) {
  const directory = path.join(pluginsDir, toolId);
  fs.mkdirSync(directory, { recursive: true });
  fs.writeFileSync(path.join(directory, "tool_info.json"), "{invalid");
  fs.writeFileSync(path.join(directory, "readme.md"), "# Invalid\n");
}

function createMissingEntrypointTool(pluginsDir, toolId) {
  const directory = path.join(pluginsDir, toolId);
  fs.mkdirSync(directory, { recursive: true });
  const metadata = {
    tool_id: toolId,
    display_name: toolId,
    version: "1.0.0",
    description: "Missing entrypoint test",
    scope: "assistant",
    permissions: { required_role: "user" },
    capabilities: ["Test"],
    limitations: ["Unavailable"],
    entrypoints: { backend: "missing.js" }
  };
  fs.writeFileSync(path.join(directory, "tool_info.json"), `${JSON.stringify(metadata, null, 2)}\n`);
  fs.writeFileSync(path.join(directory, "readme.md"), "# Missing entrypoint\n");
}

function actionDefinition() {
  return {
    tool_id: "test_admin.remove",
    display_name: "Remove test data",
    description: "Mutates test data.",
    owning_plugin: "test_admin",
    required_role: "admin",
    required_permission: "test.remove",
    audit_category: "test",
    confirmation_required: true,
    risk_level: "sensitive",
    read_only: false,
    schema: { target: { type: "string", required: true } },
    permission_check: ({ user }) => user?.isAdmin === true,
    workflow_handler: async () => ({ status: "ok" })
  };
}

function actor(role) {
  return {
    id: `${role}-1`,
    username: role,
    isAdmin: role === "admin",
    isMod: role === "mod"
  };
}

function origin(platform, webuiActionsAllowed = platform === "webui") {
  return {
    origin: platform,
    platform,
    max_message_length: platform === "twitch" ? 450 : 1900,
    permission_context: {
      identified_user: true,
      webui_actions_allowed: webuiActionsAllowed
    }
  };
}

function promptConfig() {
  return {
    support_scope: {},
    instructions: {
      roleplay_intensity: 0,
      community_tone: "",
      admin_custom: ""
    }
  };
}

run().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
