"use strict";

const CONDITION_PATTERN = /^(<=|>=|<|>|==|=)\s*(-?\d+)$/;

function normalizeRandomReplies(value) {
  let rows = value;
  if (typeof rows === "string") {
    try { rows = JSON.parse(rows || "[]"); } catch { rows = []; }
  }
  if (!Array.isArray(rows)) return [];
  return rows.map((row) => ({
    text: String(row?.text || "").trim(),
    weight: integerValue(row?.weight, 1),
    condition: String(row?.condition || "").trim().replace(/\s+/g, "")
  })).filter((row) => row.text);
}

function parseRandomRepliesFromBody(body = {}) {
  const texts = arrayValue(body.random_text);
  const weights = arrayValue(body.random_weight);
  const conditions = arrayValue(body.random_condition);
  return normalizeRandomReplies(texts.map((text, index) => ({
    text,
    weight: weights[index] ?? 1,
    condition: conditions[index] ?? ""
  })));
}

function normalizeRandomConfig({ replies, rngEnabled, rngMin, rngMax } = {}) {
  return {
    replies: normalizeRandomReplies(replies),
    rngEnabled: rngEnabled === true || rngEnabled === 1 || rngEnabled === "on" || rngEnabled === "true",
    rngMin: integerValue(rngMin, 1),
    rngMax: integerValue(rngMax, 100)
  };
}

function validateRandomConfig(input = {}) {
  const config = normalizeRandomConfig(input);
  const errors = [];
  if (!config.replies.length) errors.push("Add at least one random reply.");
  if (config.replies.length > 100) errors.push("Random Reply supports at most 100 messages.");
  if (!Number.isInteger(config.rngMin) || !Number.isInteger(config.rngMax) || config.rngMin < -1000000 || config.rngMin > 1000000 || config.rngMax < -1000000 || config.rngMax > 1000000) {
    errors.push("RNG limits must be whole numbers between -1,000,000 and 1,000,000.");
  }
  if (config.rngMin > config.rngMax) errors.push("RNG minimum must be less than or equal to the maximum.");
  config.replies.forEach((reply, index) => {
    if (reply.text.length > 2000) errors.push(`Reply ${index + 1} is longer than 2,000 characters.`);
    if (!Number.isInteger(reply.weight) || reply.weight < 1 || reply.weight > 999) {
      errors.push(`Reply ${index + 1} weight must be a whole number from 1 to 999.`);
    }
    if (reply.condition && !CONDITION_PATTERN.test(reply.condition)) {
      errors.push(`Reply ${index + 1} condition must look like <20, >=10, or =5.`);
    }
    if (reply.condition && !config.rngEnabled) {
      errors.push(`Turn on RNG before adding a condition to reply ${index + 1}.`);
    }
  });
  return { ok: errors.length === 0, errors, config };
}

function selectRandomReply(input = {}, random = Math.random) {
  const validation = validateRandomConfig(input);
  if (!validation.ok) throw new Error(validation.errors[0]);
  const config = validation.config;
  const rng = config.rngEnabled
    ? config.rngMin + Math.floor(safeRandom(random) * (config.rngMax - config.rngMin + 1))
    : null;
  const eligible = config.replies.filter((reply) => !reply.condition || conditionMatches(reply.condition, rng));
  if (!eligible.length) throw new Error(`No random reply matches RNG value ${rng}.`);
  const totalWeight = eligible.reduce((total, reply) => total + reply.weight, 0);
  let target = safeRandom(random) * totalWeight;
  for (const reply of eligible) {
    target -= reply.weight;
    if (target < 0) return { reply, rng, eligibleCount: eligible.length };
  }
  return { reply: eligible[eligible.length - 1], rng, eligibleCount: eligible.length };
}

function conditionMatches(condition, value) {
  const match = String(condition || "").match(CONDITION_PATTERN);
  if (!match || !Number.isFinite(value)) return false;
  const expected = Number(match[2]);
  if (match[1] === "<") return value < expected;
  if (match[1] === "<=") return value <= expected;
  if (match[1] === ">") return value > expected;
  if (match[1] === ">=") return value >= expected;
  return value === expected;
}

function safeRandom(random) {
  const value = Number(typeof random === "function" ? random() : Math.random());
  if (!Number.isFinite(value)) return 0;
  return Math.max(0, Math.min(value, 0.999999999999));
}

function arrayValue(value) {
  if (Array.isArray(value)) return value;
  if (value === undefined || value === null) return [];
  return [value];
}

function integerValue(value, fallback) {
  if (value === undefined || value === null || value === "") return fallback;
  const number = Number(value);
  return Number.isInteger(number) ? number : Number.NaN;
}

module.exports = {
  conditionMatches,
  normalizeRandomConfig,
  normalizeRandomReplies,
  parseRandomRepliesFromBody,
  selectRandomReply,
  validateRandomConfig
};
