/**
 * User Cache Service
 *
 * Provides Redis-based caching for user lookups to improve
 * performance during authentication flows (OTP verification, login).
 *
 * Cache Strategy:
 * - Cache user by email AND phone number (dual key)
 * - Short TTL (5 min) to balance performance vs freshness
 * - Invalidate on user update
 */

import { redis, redisConfigured } from '../redis';
import { prisma } from '../db';
import { logger } from '../logger';

// Cache configuration
const USER_CACHE_TTL_SECONDS = 5 * 60; // 5 minutes
const USER_CACHE_PREFIX = 'user_cache:';

// Minimal user data needed for auth flows (avoid caching sensitive data)
export interface CachedUserData {
  id: string;
  email: string;
  name: string | null;
  phoneNumber: string | null;
  // Don't cache passwords or sensitive tokens
}

/**
 * Get cached user by identifier (email or phone)
 * Falls back to database if not in cache
 */
export async function getCachedUser(
  identifier: string,
  isEmail: boolean
): Promise<CachedUserData | null> {
  const cacheKey = `${USER_CACHE_PREFIX}${isEmail ? 'email' : 'phone'}:${identifier.toLowerCase()}`;

  // Try cache first if Redis is configured
  if (redisConfigured) {
    try {
      const cached = await redis.get(cacheKey);
      if (cached) {
        const userData = JSON.parse(cached) as CachedUserData;
        logger.debug('[UserCache] Cache hit', { identifier: identifier.slice(-4) });
        return userData;
      }
    } catch (error) {
      // Cache miss or error - fall through to database
      logger.debug('[UserCache] Cache miss/error', {
        error: error instanceof Error ? error.message : 'unknown',
      });
    }
  }

  // Query database
  const user = await prisma.users.findFirst({
    where: isEmail
      ? { email: identifier.toLowerCase() }
      : { phoneNumber: identifier },
    select: {
      id: true,
      email: true,
      name: true,
      phoneNumber: true,
    },
  });

  if (!user) {
    return null;
  }

  // Cache the result if Redis is available
  if (redisConfigured) {
    try {
      const cacheData: CachedUserData = {
        id: user.id,
        email: user.email,
        name: user.name,
        phoneNumber: user.phoneNumber,
      };

      // Cache by both email and phone for future lookups
      const promises: Promise<unknown>[] = [
        redis.setex(
          `${USER_CACHE_PREFIX}email:${user.email.toLowerCase()}`,
          USER_CACHE_TTL_SECONDS,
          JSON.stringify(cacheData)
        ),
      ];

      if (user.phoneNumber) {
        promises.push(
          redis.setex(
            `${USER_CACHE_PREFIX}phone:${user.phoneNumber}`,
            USER_CACHE_TTL_SECONDS,
            JSON.stringify(cacheData)
          )
        );
      }

      // Non-blocking cache write
      Promise.all(promises).catch((err) => {
        logger.warn('[UserCache] Failed to cache user', {
          error: err instanceof Error ? err.message : 'unknown',
        });
      });

      logger.debug('[UserCache] Cached user from DB', { userId: user.id });
    } catch (error) {
      // Caching failure shouldn't break the flow
      logger.warn('[UserCache] Cache write failed', {
        error: error instanceof Error ? error.message : 'unknown',
      });
    }
  }

  return {
    id: user.id,
    email: user.email,
    name: user.name,
    phoneNumber: user.phoneNumber,
  };
}

/**
 * Invalidate user cache when user data changes
 * Call this after user updates
 */
export async function invalidateUserCache(
  email: string,
  phoneNumber?: string | null
): Promise<void> {
  if (!redisConfigured) {
    return;
  }

  try {
    const keys = [`${USER_CACHE_PREFIX}email:${email.toLowerCase()}`];
    if (phoneNumber) {
      keys.push(`${USER_CACHE_PREFIX}phone:${phoneNumber}`);
    }

    await redis.del(...keys);
    logger.debug('[UserCache] Invalidated cache', { email: email.slice(-4) });
  } catch (error) {
    logger.warn('[UserCache] Cache invalidation failed', {
      error: error instanceof Error ? error.message : 'unknown',
    });
  }
}

/**
 * Pre-warm cache for a user (call during OTP send)
 * This ensures the user is cached before verification
 */
export async function prewarmUserCache(email: string): Promise<void> {
  try {
    await getCachedUser(email, true);
  } catch (error) {
    // Pre-warming failure is not critical
    logger.debug('[UserCache] Prewarm failed', {
      error: error instanceof Error ? error.message : 'unknown',
    });
  }
}
