/**
 * Redis Cache Layer
 *
 * High-performance caching layer for frequently accessed data:
 * - User profiles (5min TTL)
 * - Pricing tiers (1hr TTL)
 * - Settings (15min TTL)
 * - Cache invalidation on writes
 * - Hit rate tracking and monitoring
 *
 * @server-only
 */

import "server-only";
import { cache, getCacheMetrics } from "@/lib/cache";
import { prisma } from "@/lib/db";
import { logger } from "@/lib/logger";

// Cache TTL constants
const CACHE_TTL = {
  USER_PROFILE: 300, // 5 minutes
  PRICING_TIERS: 3600, // 1 hour
  SETTINGS: 900, // 15 minutes
  STAFF_PERMISSIONS: 600, // 10 minutes
} as const;

// Cache key prefixes
const CACHE_PREFIX = {
  USER_PROFILE: "user:profile",
  PRICING_TIERS: "pricing:tiers",
  SETTINGS: "settings",
  STAFF_PERMISSIONS: "staff:permissions",
} as const;

/**
 * User profile cache interface
 */
export interface CachedUserProfile {
  id: string;
  email: string | null;
  username: string | null;
  role: string;
  isActive: boolean;
  companyName: string | null;
  createdAt: string;
  updatedAt: string;
}

/**
 * Get user profile by ID with Redis caching
 *
 * @param userId - User ID to fetch
 * @returns User profile or null if not found
 */
export async function getUserProfile(
  userId: string,
): Promise<CachedUserProfile | null> {
  const cacheKey = `${CACHE_PREFIX.USER_PROFILE}:${userId}`;

  try {
    return await cache.getOrSet(
      cacheKey,
      async () => {
        logger.debug("[Redis Cache] User profile cache miss", { userId });

        const user = await prisma.users.findUnique({
          where: { id: userId },
          select: {
            id: true,
            email: true,
            username: true,
            role: true,
            isActive: true,
            companyName: true,
            createdAt: true,
            updatedAt: true,
          },
        });

        if (!user) {
          return null;
        }

        // Convert dates to ISO strings for JSON serialization
        return {
          ...user,
          createdAt: user.createdAt.toISOString(),
          updatedAt: user.updatedAt.toISOString(),
        };
      },
      CACHE_TTL.USER_PROFILE,
    );
  } catch (error) {
    logger.error("[Redis Cache] Error fetching user profile", {
      userId,
      error,
    });
    // Fall back to direct database query on cache failure
    const user = await prisma.users.findUnique({
      where: { id: userId },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        isActive: true,
        companyName: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    if (!user) return null;

    return {
      ...user,
      createdAt: user.createdAt.toISOString(),
      updatedAt: user.updatedAt.toISOString(),
    };
  }
}

/**
 * Get user profile by email with Redis caching
 *
 * @param email - User email to fetch
 * @returns User profile or null if not found
 */
export async function getUserProfileByEmail(
  email: string,
): Promise<CachedUserProfile | null> {
  const cacheKey = `${CACHE_PREFIX.USER_PROFILE}:email:${email.toLowerCase()}`;

  try {
    return await cache.getOrSet(
      cacheKey,
      async () => {
        logger.debug("[Redis Cache] User profile by email cache miss", {
          email,
        });

        const user = await prisma.users.findUnique({
          where: { email: email.toLowerCase() },
          select: {
            id: true,
            email: true,
            username: true,
            role: true,
            isActive: true,
            companyName: true,
            createdAt: true,
            updatedAt: true,
          },
        });

        if (!user) {
          return null;
        }

        return {
          ...user,
          createdAt: user.createdAt.toISOString(),
          updatedAt: user.updatedAt.toISOString(),
        };
      },
      CACHE_TTL.USER_PROFILE,
    );
  } catch (error) {
    logger.error("[Redis Cache] Error fetching user profile by email", {
      email,
      error,
    });
    const user = await prisma.users.findUnique({
      where: { email: email.toLowerCase() },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        isActive: true,
        companyName: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    if (!user) return null;

    return {
      ...user,
      createdAt: user.createdAt.toISOString(),
      updatedAt: user.updatedAt.toISOString(),
    };
  }
}

/**
 * Invalidate user profile cache
 *
 * @param userId - User ID to invalidate
 * @param email - Optional email to invalidate
 */
export async function invalidateUserProfile(
  userId: string,
  email?: string,
): Promise<void> {
  try {
    const keysToInvalidate = [`${CACHE_PREFIX.USER_PROFILE}:${userId}`];

    if (email) {
      keysToInvalidate.push(
        `${CACHE_PREFIX.USER_PROFILE}:email:${email.toLowerCase()}`,
      );
    }

    await Promise.all(keysToInvalidate.map((key) => cache.del(key)));

    logger.debug("[Redis Cache] User profile invalidated", { userId, email });
  } catch (error) {
    logger.error("[Redis Cache] Error invalidating user profile", {
      userId,
      email,
      error,
    });
  }
}

/**
 * Pricing tier cache interface
 */
export interface CachedPricingTier {
  id: string;
  name: string;
  description: string | null;
  monthlyPrice: number;
  yearlyPrice: number;
  features: string[];
  maxUsers: number;
  isActive: boolean;
}

/**
 * Get all active pricing tiers with Redis caching
 *
 * @returns Array of pricing tiers
 */
export async function getPricingTiers(): Promise<CachedPricingTier[]> {
  const cacheKey = `${CACHE_PREFIX.PRICING_TIERS}:all`;

  try {
    return await cache.getOrSet(
      cacheKey,
      async () => {
        logger.debug("[Redis Cache] Pricing tiers cache miss");

        // Fetch from database (example - adjust to your schema)
        // This is a placeholder - you may need to adjust based on your actual pricing tier schema
        const tiers = [
          {
            id: "tier1",
            name: "Starter",
            description: "Perfect for small clinics",
            monthlyPrice: 99,
            yearlyPrice: 990,
            features: [
              "Up to 5 users",
              "Basic WhatsApp integration",
              "Email support",
            ],
            maxUsers: 5,
            isActive: true,
          },
          {
            id: "tier2",
            name: "Professional",
            description: "For growing practices",
            monthlyPrice: 199,
            yearlyPrice: 1990,
            features: [
              "Up to 20 users",
              "Advanced WhatsApp features",
              "Priority support",
            ],
            maxUsers: 20,
            isActive: true,
          },
          {
            id: "tier3",
            name: "Enterprise",
            description: "For large organizations",
            monthlyPrice: 499,
            yearlyPrice: 4990,
            features: ["Unlimited users", "Full API access", "24/7 support"],
            maxUsers: -1,
            isActive: true,
          },
        ];

        return tiers;
      },
      CACHE_TTL.PRICING_TIERS,
    );
  } catch (error) {
    logger.error("[Redis Cache] Error fetching pricing tiers", { error });
    // Return empty array on error
    return [];
  }
}

/**
 * Get specific pricing tier by ID with Redis caching
 *
 * @param tierId - Tier ID to fetch
 * @returns Pricing tier or null if not found
 */
export async function getPricingTier(
  tierId: string,
): Promise<CachedPricingTier | null> {
  const cacheKey = `${CACHE_PREFIX.PRICING_TIERS}:${tierId}`;

  try {
    return await cache.getOrSet(
      cacheKey,
      async () => {
        logger.debug("[Redis Cache] Pricing tier cache miss", { tierId });

        const tiers = await getPricingTiers();
        return tiers.find((tier) => tier.id === tierId) || null;
      },
      CACHE_TTL.PRICING_TIERS,
    );
  } catch (error) {
    logger.error("[Redis Cache] Error fetching pricing tier", {
      tierId,
      error,
    });
    return null;
  }
}

/**
 * Invalidate pricing tier cache
 */
export async function invalidatePricingTiers(): Promise<void> {
  try {
    await cache.del(`${CACHE_PREFIX.PRICING_TIERS}:all`);
    logger.debug("[Redis Cache] Pricing tiers invalidated");
  } catch (error) {
    logger.error("[Redis Cache] Error invalidating pricing tiers", { error });
  }
}

/**
 * Settings cache interface
 */
export interface CachedSettings {
  [key: string]: string | number | boolean | null;
}

/**
 * Get application settings with Redis caching
 *
 * @param namespace - Settings namespace (e.g., 'app', 'billing', 'notifications')
 * @returns Settings object
 */
export async function getSettings(
  namespace: string = "app",
): Promise<CachedSettings> {
  const cacheKey = `${CACHE_PREFIX.SETTINGS}:${namespace}`;

  try {
    return await cache.getOrSet(
      cacheKey,
      async () => {
        logger.debug("[Redis Cache] Settings cache miss", { namespace });

        // Fetch from database (example - adjust to your schema)
        // This is a placeholder - you may need to adjust based on your actual settings schema
        const settings: CachedSettings = {
          maintenanceMode: false,
          maxUploadSize: 10485760, // 10MB
          sessionTimeout: 3600,
          enableEmailNotifications: true,
          enableSMSNotifications: false,
        };

        return settings;
      },
      CACHE_TTL.SETTINGS,
    );
  } catch (error) {
    logger.error("[Redis Cache] Error fetching settings", { namespace, error });
    return {};
  }
}

/**
 * Get specific setting value with Redis caching
 *
 * @param namespace - Settings namespace
 * @param key - Setting key
 * @returns Setting value or null if not found
 */
export async function getSetting<T = string | number | boolean>(
  namespace: string,
  key: string,
): Promise<T | null> {
  try {
    const settings = await getSettings(namespace);
    return (settings[key] as T) || null;
  } catch (error) {
    logger.error("[Redis Cache] Error fetching setting", {
      namespace,
      key,
      error,
    });
    return null;
  }
}

/**
 * Invalidate settings cache
 *
 * @param namespace - Settings namespace to invalidate
 */
export async function invalidateSettings(
  namespace: string = "app",
): Promise<void> {
  try {
    await cache.del(`${CACHE_PREFIX.SETTINGS}:${namespace}`);
    logger.debug("[Redis Cache] Settings invalidated", { namespace });
  } catch (error) {
    logger.error("[Redis Cache] Error invalidating settings", {
      namespace,
      error,
    });
  }
}

/**
 * Get cache hit rate statistics
 *
 * @returns Cache metrics including hit rate
 */
export function getCacheHitRate() {
  const metrics = getCacheMetrics();

  return {
    ...metrics,
    namespaces: {
      userProfile: metrics.namespaces[CACHE_PREFIX.USER_PROFILE] || {
        hits: 0,
        misses: 0,
      },
      pricingTiers: metrics.namespaces[CACHE_PREFIX.PRICING_TIERS] || {
        hits: 0,
        misses: 0,
      },
      settings: metrics.namespaces[CACHE_PREFIX.SETTINGS] || {
        hits: 0,
        misses: 0,
      },
    },
  };
}

/**
 * Clear all cache entries (use with caution!)
 */
export async function clearAllCache(): Promise<void> {
  logger.warn("[Redis Cache] Clearing all cache entries");

  // Note: prefixes available for future SCAN-based implementation
  // const prefixes = Object.values(CACHE_PREFIX);

  // Note: This is a simple implementation
  // For production, you may want to use Redis SCAN to find and delete keys
  await Promise.all([
    invalidatePricingTiers(),
    invalidateSettings("app"),
    invalidateSettings("billing"),
    invalidateSettings("notifications"),
  ]);

  logger.info("[Redis Cache] All cache entries cleared");
}
