type MetricKey = "searchSuggestions" | "searchPage";

type RateLimitEntry = {
  count: number;
  resetAt: number;
};

type MetricStats = {
  allowed: number;
  blocked: number;
  lastAllowedAt: number | null;
  lastBlockedAt: number | null;
  lastAllowedQuery: string | null;
  lastBlockedQuery: string | null;
};

type RateLimitConfig = {
  windowMs: number;
  maxRequests: number;
};

type MetricState = {
  rateLimitStore: Map<string, RateLimitEntry>;
  stats: MetricStats;
  config: RateLimitConfig;
};

type GlobalMetricsStore = {
  __storefrontApiMetrics__?: Record<MetricKey, MetricState>;
};

const globalStore = globalThis as typeof globalThis & GlobalMetricsStore;

function createMetricState(config: RateLimitConfig): MetricState {
  return {
    rateLimitStore: new Map<string, RateLimitEntry>(),
    stats: {
      allowed: 0,
      blocked: 0,
      lastAllowedAt: null,
      lastBlockedAt: null,
      lastAllowedQuery: null,
      lastBlockedQuery: null,
    },
    config,
  };
}

const metricsStore = globalStore.__storefrontApiMetrics__ ?? {
  searchSuggestions: createMetricState({ windowMs: 60_000, maxRequests: 25 }),
  searchPage: createMetricState({ windowMs: 60_000, maxRequests: 20 }),
};

globalStore.__storefrontApiMetrics__ = metricsStore;

export function getClientKeyFromHeaders(input: { forwardedFor?: string | null; realIp?: string | null }) {
  const firstForwardedIp = input.forwardedFor?.split(",")[0]?.trim();
  if (firstForwardedIp) {
    return firstForwardedIp;
  }

  const trimmedRealIp = input.realIp?.trim();
  if (trimmedRealIp) {
    return trimmedRealIp;
  }

  return "unknown";
}

export function consumeMetricRateLimit(metric: MetricKey, clientKey: string) {
  const state = metricsStore[metric];
  const now = Date.now();
  const current = state.rateLimitStore.get(clientKey);

  if (!current || current.resetAt <= now) {
    const nextEntry = { count: 1, resetAt: now + state.config.windowMs };
    state.rateLimitStore.set(clientKey, nextEntry);
    return {
      allowed: true,
      remaining: state.config.maxRequests - nextEntry.count,
      resetAt: nextEntry.resetAt,
      resetInSeconds: Math.max(0, Math.ceil((nextEntry.resetAt - now) / 1000)),
      limit: state.config.maxRequests,
    };
  }

  if (current.count >= state.config.maxRequests) {
    return {
      allowed: false,
      remaining: 0,
      resetAt: current.resetAt,
      resetInSeconds: Math.max(0, Math.ceil((current.resetAt - now) / 1000)),
      limit: state.config.maxRequests,
    };
  }

  current.count += 1;
  state.rateLimitStore.set(clientKey, current);

  return {
    allowed: true,
    remaining: state.config.maxRequests - current.count,
    resetAt: current.resetAt,
    resetInSeconds: Math.max(0, Math.ceil((current.resetAt - now) / 1000)),
    limit: state.config.maxRequests,
  };
}

export function recordMetricAllowed(metric: MetricKey, query: string) {
  const state = metricsStore[metric];
  state.stats.allowed += 1;
  state.stats.lastAllowedAt = Date.now();
  state.stats.lastAllowedQuery = query || null;
  return state.stats.allowed;
}

export function recordMetricBlocked(metric: MetricKey, query: string) {
  const state = metricsStore[metric];
  state.stats.blocked += 1;
  state.stats.lastBlockedAt = Date.now();
  state.stats.lastBlockedQuery = query || null;
  return state.stats.blocked;
}

export function getApiMetricsSnapshot() {
  const now = Date.now();

  return {
    generatedAt: now,
    metrics: {
      searchSuggestions: {
        ...metricsStore.searchSuggestions.stats,
        activeClients: metricsStore.searchSuggestions.rateLimitStore.size,
        ...metricsStore.searchSuggestions.config,
      },
      searchPage: {
        ...metricsStore.searchPage.stats,
        activeClients: metricsStore.searchPage.rateLimitStore.size,
        ...metricsStore.searchPage.config,
      },
    },
  };
}
