const DEFAULT_RATE_LIMITS = Object.freeze({
  roles: {
    admin: { requests: 30, window_seconds: 60 },
    mod: { requests: 12, window_seconds: 60 },
    user: { requests: 4, window_seconds: 60 }
  },
  platforms: {
    webui: { requests: 60, window_seconds: 60 },
    discord: { requests: 30, window_seconds: 60 },
    twitch: { requests: 20, window_seconds: 60 },
    youtube: { requests: 20, window_seconds: 60 },
    kick: { requests: 20, window_seconds: 60 },
    other: { requests: 20, window_seconds: 60 }
  },
  per_user: { requests: 2, window_seconds: 30 },
  per_channel: { requests: 12, window_seconds: 60 },
  queue_when_limited: false
});

class AiRateLimiter {
  constructor(getConfig, recordAudit = () => {}) {
    this.getConfig = getConfig;
    this.recordAudit = recordAudit;
    this.buckets = new Map();
    this.denials = [];
  }

  check(context) {
    const config = mergeLimits(this.getConfig()?.rate_limits);
    const platform = context.platform || context.origin || "webui";
    const checks = [
      [`role:${context.role}`, config.roles[context.role] || config.roles.user],
      [`platform:${platform}`, config.platforms[platform] || config.platforms.other],
      [`user:${context.user_id}`, config.per_user]
    ];
    const channelKey = context.server_id || context.channel_id;
    if (channelKey) checks.push([`channel:${platform}:${channelKey}`, config.per_channel]);
    const now = Date.now();
    const prepared = [];
    for (const [key, limit] of checks) {
      const result = this.inspect(key, limit, now);
      if (!result.allowed) {
        const denial = {
          at: new Date(now).toISOString(),
          reason: "rate_limited",
          bucket: key,
          retry_after_seconds: result.retry_after_seconds,
          user_id: context.user_id,
          platform,
          channel_id: context.channel_id || null
        };
        this.denials.unshift(denial);
        this.denials = this.denials.slice(0, 100);
        this.recordAudit({ kind: "access", status: "denied", denial_reason: "rate_limited", ...denial });
        return { ...result, reason: "rate_limited", bucket: key };
      }
      prepared.push(result);
    }
    for (const result of prepared) this.commit(result, now);
    return { allowed: true };
  }

  recentDenials(limit = 25) {
    return this.denials.slice(0, limit);
  }

  inspect(key, limit, now) {
    const requests = Math.max(0, Number(limit?.requests) || 0);
    const windowMs = Math.max(1, Number(limit?.window_seconds) || 1) * 1000;
    const current = (this.buckets.get(key) || []).filter((timestamp) => now - timestamp < windowMs);
    if (!requests) return { allowed: true, key, current, disabled: true };
    if (current.length >= requests) {
      const retry = Math.max(1, Math.ceil((windowMs - (now - current[0])) / 1000));
      return { allowed: false, retry_after_seconds: retry, key, current };
    }
    return { allowed: true, key, current };
  }

  commit(result, now) {
    if (result.disabled) return;
    this.buckets.set(result.key, [...result.current, now]);
  }
}

function mergeLimits(value = {}) {
  return {
    ...DEFAULT_RATE_LIMITS,
    ...value,
    roles: { ...DEFAULT_RATE_LIMITS.roles, ...(value.roles || {}) },
    platforms: { ...DEFAULT_RATE_LIMITS.platforms, ...(value.platforms || {}) },
    per_user: { ...DEFAULT_RATE_LIMITS.per_user, ...(value.per_user || {}) },
    per_channel: { ...DEFAULT_RATE_LIMITS.per_channel, ...(value.per_channel || {}) }
  };
}

module.exports = { DEFAULT_RATE_LIMITS, AiRateLimiter, mergeLimits };
