/**
 * Utilitários de Segurança para Tokens
 * 
 * Fornece funções para proteger tokens contra vazamento:
 * - Criptografia básica de tokens
 * - Validação de expiração
 * - Mascaramento para logs
 * - Sanitização de dados
 * - Proteção CSRF
 */

import { isTokenExpired, decodeJWT } from './jwtUtils';

/**
 * Mascara um token para uso em logs
 * Mostra apenas primeiros 8 e últimos 4 caracteres
 * @param {string} token - Token a ser mascarado
 * @returns {string} Token mascarado
 */
export const maskTokenForLogging = (token) => {
  if (!token || typeof token !== 'string') {
    return '[NO TOKEN]';
  }
  
  if (token.length <= 12) {
    return '***';
  }
  
  const start = token.substring(0, 8);
  const end = token.substring(token.length - 4);
  return `${start}...${end}`;
};

/**
 * Mascara dados sensíveis de um objeto para logs
 * @param {Object} data - Objeto com dados sensíveis
 * @param {string[]} sensitiveFields - Campos sensíveis a mascarar
 * @returns {Object} Objeto com dados mascarados
 */
export const maskSensitiveData = (data, sensitiveFields = ['token', 'password', 'senha', 'secret', 'apiKey']) => {
  if (!data || typeof data !== 'object') {
    return data;
  }
  
  const masked = { ...data };
  
  sensitiveFields.forEach(field => {
    if (masked[field]) {
      if (typeof masked[field] === 'string') {
        masked[field] = maskTokenForLogging(masked[field]);
      } else {
        masked[field] = '[MASKED]';
      }
    }
  });
  
  return masked;
};

/**
 * Criptografa um token usando Web Crypto API (quando disponível)
 * Fallback: encoding base64 com ofuscação
 * @param {string} token - Token a ser criptografado
 * @returns {Promise<string>} Token criptografado
 */
export const encryptToken = async (token) => {
  if (!token) return null;
  
  // Em ambiente de produção, usar Web Crypto API se disponível
  if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) {
    try {
      // Deriva chave do user agent + timestamp (não armazenada)
      const encoder = new TextEncoder();
      const data = encoder.encode(token);
      
      // Usa AES-GCM se disponível, senão usa encoding simples
      // Nota: Criptografia real requer chave armazenada, o que não é seguro no cliente
      // Esta implementação é uma ofuscação básica
      const keyMaterial = await window.crypto.subtle.importKey(
        'raw',
        encoder.encode(navigator.userAgent + window.location.origin),
        { name: 'PBKDF2' },
        false,
        ['deriveBits']
      );
      
      // Para simplificar e manter compatibilidade, usamos encoding base64 com ofuscação
      // Em produção real, a criptografia deve ser feita no backend
      const base64 = btoa(token);
      const obfuscated = base64.split('').reverse().join('');
      return `enc_${obfuscated}`;
    } catch (error) {
      console.warn('Erro ao criptografar token, usando fallback:', error);
    }
  }
  
  // Fallback: encoding base64 com ofuscação simples
  const base64 = btoa(token);
  const obfuscated = base64.split('').reverse().join('');
  return `enc_${obfuscated}`;
};

/**
 * Descriptografa um token
 * @param {string} encryptedToken - Token criptografado
 * @returns {Promise<string>} Token descriptografado
 */
export const decryptToken = async (encryptedToken) => {
  if (!encryptedToken || !encryptedToken.startsWith('enc_')) {
    return encryptedToken; // Já está descriptografado ou inválido
  }
  
  try {
    const obfuscated = encryptedToken.replace('enc_', '');
    const base64 = obfuscated.split('').reverse().join('');
    return atob(base64);
  } catch (error) {
    console.warn('Erro ao descriptografar token:', error);
    return null;
  }
};

/**
 * Valida se um token está expirado e ainda é válido
 * @param {string} token - Token JWT a ser validado
 * @returns {boolean} True se o token é válido (não expirado)
 */
export const validateTokenExpiration = (token) => {
  if (!token) return false;
  
  // Valida expiração usando jwtUtils
  // Adiciona um buffer de 10 segundos para evitar problemas de sincronia de tempo
  const payload = decodeJWT(token);
  if (!payload || !payload.exp) return true; // Se não tem exp, considera válido

  const expirationTime = (payload.exp * 1000) - 10000; // 10 segundos de folga
  return Date.now() < expirationTime;
};

/**
 * Sanitiza dados do usuário removendo informações sensíveis
 * @param {Object} userData - Dados do usuário
 * @returns {Object} Dados sanitizados
 */
export const sanitizeUserData = (userData) => {
  if (!userData || typeof userData !== 'object') {
    return userData;
  }
  
  // Remove campos sensíveis que não devem ser armazenados
  const sensitiveFields = ['password', 'senha', 'secret', 'apiKey', 'accessToken', 'refreshToken'];
  const sanitized = { ...userData };
  
  sensitiveFields.forEach(field => {
    delete sanitized[field];
  });
  
  // Sanitiza strings para prevenir XSS
  Object.keys(sanitized).forEach(key => {
    if (typeof sanitized[key] === 'string') {
      sanitized[key] = sanitizeString(sanitized[key]);
    }
  });
  
  return sanitized;
};

/**
 * Sanitiza uma string para prevenir XSS
 * @param {string} str - String a ser sanitizada
 * @returns {string} String sanitizada
 */
export const sanitizeString = (str) => {
  if (!str || typeof str !== 'string') {
    return str;
  }
  
  // Remove caracteres perigosos para XSS
  return str
    .replace(/[<>]/g, '') // Remove < e >
    .replace(/javascript:/gi, '') // Remove javascript:
    .replace(/on\w+=/gi, '') // Remove event handlers
    .trim();
};

/**
 * Gera um token CSRF
 * @returns {string} Token CSRF
 */
export const generateCSRFToken = () => {
  // Gera token aleatório usando crypto se disponível
  const array = new Uint8Array(32);
  if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
    window.crypto.getRandomValues(array);
  } else {
    // Fallback: Math.random (menos seguro, mas funcional)
    for (let i = 0; i < array.length; i++) {
      array[i] = Math.floor(Math.random() * 256);
    }
  }
  
  return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
};

/**
 * Armazena token CSRF
 * @param {string} token - Token CSRF
 */
export const setCSRFToken = (token) => {
  if (typeof window === 'undefined') return;
  
  // Armazena em sessionStorage (não persiste entre sessões)
  sessionStorage.setItem('csrf_token', token);
};

/**
 * Recupera token CSRF
 * @returns {string|null} Token CSRF ou null
 */
export const getCSRFToken = () => {
  if (typeof window === 'undefined') return null;
  return sessionStorage.getItem('csrf_token');
};

/**
 * Valida token CSRF
 * @param {string} token - Token CSRF a ser validado
 * @returns {boolean} True se o token é válido
 */
export const validateCSRFToken = (token) => {
  if (!token) return false;
  
  const storedToken = getCSRFToken();
  if (!storedToken) return false;
  
  return token === storedToken;
};

/**
 * Limpa token CSRF
 */
export const clearCSRFToken = () => {
  if (typeof window === 'undefined') return;
  sessionStorage.removeItem('csrf_token');
};

/**
 * Verifica se está em ambiente de desenvolvimento
 * @returns {boolean} True se estiver em desenvolvimento
 */
export const isDevelopment = () => {
  return import.meta.env?.DEV === true || import.meta.env?.MODE === 'development';
};

/**
 * Log seguro - apenas em desenvolvimento e com dados mascarados
 * @param {string} message - Mensagem do log
 * @param {any} data - Dados a serem logados (serão mascarados)
 */
export const secureLog = (message, data = null) => {
  if (!isDevelopment()) {
    return; // Não loga em produção
  }
  
  if (data && typeof data === 'object') {
    const masked = maskSensitiveData(data);
    console.log(`[SECURE] ${message}`, masked);
  } else if (data && typeof data === 'string') {
    // Verifica se parece ser um token
    if (data.length > 20 && data.includes('.')) {
      console.log(`[SECURE] ${message}`, maskTokenForLogging(data));
    } else {
      console.log(`[SECURE] ${message}`, data);
    }
  } else {
    console.log(`[SECURE] ${message}`, data);
  }
};

/**
 * Limpa todos os tokens expirados do localStorage
 * @returns {number} Número de tokens removidos
 */
export const cleanExpiredTokens = () => {
  if (typeof window === 'undefined') return 0;
  
  let removed = 0;
  const keysToCheck = [];
  
  // Coleta todas as chaves que podem conter tokens
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (key && (key.startsWith('token_') || key.startsWith('token_setor_') || key === 'x-access-token')) {
      keysToCheck.push(key);
    }
  }
  
  // Verifica e remove tokens expirados
  keysToCheck.forEach(key => {
    try {
      const value = localStorage.getItem(key);
      if (value && value.startsWith('eyJ')) { // JWT geralmente começa com eyJ
        if (isTokenExpired(value)) {
          localStorage.removeItem(key);
          removed++;
        }
      }
    } catch (error) {
      console.warn(`Erro ao verificar token ${key}:`, error);
    }
  });
  
  return removed;
};
