import crypto from "node:crypto";

import { redisConnection } from "$connections/index.js";
import { telegramBotConfig } from "./config.js";

const stateKey = (telegramId) => `telegram:bot:state:${String(telegramId)}`;
const lockKey = (telegramId, operation) =>
  `telegram:bot:lock:${String(telegramId)}:${String(operation)}`;
const handledUpdateKey = (updateId) =>
  `telegram:bot:update:${String(updateId)}`;

export const createShortId = () => crypto.randomBytes(8).toString("hex");

export const getBotState = async (telegramId) => {
  const payload = await redisConnection.get(stateKey(telegramId));

  if (!payload) {
    return null;
  }

  try {
    return JSON.parse(payload);
  } catch {
    await redisConnection.del(stateKey(telegramId));
    return null;
  }
};

export const setBotState = async (telegramId, state) => {
  await redisConnection.setex(
    stateKey(telegramId),
    telegramBotConfig.stateTtlSeconds,
    JSON.stringify(state),
  );
  return state;
};

export const clearBotState = async (telegramId) => {
  await redisConnection.del(stateKey(telegramId));
};

export const acquireBotLock = async (
  telegramId,
  operation,
  ttlSeconds = 30,
) => {
  const result = await redisConnection.set(
    lockKey(telegramId, operation),
    "1",
    "EX",
    ttlSeconds,
    "NX",
  );
  return result === "OK";
};

export const releaseBotLock = async (telegramId, operation) => {
  await redisConnection.del(lockKey(telegramId, operation));
};

export const markUpdateHandled = async (updateId) => {
  if (updateId === undefined || updateId === null) {
    return true;
  }

  const result = await redisConnection.set(
    handledUpdateKey(updateId),
    "1",
    "EX",
    24 * 60 * 60,
    "NX",
  );
  return result === "OK";
};

export const enforceBotRateLimit = async ({
  scope,
  identifier,
  max,
  windowSeconds,
}) => {
  const key = `telegram:bot:rate:${scope}:${String(identifier)}`;
  const count = await redisConnection.incr(key);

  if (count === 1) {
    await redisConnection.expire(key, windowSeconds);
  }

  if (count > max) {
    const error = new Error(
      "تعداد تلاش‌ها بیش از حد مجاز است. لطفاً کمی بعد دوباره تلاش کنید.",
    );
    error.status = 429;
    throw error;
  }
};
