/**
 * Gerenciador de Tokens por Módulo
 * 
 * Gerencia tokens e setores separados por módulo/ambiente, permitindo que
 * cada módulo (Financeiro, Frota, RH, etc.) mantenha seu próprio token
 * sem conflitos.
 * 
 * O backend agora requer tanto o token quanto o setor em todas as requisições.
 * 
 * SEGURANÇA: Tokens são criptografados antes de armazenar e validados antes de usar.
 */

import { 
  encryptToken, 
  decryptToken, 
  validateTokenExpiration,
  sanitizeUserData,
  maskTokenForLogging,
  secureLog,
  cleanExpiredTokens
} from './tokenSecurity';

// Mapeamento de setores (backend) para módulos (frontend)
const SETOR_TO_MODULE = {
  'Financeiro': 'financeiro',
  'Frota': 'fleet',
  'RH': 'rh',
  'Recursos Humanos': 'rh',
  'Gestão de Risco': 'gr',
  'GR': 'gr',
  'Auto Lab': 'autolab',
  'Autolab': 'autolab',
  'Prafrot': 'prafrot',
  'Monitoramento': 'prafrot', // Setor do Prafrot é "Monitoramento"
  'Workspace': 'workspace',
};

// Mapeamento reverso: módulo → setor (para enviar nas requisições)
// Prioriza o setor armazenado, senão usa o mapeamento padrão
const MODULE_TO_SETOR = {
  'financeiro': 'Financeiro',
  'fleet': 'Frota',
  'rh': 'RH',
  'prafrot': 'Monitoramento', // Setor do Prafrot é "Monitoramento"
  'gr': 'Gestão de Risco',
  'autolab': 'Auto Lab',
  'workspace': 'Workspace',
};

/**
 * Extrai o módulo da URL atual
 * @param {string} pathname - Pathname da URL (opcional, usa window.location.pathname se não fornecido)
 * @returns {string|null} - Nome do módulo ou null se não identificado
 */
export const getModuleFromPath = (pathname = null) => {
  const path = pathname || (typeof window !== 'undefined' ? window.location.pathname : '');
  
  if (!path) return null;
  
  // Mapeamento de padrões de URL para módulos
  if (path.includes('/plataforma/financeiro-v2') || path.includes('/plataforma/financeiro-cnab')) {
    return 'financeiro';
  }
  if (path.includes('/plataforma/fleet-v2') || path.includes('/plataforma/fleet/')) {
    return 'fleet';
  }
  if (path.includes('/plataforma/rh')) {
    return 'rh';
  }
  if (path.includes('/plataforma/prafrot')) {
    return 'prafrot';
  }
  if (path.includes('/plataforma/gr')) {
    return 'gr';
  }
  if (path.includes('/plataforma/autolab')) {
    return 'autolab';
  }
  if (path.includes('/plataforma/workspace')) {
    return 'workspace';
  }
  
  return null;
};

/**
 * Mapeia um setor do backend para um módulo do frontend
 * @param {string} setor - Nome do setor retornado pelo backend
 * @returns {string|null} - Nome do módulo ou null se não mapeado
 */
export const getModuleFromSetor = (setor) => {
  if (!setor) return null;
  return SETOR_TO_MODULE[setor] || null;
};

/**
 * Mapeia um módulo do frontend para um setor do backend
 * @param {string} module - Nome do módulo
 * @returns {string|null} - Nome do setor ou null se não mapeado
 */
export const getSetorFromModule = (module) => {
  if (!module) return null;
  return MODULE_TO_SETOR[module] || null;
};

/**
 * Armazena token para um setor específico
 * Permite múltiplos tokens por setor (armazena o mais recente)
 * SEGURANÇA: Token é criptografado antes de armazenar
 * @param {string} setor - Nome do setor
 * @param {string} token - Token JWT
 */
export const setTokenForSetor = async (setor, token) => {
  if (!setor || !token) {
    secureLog('tokenManager: setor e token são obrigatórios');
    return;
  }
  
  // Valida expiração antes de armazenar
  if (!validateTokenExpiration(token)) {
    secureLog('tokenManager: Tentativa de armazenar token expirado', { setor });
    return;
  }
  
  try {
    // Criptografa token antes de armazenar
    const encryptedToken = await encryptToken(token);
    if (!encryptedToken) {
      secureLog('tokenManager: Erro ao criptografar token', { setor });
      return;
    }
    
    const tokenKey = `token_setor_${setor}`;
    localStorage.setItem(tokenKey, encryptedToken);
    
    // Armazena também no formato de tokens múltiplos (array)
    const tokensKey = `tokens_setor_${setor}`;
    const existingTokens = await getTokensForSetor(setor);
    if (!existingTokens.includes(token)) {
      existingTokens.push(token);
      // Criptografa todos os tokens do array
      const encryptedTokens = await Promise.all(
        existingTokens.map(t => encryptToken(t))
      );
      localStorage.setItem(tokensKey, JSON.stringify(encryptedTokens));
    }
  } catch (error) {
    secureLog('tokenManager: Erro ao armazenar token', { setor, error: error.message });
  }
};

/**
 * Armazena token e setor para um módulo específico
 * Também armazena o token para cada setor retornado
 * SEGURANÇA: Token é criptografado e validado antes de armazenar
 * @param {string} module - Nome do módulo
 * @param {string} token - Token JWT
 * @param {string|string[]} setor - Nome do setor ou array de setores
 */
export const setTokenForModule = async (module, token, setor) => {
  if (!module || !token) {
    secureLog('tokenManager: module e token são obrigatórios');
    return;
  }
  
  // Valida expiração antes de armazenar
  if (!validateTokenExpiration(token)) {
    secureLog('tokenManager: Tentativa de armazenar token expirado', { module });
    return;
  }
  
  try {
    // Criptografa token antes de armazenar
    const encryptedToken = await encryptToken(token);
    if (!encryptedToken) {
      secureLog('tokenManager: Erro ao criptografar token', { module });
      return;
    }
    
    // Armazena token por módulo (compatibilidade) - criptografado
    const tokenKey = `token_${module}`;
    localStorage.setItem(tokenKey, encryptedToken);
    
    // Processa setor(es) - pode ser string ou array
    const setores = Array.isArray(setor) ? setor : (setor ? [setor] : []);
  
    // Armazena a lista completa de setores de forma GLOBAL (Somatória/Merge)
    if (setores.length > 0) {
      const existingGlobalJson = localStorage.getItem('integra_setores');
      let mergedSetores = setores;
      
      if (existingGlobalJson) {
        try {
          const existingSetores = JSON.parse(existingGlobalJson);
          mergedSetores = [...new Set([...existingSetores, ...setores])];
        } catch (e) {
          console.warn('tokenManager: Erro ao mesclar integra_setores');
        }
      }
      
      localStorage.setItem(`integra_setores`, JSON.stringify(mergedSetores));
      // Mantém chave específica do módulo atualizada
      localStorage.setItem(`setores_${module}`, JSON.stringify(setores));
    }

    // Armazena tokens por setor
    for (const s of setores) {
      if (s) {
        await setTokenForSetor(s, token);
        // Mantém chave setor_modulo (ex: setor_prafrot)
        localStorage.setItem(`setor_${module}`, s);
      }
    }
  
    // Define como token principal (Universal)
    localStorage.setItem('x-access-token', encryptedToken);
  
    // Se não houver setor, tenta mapear do módulo
    if (setores.length === 0) {
      const mappedSetor = getSetorFromModule(module);
      if (mappedSetor) {
        await setTokenForSetor(mappedSetor, token);
        localStorage.setItem(`setor_${module}`, mappedSetor);
      }
    }
  
    // Manter compatibilidade temporária com token global (criptografado)
    // TODO: Remover quando todos os módulos estiverem usando tokenManager exclusivamente
    const existingGlobal = localStorage.getItem('x-access-token');
    if (!existingGlobal || !existingGlobal.startsWith('enc_')) {
      // Só atualiza se não existir ou não estiver criptografado
      localStorage.setItem('x-access-token', encryptedToken);
    }
  } catch (error) {
    secureLog('tokenManager: Erro ao armazenar token', { module, error: error.message });
  }
};

/**
 * Recupera todos os tokens de um setor específico
 * SEGURANÇA: Tokens são descriptografados e validados antes de retornar
 * @param {string} setor - Nome do setor
 * @returns {Promise<string[]>} - Array de tokens válidos ou array vazio
 */
export const getTokensForSetor = async (setor) => {
  if (!setor) return [];
  
  const tokensKey = `tokens_setor_${setor}`;
  const tokensJson = localStorage.getItem(tokensKey);
  
  if (tokensJson) {
    try {
      const encryptedTokens = JSON.parse(tokensJson);
      // Descriptografa e valida todos os tokens
      const decryptedTokens = await Promise.all(
        encryptedTokens.map(async (encrypted) => {
          const token = await decryptToken(encrypted);
          if (token && validateTokenExpiration(token)) {
            return token;
          }
          return null;
        })
      );
      // Remove tokens nulos (inválidos ou expirados)
      return decryptedTokens.filter(Boolean);
    } catch (e) {
      secureLog('tokenManager: Erro ao parsear tokens do setor', { setor });
      return [];
    }
  }
  
  // Fallback: tenta obter token único do setor
  const tokenKey = `token_setor_${setor}`;
  const encryptedToken = localStorage.getItem(tokenKey);
  if (encryptedToken) {
    const token = await decryptToken(encryptedToken);
    if (token && validateTokenExpiration(token)) {
      return [token];
    }
  }
  
  return [];
};

/**
 * Recupera o token mais recente de um setor específico
 * SEGURANÇA: Token é descriptografado e validado antes de retornar
 * @param {string} setor - Nome do setor
 * @returns {Promise<string|null>} - Token mais recente válido ou null
 */
export const getTokenForSetor = async (setor) => {
  if (!setor) return null;
  
  const tokens = await getTokensForSetor(setor);
  // Retorna o último token (mais recente)
  if (tokens.length > 0) {
    return tokens[tokens.length - 1];
  }
  
  // Fallback: token único do setor
  const tokenKey = `token_setor_${setor}`;
  const encryptedToken = localStorage.getItem(tokenKey);
  if (encryptedToken) {
    const token = await decryptToken(encryptedToken);
    if (token && validateTokenExpiration(token)) {
      return token;
    }
  }
  
  return null;
};

/**
 * Recupera o token de um módulo específico
 * SEGURANÇA: Token é descriptografado e validado antes de retornar
 * @param {string} module - Nome do módulo
 * @returns {Promise<string|null>} - Token válido ou null se não encontrado/expirado
 */
export const getTokenForModule = async (module) => {
  if (!module) return null;
  
  // 1. Tenta obter token do módulo diretamente (Mais específico)
  const tokenKey = `token_${module}`;
  const encryptedToken = localStorage.getItem(tokenKey);
  if (encryptedToken) {
    const token = await decryptToken(encryptedToken);
    if (token && validateTokenExpiration(token)) {
      return token;
    }
  }

  // 2. Tenta obter token do setor correspondente ao módulo
  const setores = getSetoresForModule(module);
  for (const setor of setores) {
    const setorToken = await getTokenForSetor(setor);
    if (setorToken) return setorToken;
  }

  // 3. Fallback: Tenta obter o token global (Universal/Legado)
  const globalEncrypted = localStorage.getItem('x-access-token');
  if (globalEncrypted) {
    const globalToken = await decryptToken(globalEncrypted);
    if (globalToken && validateTokenExpiration(globalToken)) {
      return globalToken;
    }
  }
  
  return null;
};

/**
 * Recupera os setores de um módulo específico
 * @param {string} module - Nome do módulo
 * @returns {string[]} - Array de setores ou array vazio
 */
export const getSetoresForModule = (module) => {
  const moduleSetores = (() => {
    if (!module) return [];
    try { return JSON.parse(localStorage.getItem(`setores_${module}`) || '[]'); } catch(e) { return []; }
  })();

  const globalSetores = (() => {
    try { return JSON.parse(localStorage.getItem('integra_setores') || '[]'); } catch(e) { return []; }
  })();
  
  const userSetores = (() => {
    try {
      const user = JSON.parse(localStorage.getItem('integra_user') || '{}');
      return user.setores || [];
    } catch(e) { return []; }
  })();

  // Retorna a união de todas as fontes de setores identificadas, PRIORIZANDO os do módulo
  return [...new Set([...moduleSetores, ...globalSetores, ...userSetores])];
};

/**
 * Recupera o setor de um módulo específico (legado/compatibilidade)
 * @param {string} module - Nome do módulo
 * @returns {string|null} - Setor ou null se não encontrado
 */
export const getSetorForModule = (module) => {
  const setores = getSetoresForModule(module);
  if (setores.length === 0) return null;
  
  // Se o setor mapeado para o módulo estiver na lista, prefere ele (ex: Financeiro para financeiro-v2)
  const mappedSetor = getSetorFromModule(module);
  if (mappedSetor && setores.includes(mappedSetor)) {
    return mappedSetor;
  }
  
  // Senão retorna o primeiro da lista priorizada
  return setores[0];
};

/**
 * Obtém o token do módulo atual (baseado na URL)
 * SEGURANÇA: Token é descriptografado e validado antes de retornar
 * @returns {Promise<string|null>} - Token válido ou null se não encontrado/expirado
 */
export const getCurrentModuleToken = async () => {
  const module = getModuleFromPath();
  if (!module) return null;
  return await getTokenForModule(module);
};

/**
 * Obtém o setor do módulo atual (baseado na URL)
 * @returns {string|null} - Setor ou null se não encontrado
 */
export const getCurrentModuleSetor = () => {
  const module = getModuleFromPath();
  if (!module) return null;
  return getSetorForModule(module);
};

/**
 * Obtém token e setor do módulo atual
 * Usa o token mais recente do setor correspondente ao módulo
 * SEGURANÇA: Token é descriptografado e validado antes de retornar
 * @returns {Promise<{token: string|null, setor: string|null}>} - Objeto com token e setor
 */
export const getCurrentModuleAuth = async () => {
  const module = getModuleFromPath();
  if (!module) {
    return { token: null, setor: null };
  }
  
  // Obtém setor do módulo
  const setor = getSetorForModule(module);
  
  // Se houver setor, obtém o token mais recente desse setor
  let token = null;
  if (setor) {
    token = await getTokenForSetor(setor);
  }
  
  // Fallback: tenta obter token do módulo diretamente
  if (!token) {
    token = await getTokenForModule(module);
  }
  
  return {
    token,
    setor: setor || null,
  };
};

/**
 * Limpa token de um setor específico
 * @param {string} setor - Nome do setor
 * @param {string} token - Token específico a remover (opcional, remove todos se não fornecido)
 */
export const clearTokenForSetor = async (setor, token = null) => {
  if (!setor) return;
  
  if (token) {
    // Remove token específico do array
    const tokens = await getTokensForSetor(setor);
    const filtered = tokens.filter(t => t !== token);
    if (filtered.length > 0) {
      // Re-criptografa tokens restantes
      const encryptedTokens = await Promise.all(
        filtered.map(t => encryptToken(t))
      );
      localStorage.setItem(`tokens_setor_${setor}`, JSON.stringify(encryptedTokens));
    } else {
      localStorage.removeItem(`tokens_setor_${setor}`);
      localStorage.removeItem(`token_setor_${setor}`);
    }
  } else {
    // Remove todos os tokens do setor
    localStorage.removeItem(`tokens_setor_${setor}`);
    localStorage.removeItem(`token_setor_${setor}`);
  }
};

/**
 * Limpa token e setor de um módulo específico
 * @param {string} module - Nome do módulo
 */
export const clearModuleToken = async (module) => {
  if (!module) return;
  
  const tokenKey = `token_${module}`;
  const setorKey = `setor_${module}`;
  const setor = localStorage.getItem(setorKey);
  
  localStorage.removeItem(tokenKey);
  localStorage.removeItem(setorKey);
  
  // Limpa também tokens e lista de setores do setor correspondente
  if (setor) {
    await clearTokenForSetor(setor);
  }
  localStorage.removeItem(`setores_${module}`);
};

/**
 * Limpa todos os tokens e setores armazenados
 * Também limpa tokens expirados automaticamente
 */
export const clearAllTokens = async () => {
  // Limpa tokens expirados primeiro
  const removed = cleanExpiredTokens();
  if (removed > 0) {
    secureLog(`tokenManager: ${removed} tokens expirados removidos`);
  }
  
  // Limpar tokens por módulo
  await Promise.all(
    Object.keys(MODULE_TO_SETOR).map(module => clearModuleToken(module))
  );
  
  // Limpar chaves globais
  localStorage.removeItem('x-access-token');
  localStorage.removeItem('integra_setores');
  
  // Limpa também tokens por setor
  if (typeof window !== 'undefined') {
    const keysToRemove = [];
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key && (key.startsWith('token_setor_') || key.startsWith('tokens_setor_'))) {
        keysToRemove.push(key);
      }
    }
    keysToRemove.forEach(key => localStorage.removeItem(key));
  }
};

/**
 * Obtém todos os módulos que possuem token armazenado
 * SEGURANÇA: Valida tokens antes de incluir módulo
 * @returns {Promise<string[]>} - Array com nomes dos módulos autenticados
 */
export const getAuthenticatedModules = async () => {
  const modules = [];
  for (const module of Object.keys(MODULE_TO_SETOR)) {
    const token = await getTokenForModule(module);
    if (token) {
      modules.push(module);
    }
  }
  return modules;
};

/**
 * Obtém todos os setores que possuem tokens armazenados
 * @returns {string[]} - Array com nomes dos setores autenticados
 */
export const getAuthenticatedSetores = () => {
  const setores = [];
  Object.keys(localStorage).forEach(key => {
    if (key.startsWith('token_setor_') || key.startsWith('tokens_setor_')) {
      const setor = key.replace(/^(token|tokens)_setor_/, '');
      if (!setores.includes(setor)) {
        setores.push(setor);
      }
    }
  });
  return setores;
};

/**
 * Obtém informações sobre todos os tokens armazenados
 * @returns {Object} - Objeto com informações de tokens por setor
 */
export const getAllTokensInfo = () => {
  const info = {};
  const setores = getAuthenticatedSetores();
  
  setores.forEach(setor => {
    const tokens = getTokensForSetor(setor);
    info[setor] = {
      tokens: tokens,
      count: tokens.length,
      latest: tokens.length > 0 ? tokens[tokens.length - 1] : null
    };
  });
  
  return info;
};

/**
 * Remove um token específico de um setor
 * Útil para gerenciar múltiplos tokens
 * @param {string} setor - Nome do setor
 * @param {string} token - Token a ser removido
 * @returns {boolean} - True se o token foi removido com sucesso
 */
export const removeTokenFromSetor = (setor, token) => {
  if (!setor || !token) return false;
  
  const tokens = getTokensForSetor(setor);
  if (!tokens.includes(token)) return false;
  
  clearTokenForSetor(setor, token);
  return true;
};

/**
 * Adiciona um novo token para um setor
 * Útil para gerenciar múltiplos tokens
 * SEGURANÇA: Token é criptografado e validado antes de armazenar
 * @param {string} setor - Nome do setor
 * @param {string} token - Token a ser adicionado
 */
export const addTokenForSetor = async (setor, token) => {
  await setTokenForSetor(setor, token);
};

/**
 * Obtém todos os tokens do usuário (todos os setores)
 * SEGURANÇA: Tokens são descriptografados e validados
 * @returns {Promise<Array<{setor: string, tokens: string[], count: number}>>} - Array com tokens por setor
 */
export const getAllUserTokens = async () => {
  const setores = getAuthenticatedSetores();
  const result = [];
  
  for (const setor of setores) {
    const tokens = await getTokensForSetor(setor);
    result.push({
      setor,
      tokens,
      count: tokens.length
    });
  }
  
  return result;
};

/**
 * Armazena dados do usuário para um módulo específico
 * Permite múltiplos usuários (um por módulo) simultaneamente
 * SEGURANÇA: Dados são sanitizados antes de armazenar
 * @param {string} module - Nome do módulo
 * @param {Object} userData - Dados do usuário
 */
export const setUserForModule = (module, userData) => {
  if (!module || !userData) {
    secureLog('tokenManager: module e userData são obrigatórios');
    return;
  }
  
  // Sanitiza dados antes de armazenar
  const sanitizedData = sanitizeUserData(userData);
  
  const userKey = `integra_user_${module}`;
  try {
    localStorage.setItem(userKey, JSON.stringify(sanitizedData));
  } catch (e) {
    secureLog('tokenManager: Erro ao armazenar dados do usuário', { module, error: e.message });
    return;
  }
  
  // Gestão GLOBAL de Usuário (Somatória de permissões/ambientes, mas limpeza de setores)
  const currentGlobalJson = localStorage.getItem('integra_user');
  let finalUser = { ...sanitizedData };

  if (currentGlobalJson) {
    try {
      const currentGlobal = JSON.parse(currentGlobalJson);
      // Se for o mesmo usuário, mantém ambientes autorizados, mas prioriza setores atuais
      if (currentGlobal.email === finalUser.email) {
        // AMBIENTES: Mantém o que já tinha de outros módulos
        finalUser.authorizedEnvironments = {
          ...(currentGlobal.authorizedEnvironments || {}),
          ...(finalUser.authorizedEnvironments || {})
        };
        
        // SETORES: Se estamos vindo de um login fresco, os setores do sanitizedData são os mais confiáveis.
        // Se sanitizedData.setores for vazio, talvez manter os globais? 
        // No cenário do Prafrota, o sanitizedData.setores trará ["Financeiro"].
        if (!finalUser.setores || finalUser.setores.length === 0) {
           finalUser.setores = currentGlobal.setores || [];
        }
      }
    } catch (e) {
      console.warn('tokenManager: Erro ao mesclar usuário global');
    }
  }
  
  localStorage.setItem('integra_user', JSON.stringify(finalUser));
};

/**
 * Recupera dados do usuário de um módulo específico
 * @param {string} module - Nome do módulo
 * @returns {Object|null} - Dados do usuário ou null se não encontrado
 */
export const getUserForModule = (module) => {
  if (!module) return null;
  
  const userKey = `integra_user_${module}`;
  const userJson = localStorage.getItem(userKey);
  
  if (userJson) {
    try {
      return JSON.parse(userJson);
    } catch (e) {
      console.warn('tokenManager: Erro ao parsear usuário do módulo', module);
      return null;
    }
  }
  
  // Fallback: tenta obter usuário global
  const globalUser = localStorage.getItem('integra_user');
  if (globalUser) {
    try {
      return JSON.parse(globalUser);
    } catch (e) {
      return null;
    }
  }
  
  return null;
};

/**
 * Obtém o usuário do módulo atual (baseado na URL)
 * @returns {Object|null} - Dados do usuário ou null se não encontrado
 */
export const getCurrentModuleUser = () => {
  const module = getModuleFromPath();
  if (!module) {
    // Fallback para usuário global
    const globalUser = localStorage.getItem('integra_user');
    if (globalUser) {
      try {
        return JSON.parse(globalUser);
      } catch (e) {
        return null;
      }
    }
    return null;
  }
  
  return getUserForModule(module);
};

/**
 * Limpa dados do usuário de um módulo específico
 * @param {string} module - Nome do módulo
 */
export const clearUserForModule = (module) => {
  if (!module) return;
  
  const userKey = `integra_user_${module}`;
  localStorage.removeItem(userKey);
};

/**
 * Limpa todos os usuários armazenados
 */
export const clearAllUsers = () => {
  // Limpar usuários por módulo
  Object.keys(MODULE_TO_SETOR).forEach(module => {
    clearUserForModule(module);
  });
  
  // Limpar usuário global
  localStorage.removeItem('integra_user');
};

/**
 * Obtém todos os módulos que possuem usuário armazenado
 * @returns {string[]} - Array com nomes dos módulos com usuário autenticado
 */
export const getAuthenticatedUserModules = () => {
  return Object.keys(MODULE_TO_SETOR).filter(module => {
    return !!getUserForModule(module);
  });
};
