import { Request, Response, NextFunction } from 'express';

interface CacheEntry {
  data: unknown;
  expiresAt: number;
}

const cache = new Map<string, CacheEntry>();
const DEFAULT_TTL = 60 * 1000; // 1 minute

/** Never cache generic API error envelopes (avoids poisoning cache if status/body ever disagree). */
function looksLikeGenericErrorPayload(body: unknown): boolean {
  if (body == null || typeof body !== 'object' || Array.isArray(body)) return false;
  const o = body as Record<string, unknown>;
  if (typeof o.error !== 'string') return false;
  // Success shapes we must not treat as errors
  if ('pools' in o || 'tokens' in o || 'data' in o || 'chains' in o || 'tree' in o || 'quote' in o) return false;
  if (o.status === 'healthy') return false;
  return true;
}

export function cacheMiddleware(ttl: number = DEFAULT_TTL) {
  return (req: Request, res: Response, next: NextFunction) => {
    const bypassCache =
      req.query.refresh === '1' ||
      req.query.noCache === '1' ||
      /\bno-cache\b|\bno-store\b/i.test(req.header('cache-control') || '');
    const key = `${req.method}:${req.originalUrl}`;
    const cached = bypassCache ? undefined : cache.get(key);

    if (cached && cached.expiresAt > Date.now()) {
      res.setHeader('X-Token-Aggregation-Cache', 'hit');
      return res.json(cached.data);
    }

    res.setHeader('X-Token-Aggregation-Cache', bypassCache ? 'bypass' : 'miss');

    // Store original json method
    const originalJson = res.json.bind(res);

    // Override json method to cache response
    res.json = function (body: unknown) {
      // Only cache successful payloads. Otherwise a 500 body gets replayed on cache hit with HTTP 200.
      const okStatus = res.statusCode >= 200 && res.statusCode < 300;
      if (!bypassCache && okStatus && !looksLikeGenericErrorPayload(body)) {
        cache.set(key, {
          data: body,
          expiresAt: Date.now() + ttl,
        });
      }
      return originalJson(body);
    };

    next();
  };
}

export function clearCache(): void {
  cache.clear();
}

export function clearCacheForPattern(pattern: string): void {
  for (const key of cache.keys()) {
    if (key.includes(pattern)) {
      cache.delete(key);
    }
  }
}
