const { db } = require("./db");
const { incrementCommands } = require("./stats");
const {
  buildCommandContext,
  runAdvancedCommand,
  normalizeCommandResult
} = require("./commands");
const { normalizeRandomReplies, selectRandomReply } = require("./command-random");
const { getEnabledPlatformIds, normalizePlatformSelection } = require("./platforms");
const placeholders = require("./placeholders");

function createCommandRouter({ settings }) {
  const commandMap = new Map();
  const pluginCommands = new Map();

  function clearCommands(pluginId) {
    const existing = pluginCommands.get(pluginId) || [];
    for (const entry of existing) {
      const handlers = commandMap.get(entry.trigger) || [];
      const nextHandlers = handlers.filter((handler) => handler !== entry.handler);
      if (nextHandlers.length) {
        commandMap.set(entry.trigger, nextHandlers);
      } else {
        commandMap.delete(entry.trigger);
      }
    }
    pluginCommands.delete(pluginId);
  }

  function registerCommands(pluginId, commands = []) {
    if (!pluginId) {
      throw new Error("Plugin id is required to register commands.");
    }
    clearCommands(pluginId);
    const entries = [];
    for (const command of commands) {
      const triggers = (command.triggers || [])
        .map((trigger) => trigger.toLowerCase())
        .filter(Boolean);
      const handler = buildHandler(command);
      for (const trigger of triggers) {
        const list = commandMap.get(trigger) || [];
        list.push(handler);
        commandMap.set(trigger, list);
        entries.push({ trigger, handler });
      }
    }
    pluginCommands.set(pluginId, entries);
  }

  function buildHandler(command) {
    const handler = async (ctx) => {
      if (command.platforms && command.platforms.length) {
        if (!command.platforms.includes(ctx.platform)) {
          return false;
        }
      }
      return await command.handler(ctx);
    };
    handler.commandId = command.id || null;
    return handler;
  }

  async function handleMessage({ platform, raw, user, platformUser, reply, meta }) {
    const prefix = settings.getSetting("command_prefix", "!");
    if (!raw.startsWith(prefix)) {
      return false;
    }
    const rawCommand = raw.slice(prefix.length).trim();
    if (!rawCommand) {
      return false;
    }
    const parts = rawCommand.split(/\s+/);
    const trigger = parts[0].toLowerCase();
    const args = parts.slice(1);
    const argsText = args.join(" ");
    const ctx = {
      platform,
      trigger,
      raw,
      args,
      argsText,
      user: {
        id: user.id,
        username: user.internal_username || user.username,
        platformId: platformUser.id,
        displayName: platformUser.displayName || platformUser.username,
        tag: platformUser.tag
      },
      platformUser,
      meta,
      reply
    };

    const customHandled = await handleCustomCommand({
      trigger,
      platform,
      ctx,
      raw,
      reply
    });
    if (customHandled) {
      incrementCommands(user.id);
      return true;
    }

    const handlers = commandMap.get(trigger) || [];
    for (const handler of handlers) {
      try {
        const result = await handler(ctx);
        if (typeof result === "string" && result) {
          await safeReply(reply, result);
          recordCommandUsage(handler.commandId);
          incrementCommands(user.id);
          return true;
        }
        if (result === true) {
          recordCommandUsage(handler.commandId);
          incrementCommands(user.id);
          return true;
        }
      } catch (error) {
        console.error("Command handler failed", error);
        await safeReply(reply, "Command failed to execute.");
        return true;
      }
    }

    return false;
  }

  return {
    registerCommands,
    clearCommands,
    handleMessage
  };
}

async function handleCustomCommand({ trigger, platform, ctx, raw, reply }) {
  const row = db
    .prepare(
      "SELECT response, mode, language, code, platform, random_replies_json, rng_enabled, rng_min, rng_max FROM custom_commands WHERE trigger = ? AND enabled = 1"
    )
    .get(trigger);
  if (!row) {
    return false;
  }
  const enabledPlatforms = getEnabledPlatformIds();
  const allowedPlatforms = normalizePlatformSelection(row.platform, enabledPlatforms);
  if (!allowedPlatforms.includes(platform)) {
    return false;
  }
  try {
    if (row.mode === "advanced" && row.code) {
      const messageInfo = buildMessageInfo(ctx, raw);
      const commandCtx = buildCommandContext({
        platform,
        user: {
          id: ctx.user.id,
          platformId: ctx.user.platformId,
          username: ctx.user.username,
          displayName: ctx.user.displayName,
          tag: ctx.user.tag
        },
        message: messageInfo,
        args: ctx.args,
        argsText: ctx.argsText
      });
      const result = await runAdvancedCommand(
        { code: row.code, language: row.language },
        commandCtx
      );
      const output = normalizeCommandResult(result);
      if (output) {
        await safeReply(reply, output);
      } else {
        await safeReply(reply, "Command ran but returned no output.");
      }
    } else if (row.mode === "random") {
      const selected = selectRandomReply({
        replies: normalizeRandomReplies(row.random_replies_json),
        rngEnabled: Boolean(row.rng_enabled),
        rngMin: row.rng_min,
        rngMax: row.rng_max
      });
      const rendered = await placeholders.renderTemplate({
        fieldId: "core.custom_commands.static_response",
        template: selected.reply.text,
        outputAudience: "user",
        user: {
          id: ctx.user.id,
          username: ctx.user.username,
          isAdmin: false,
          isMod: false
        },
        runtimeContext: { ctx, user: ctx.user, platform, runtime: true, command: { rng: selected.rng } }
      });
      await safeReply(reply, rendered.rendered);
    } else {
      const rendered = await placeholders.renderTemplate({
        fieldId: "core.custom_commands.static_response",
        template: row.response,
        outputAudience: "user",
        user: {
          id: ctx.user.id,
          username: ctx.user.username,
          isAdmin: false,
          isMod: false
        },
        runtimeContext: { ctx, user: ctx.user, platform, runtime: true }
      });
      await safeReply(reply, rendered.rendered);
    }
    recordCommandUsage(`custom:${trigger}`);
    return true;
  } catch (error) {
    console.error("Failed to reply to command", error);
    await safeReply(reply, "Command failed to execute.");
    return true;
  }
}

function buildMessageInfo(ctx, raw) {
  if (ctx.platform === "discord" && ctx.meta?.message) {
    const message = ctx.meta.message;
    return {
      id: message.id,
      content: raw,
      channelId: message.channelId,
      guildId: message.guildId
    };
  }
  if (ctx.platform === "twitch") {
    return {
      channel: ctx.meta?.channel,
      content: raw
    };
  }
  if (ctx.platform === "youtube") {
    return {
      liveChatId: ctx.meta?.liveChatId,
      messageId: ctx.meta?.messageId,
      channelId: ctx.meta?.author?.channelId,
      content: raw
    };
  }
  return { content: raw };
}

async function safeReply(reply, content) {
  try {
    await reply(content);
  } catch (error) {
    console.error("Command reply failed", error);
  }
}

function recordCommandUsage(commandId) {
  if (!commandId) {
    return;
  }
  const now = Date.now();
  db.prepare(
    "INSERT INTO command_usage (command_id, count, updated_at) VALUES (?, 1, ?) " +
      "ON CONFLICT(command_id) DO UPDATE SET count = count + 1, updated_at = excluded.updated_at"
  ).run(commandId, now);
}

module.exports = {
  createCommandRouter
};
