const assert = require("assert");
const placeholders = require("../src/services/placeholders");
const { getSetting, setSetting } = require("../src/services/settings");

const {
  placeholders: placeholderMap,
  fieldPolicies
} = placeholders._internals;

placeholderMap.clear();
fieldPolicies.clear();
placeholders.registerCorePlaceholders();
placeholders.registerFieldPolicy({
  field_id: "okf.markdown",
  label: "OKF Markdown",
  field_type: "okf_markdown",
  output_audience: "user",
  min_editor_role: "user",
  allowed_namespaces: [],
  max_sensitivity: "admin"
});

const user = { id: "user-1", username: "Viewer", isAdmin: false, isMod: false };
const mod = { id: "mod-1", username: "Mod", isAdmin: false, isMod: true };
const admin = { id: "admin-1", username: "Admin", isAdmin: true, isMod: true };

let catalog = placeholders.catalog({
  fieldId: "core.custom_commands.static_response",
  user: mod,
  outputAudience: "user"
});
assert(catalog.placeholders.some((entry) => entry.token === "{{core.main.bot_name}}"));
assert(catalog.placeholders.some((entry) => entry.token === "{{user.public.display_name}}"));
assert(catalog.placeholders.some((entry) => entry.token === "{{core.command.rng}}"));

const customValidation = placeholders.validateCustomPlaceholders([
  { name: "stream.title", value: "Friday stream", visibility: "user", description: "Current stream title" },
  { name: "staff.note", value: "Internal note", visibility: "mod" },
  { name: "admin.internal_note", value: "Admin only", visibility: "admin" }
]);
assert.equal(customValidation.ok, true);
assert.equal(placeholders.validateCustomPlaceholders([{ name: "Bad Name", value: "no" }]).ok, false);
assert.equal(placeholders.validateCustomPlaceholders([{ name: "same", value: "one" }, { name: "same", value: "two" }]).ok, false);
assert.equal(placeholders.validateCustomPlaceholders([{ name: "service.api_key", value: "do-not-store", visibility: "admin" }]).ok, false);
assert.equal(placeholders.validateCustomPlaceholders([{ name: "missing.visibility", value: "no" }]).ok, false);
placeholders.registerCustomPlaceholders(customValidation.definitions);
catalog = placeholders.catalog({
  fieldId: "core.custom_commands.static_response",
  user: mod,
  outputAudience: "user"
});
assert(catalog.placeholders.some((entry) => entry.token === "{{custom.stream.title}}"));
assert.equal(catalog.placeholders.some((entry) => entry.token === "{{custom.staff.note}}"), false);

placeholders.registerPlaceholder({
  id: "core.main.admin_note",
  namespace: "core.main",
  label: "Admin note",
  sensitivity: "admin",
  min_editor_role: "admin",
  min_viewer_role: "admin",
  allowed_field_types: ["command_response"],
  resolver: () => "hidden"
});

catalog = placeholders.catalog({
  fieldId: "core.custom_commands.static_response",
  user: admin,
  outputAudience: "user"
});
assert.equal(catalog.placeholders.some((entry) => entry.token === "{{core.main.admin_note}}"), false);

const invalid = placeholders.validateTemplate({
  fieldId: "core.custom_commands.static_response",
  template: "Hello {{core.main.admin_note}}",
  user: admin,
  outputAudience: "user"
});
assert.equal(invalid.ok, false);
assert.equal(invalid.errors[0].reason, "viewer_role_forbidden");

(async () => {
  const rendered = await placeholders.renderTemplate({
    fieldId: "core.custom_commands.static_response",
    template: "Hi {{user.public.display_name}}, prefix is {{core.main.command_prefix}}.",
    user,
    outputAudience: "user",
    runtimeContext: {
      runtime: true,
      user: { displayName: "SammyCat" }
    }
  });
  assert.equal(rendered.ok, true);
  assert.match(rendered.rendered, /^Hi SammyCat, prefix is .+\.$/);

  const randomRendered = await placeholders.renderTemplate({
    fieldId: "core.custom_commands.static_response",
    template: "Rolled {{core.command.rng}}",
    user: mod,
    outputAudience: "user",
    runtimeContext: { runtime: true, command: { rng: 42 } }
  });
  assert.equal(randomRendered.rendered, "Rolled 42");

  const customRendered = await placeholders.renderTemplate({
    fieldId: "core.custom_commands.static_response",
    template: "Now: {{custom.stream.title}}",
    user: mod,
    outputAudience: "user",
    runtimeContext: { runtime: true }
  });
  assert.equal(customRendered.rendered, "Now: Friday stream");
  const hiddenCustom = await placeholders.renderTemplate({
    fieldId: "core.custom_commands.static_response",
    template: "{{custom.staff.note}}",
    user: admin,
    outputAudience: "user",
    runtimeContext: { runtime: true },
    fallback: "[hidden]"
  });
  assert.equal(hiddenCustom.rendered, "[hidden]");

  const previousGuildId = getSetting("discord_guild_id", "");
  try {
    setSetting("discord_guild_id", "guild-1");
    placeholders.registerPlatformPlaceholders({
      discordClient: {
        guilds: {
          cache: new Map([
            ["guild-1", {
              name: "Cozy Carnage",
              memberCount: 42,
              createdAt: new Date("2025-01-01T00:00:00.000Z"),
              channels: { cache: new Map([
                ["text", { type: 0 }],
                ["voice", { type: 2 }]
              ]) },
              roles: { cache: new Map([["role", {}]]) },
              emojis: { cache: new Map([["emoji", {}]]) },
              premiumSubscriptionCount: 3
            }]
          ])
        }
      }
    });
    const platformCatalog = placeholders.catalog({
      fieldId: "okf.markdown",
      user: admin,
      outputAudience: "user"
    });
    assert(platformCatalog.placeholders.some((entry) => entry.token === "{{platform.discord.guild.member_count}}"));
    assert(platformCatalog.placeholders.some((entry) => entry.token === "{{platform.discord.guild.data_status}}"));

    const platformRendered = await placeholders.renderTemplate({
      fieldId: "okf.markdown",
      template: "Discord has {{platform.discord.guild.member_count}} members ({{platform.discord.guild.data_status}}).",
      user: admin,
      outputAudience: "user",
      runtimeContext: { runtime: true }
    });
    assert.equal(platformRendered.rendered, "Discord has 42 members (live_cache).");

    const missingPlatformRendered = await placeholders.renderTemplate({
      fieldId: "okf.markdown",
      template: "Twitch: {{platform.twitch.channel.data_status}}; YouTube: {{platform.youtube.channel.data_status}}.",
      user: admin,
      outputAudience: "user",
      runtimeContext: { runtime: true }
    });
    assert.match(missingPlatformRendered.rendered, /^Twitch: (unavailable|configured_not_connected); YouTube: (unavailable|configured_not_connected)\.$/);
  } finally {
    setSetting("discord_guild_id", previousGuildId);
  }

  placeholders.registerPlaceholder({
    id: "okf.admin.secret",
    namespace: "okf.admin",
    label: "Admin secret",
    sensitivity: "admin",
    min_editor_role: "admin",
    min_viewer_role: "admin",
    allowed_field_types: ["okf_markdown"],
    resolver: () => "secret"
  });
  const userVisibleAdminPlaceholder = placeholders.validateTemplate({
    fieldId: "okf.markdown",
    template: "{{okf.admin.secret}}",
    user: admin,
    outputAudience: "user"
  });
  assert.equal(userVisibleAdminPlaceholder.ok, false);
  assert.equal(userVisibleAdminPlaceholder.errors[0].reason, "viewer_role_forbidden");

  placeholders.registerFieldPolicy({
    field_id: "test.async",
    label: "Async renderer test",
    field_type: "test_text",
    output_audience: "user",
    min_editor_role: "user",
    allowed_namespaces: ["test"],
    max_sensitivity: "public_safe"
  });
  let duplicateCalls = 0;
  placeholders.registerPlaceholder({
    id: "test.duplicate",
    namespace: "test",
    label: "Duplicate token",
    allowed_field_types: ["test_text"],
    resolver: async () => (++duplicateCalls === 1 ? "{{test.duplicate}}" : "second")
  });
  placeholders.registerPlaceholder({
    id: "test.failed",
    namespace: "test",
    label: "Failed token",
    allowed_field_types: ["test_text"],
    resolver: async () => { throw new Error("simulated failure"); }
  });
  placeholders.registerPlaceholder({
    id: "test.slow",
    namespace: "test",
    label: "Slow token",
    allowed_field_types: ["test_text"],
    resolver: () => new Promise(() => {})
  });
  placeholders.registerPlaceholder({
    id: "test.unavailable",
    namespace: "test",
    label: "Unavailable token",
    allowed_field_types: ["test_text"],
    available: () => false,
    resolver: () => "must-not-render"
  });
  const deterministic = await placeholders.renderTemplate({
    fieldId: "test.async",
    template: "{{test.duplicate}}{{test.duplicate}}|{{test.failed}}|{{test.unavailable}}|{{test.slow}}",
    user,
    outputAudience: "user",
    runtimeContext: { runtime: true, placeholder_timeout_ms: 50 },
    fallback: "[safe]"
  });
  assert.equal(deterministic.rendered, "{{test.duplicate}}second|[safe]|[safe]|[safe]");
  assert(deterministic.errors.some((entry) => entry.reason === "resolver_failed"));
  assert(deterministic.errors.some((entry) => entry.reason === "placeholder_unavailable"));
  assert(deterministic.errors.some((entry) => entry.reason === "resolver_timeout"));
  console.log("Placeholder verification passed.");
})().catch((error) => {
  console.error(error);
  process.exit(1);
});
