import type { Context, Next } from "hono";
import { REDIS_KEYS } from "@/lib/redis.utils";
import { logError } from "@/lib/logger";

interface CacheOptions {
    ttl?: number; // Time to live in seconds
    key?: string | ((ctx: Context) => string);
}

export function redisCacheMiddleware(options: CacheOptions = {}) {
    const { ttl = 300 } = options;

    return async (ctx: Context, next: Next) => {
        const redis = ctx.get("redisDB");
        if (!redis) {
            return await next();
        }

        let cacheKey = "";
        if (typeof options.key === "function") {
            cacheKey = options.key(ctx);
        } else if (typeof options.key === "string") {
            cacheKey = options.key;
        } else {
            const url = new URL(ctx.req.url);
            cacheKey = `${ctx.req.method}:${url.pathname}${url.search}`;
        }

        const fullCacheKey = REDIS_KEYS.cache(cacheKey);

        const cacheControl = ctx.req.header("cache-control") || "";
        const pragma = ctx.req.header("pragma") || "";
        const bypassCache =
            cacheControl.includes("no-cache") ||
            pragma.includes("no-cache") ||
            ctx.req.query("refresh") === "true" ||
            ctx.req.query("nocache") === "true";

        try {
            if (!bypassCache) {
                const cachedResponse = await redis.get(fullCacheKey);
                if (cachedResponse) {
                    const { body, status, headers } = JSON.parse(cachedResponse);
                    return ctx.newResponse(body, status, headers);
                }
            }
        } catch (err) {
            logError(`[Redis Cache] Get Error: ${err}`);
        }

        await next();

        if (ctx.res.status >= 200 && ctx.res.status < 300) {
            try {
                const contentType = ctx.res.headers.get("content-type");
                if (contentType && contentType.includes("application/json")) {
                    const body = await ctx.res.clone().text();
                    const responseData = {
                        body,
                        status: ctx.res.status,
                        headers: Object.fromEntries(ctx.res.headers.entries()),
                    };

                    await redis.set(fullCacheKey, JSON.stringify(responseData), "EX", ttl);
                }
            } catch (err) {
                logError(`[Redis Cache] Set Error: ${err}`);
            }
        }
    };
}
