import { redisClient, isRedisConnected } from '../../config/redis.js';
import { logger } from '../utils/logger.js';
import { EffectivePermission } from '../../modules/roles/permissions.service.js';

/**
 * Service for caching user permissions in Redis
 * Key format: permissions:{tenantId}:{userId}
 * TTL: 5 minutes (300 seconds)
 */

const CACHE_TTL = 300; // 5 minutes
const CACHE_PREFIX = 'permissions';

/**
 * Builds the cache key for user permissions
 */
function buildKey(tenantId: string, userId: string): string {
  return `${CACHE_PREFIX}:${tenantId}:${userId}`;
}

/**
 * Get cached permissions for a user
 * @returns Array of effective permissions or null if not cached
 */
export async function getCachedPermissions(
  tenantId: string,
  userId: string
): Promise<EffectivePermission[] | null> {
  if (!isRedisConnected()) {
    return null;
  }

  try {
    const key = buildKey(tenantId, userId);
    const cached = await redisClient.get(key);

    if (cached) {
      logger.debug('Permission cache hit', { tenantId, userId });
      return JSON.parse(cached) as EffectivePermission[];
    }

    logger.debug('Permission cache miss', { tenantId, userId });
    return null;
  } catch (error) {
    logger.error('Error reading permission cache', {
      error: (error as Error).message,
      tenantId,
      userId,
    });
    return null;
  }
}

/**
 * Cache permissions for a user
 */
export async function setCachedPermissions(
  tenantId: string,
  userId: string,
  permissions: EffectivePermission[]
): Promise<void> {
  if (!isRedisConnected()) {
    return;
  }

  try {
    const key = buildKey(tenantId, userId);
    await redisClient.setex(key, CACHE_TTL, JSON.stringify(permissions));
    logger.debug('Permissions cached', {
      tenantId,
      userId,
      count: permissions.length,
      ttl: CACHE_TTL,
    });
  } catch (error) {
    logger.error('Error caching permissions', {
      error: (error as Error).message,
      tenantId,
      userId,
    });
  }
}

/**
 * Invalidate cached permissions for a specific user
 * Call this when user roles or permissions change
 */
export async function invalidateUserPermissions(
  tenantId: string,
  userId: string
): Promise<void> {
  if (!isRedisConnected()) {
    return;
  }

  try {
    const key = buildKey(tenantId, userId);
    await redisClient.del(key);
    logger.info('User permission cache invalidated', { tenantId, userId });
  } catch (error) {
    logger.error('Error invalidating user permission cache', {
      error: (error as Error).message,
      tenantId,
      userId,
    });
  }
}

/**
 * Invalidate cached permissions for all users in a tenant
 * Call this when role permissions are modified at tenant level
 */
export async function invalidateTenantPermissions(tenantId: string): Promise<void> {
  if (!isRedisConnected()) {
    return;
  }

  try {
    const pattern = `${CACHE_PREFIX}:${tenantId}:*`;
    let cursor = '0';
    let deletedCount = 0;

    do {
      const [nextCursor, keys] = await redisClient.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
      cursor = nextCursor;

      if (keys.length > 0) {
        await redisClient.del(...keys);
        deletedCount += keys.length;
      }
    } while (cursor !== '0');

    logger.info('Tenant permission cache invalidated', {
      tenantId,
      keysDeleted: deletedCount,
    });
  } catch (error) {
    logger.error('Error invalidating tenant permission cache', {
      error: (error as Error).message,
      tenantId,
    });
  }
}

/**
 * Check if a specific permission exists in cached permissions
 * Returns null if not in cache (need to check DB)
 */
export async function checkCachedPermission(
  tenantId: string,
  userId: string,
  resource: string,
  action: string
): Promise<boolean | null> {
  const permissions = await getCachedPermissions(tenantId, userId);

  if (permissions === null) {
    return null; // Not cached, need DB check
  }

  return permissions.some(p => p.resource === resource && p.action === action);
}

/**
 * Export service object for convenience
 */
export const permissionCacheService = {
  get: getCachedPermissions,
  set: setCachedPermissions,
  invalidateUser: invalidateUserPermissions,
  invalidateTenant: invalidateTenantPermissions,
  checkPermission: checkCachedPermission,
};
