import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react'; import { User, UserRole } from '../types'; // Mock Users Database const MOCK_USERS: User[] = [ { id: 'superadmin-1', name: 'Dev Admin', email: 'admin@photum.com', role: UserRole.SUPERADMIN, avatar: 'https://i.pravatar.cc/150?u=admin' }, { id: 'owner-1', name: 'PHOTUM CEO', email: 'empresa@photum.com', role: UserRole.BUSINESS_OWNER, avatar: 'https://i.pravatar.cc/150?u=ceo', allowedRegions: ['SP', 'MG'] }, { id: 'owner-sp', name: 'ADMIN SP', email: 'admin.sp@photum.com', role: UserRole.BUSINESS_OWNER, avatar: 'https://i.pravatar.cc/150?u=sp', allowedRegions: ['SP'] }, { id: 'owner-mg', name: 'ADMIN MG', email: 'admin.mg@photum.com', role: UserRole.BUSINESS_OWNER, avatar: 'https://i.pravatar.cc/150?u=mg', allowedRegions: ['MG'] }, { id: 'photographer-1', name: 'COLABORADOR PHOTUM', email: 'foto@photum.com', role: UserRole.PHOTOGRAPHER, avatar: 'https://i.pravatar.cc/150?u=photo' }, { id: 'client-1', name: 'PARCEIRO PHOTUM', email: 'cliente@photum.com', role: UserRole.EVENT_OWNER, avatar: 'https://i.pravatar.cc/150?u=client' }, { id: 'viewer-1', name: 'VISUALIZADOR PHOTUM', email: 'viewer@photum.com', role: UserRole.AGENDA_VIEWER, avatar: 'https://i.pravatar.cc/150?u=viewer' } ]; interface AuthContextType { user: User | null; login: (email: string, password?: string) => Promise; logout: () => void; register: (data: { nome: string; email: string; senha: string; telefone: string; role: string; empresaId?: string; tipo_profissional?: string; regiao?: string }) => Promise<{ success: boolean; userId?: string; token?: string }>; availableUsers: User[]; // Helper for the login screen demo token: string | null; isLoading: boolean; } const AuthContext = createContext(undefined); export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [user, setUser] = useState(null); const [token, setToken] = useState(localStorage.getItem("token")); // Initial loading state depends on whether we have a token to verify const [isLoading, setIsLoading] = useState(!!localStorage.getItem("token")); useEffect(() => { const restoreSession = async () => { if (!token) { setIsLoading(false); return; } setIsLoading(true); try { const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; const response = await fetch(`${API_URL}/api/me`, { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const data = await response.json(); const backendUser = data.user; console.log("AuthContext: restoreSession raw user:", backendUser); const mappedUser: User = { id: backendUser.id, email: backendUser.email, name: data.profissional?.nome || data.empresa?.nome || backendUser.name || backendUser.nome || backendUser.email.split('@')[0], phone: backendUser.phone || backendUser.telefone || backendUser.whatsapp, // Map phone role: backendUser.role as UserRole, ativo: backendUser.ativo, empresaId: backendUser.company_id || backendUser.empresa_id || backendUser.companyId, companyName: backendUser.company_name || backendUser.empresa_nome || backendUser.companyName, avatar: data.profissional?.avatar_url || data.empresa?.avatar_url || backendUser.avatar, allowedRegions: backendUser.allowed_regions || [], // Map allowed regions // Client specific fields cpf_cnpj: backendUser.cpf_cnpj, cep: backendUser.cep, endereco: backendUser.endereco, numero: backendUser.numero, complemento: backendUser.complemento, bairro: backendUser.bairro, cidade: backendUser.cidade, estado: backendUser.estado, professionalId: data.profissional?.id, // Map professional ID functions: data.profissional?.functions || [], }; console.log("AuthContext: restoreSession mapped user:", mappedUser); if (!backendUser.ativo) { console.warn("User is not active, logging out."); logout(); return; } setUser(mappedUser); } else { // Token invalid or expired logout(); } } catch (err) { console.error("Session restore error:", err); logout(); } finally { setIsLoading(false); } }; if (token) { restoreSession(); } else { setIsLoading(false); } }, [token]); // removed 'user' from dependency to avoid loop if user is set, though !user check handles it. safer to just depend on token mount. const getErrorMessage = (errorKey: string): string => { // Map backend error messages to Portuguese const errorMap: { [key: string]: string } = { "email already registered": "Este e-mail já está cadastrado.", "invalid credentials": "E-mail ou senha incorretos.", "user not found": "Usuário não encontrado.", "crypto/bcrypt: hashedPassword is not the hash of the given password": "Senha incorreta.", "password is too short": "A senha é muito curta.", "cpf already registered": "CPF já cadastrado.", }; // Check for partial matches or exact matches if (errorMap[errorKey]) return errorMap[errorKey]; // Fallbacks if (errorKey.includes('hashedPassword')) return "Senha incorreta."; if (errorKey.includes('email')) return "Erro relacionado ao e-mail."; if (errorKey.includes('password')) return "Erro relacionado à senha."; return "Ocorreu um erro inesperado. Tente novamente."; }; const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; const login = async (email: string, password?: string) => { // 1. Try Real API first try { const response = await fetch(`${API_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, senha: password }) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(getErrorMessage(errorData.error || 'Falha no login')); } const data = await response.json(); const backendUser = data.user; // Enforce active check if (!backendUser.ativo) { throw new Error("Cadastro pendente de aprovação."); } // Store token (optional, if you need it for other requests outside cookies) localStorage.setItem('token', data.access_token); setToken(data.access_token); // Map backend user to frontend User type const mappedUser: User = { id: backendUser.id, email: backendUser.email, name: data.profissional?.nome || data.empresa?.nome || backendUser.name || backendUser.nome || backendUser.email.split('@')[0], phone: backendUser.phone || backendUser.telefone || backendUser.whatsapp, // Map phone role: backendUser.role as UserRole, ativo: backendUser.ativo, empresaId: backendUser.company_id || backendUser.empresa_id || backendUser.companyId, companyName: backendUser.company_name || backendUser.empresa_nome || backendUser.companyName, avatar: data.profissional?.avatar_url || data.empresa?.avatar_url || backendUser.avatar, allowedRegions: backendUser.allowed_regions || [], // Client specific fields cpf_cnpj: backendUser.cpf_cnpj, cep: backendUser.cep, endereco: backendUser.endereco, numero: backendUser.numero, complemento: backendUser.complemento, bairro: backendUser.bairro, cidade: backendUser.cidade, estado: backendUser.estado, professionalId: data.profissional?.id, // Map professional ID functions: data.profissional?.functions || [], }; setUser(mappedUser); return true; } catch (err) { console.error('Login error:', err); // 2. Fallback to Demo/Mock users if API fails const mockUser = MOCK_USERS.find(u => u.email === email); if (mockUser) { console.log('Using demo user:', mockUser.email); await new Promise(resolve => setTimeout(resolve, 500)); // Simulate delay setUser({ ...mockUser, ativo: true }); return true; } throw err; } }; const logout = async () => { try { if (token) { await fetch(`${import.meta.env.VITE_API_URL}/auth/logout`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` // Though refresh token is in cookie }, // body: JSON.stringify({ refresh_token: ... }) // If we had it in client state, but it is in cookie. }); } } catch (error) { console.error("Logout failed", error); } finally { localStorage.removeItem('token'); setToken(null); setUser(null); // Force redirect or let app router handle it safely window.location.href = '/'; } }; const register = async (data: any, autoLogin: boolean = true, skipLoading: boolean = false) => { if (!skipLoading) setIsLoading(true); try { const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8080"; const payload = { email: data.email, senha: data.password || data.senha, role: data.role, nome: data.name || data.nome, telefone: data.phone || data.telefone, professional_type: data.professional_type || data.tipo_profissional, empresa_id: data.company_id || data.empresaId || data.empresa_id, regiao: data.regiao, cpf_cnpj: data.cpf_cnpj, cep: data.cep, endereco: data.endereco, numero: data.numero, complemento: data.complemento, bairro: data.bairro, cidade: data.cidade, estado: data.estado }; const response = await fetch(`${API_URL}/auth/register`, { method: "POST", headers: { "Content-Type": "application/json", "x-regiao": data.regiao || 'SP' }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); const rawError = errorData.error || 'Falha no cadastro'; throw new Error(getErrorMessage(rawError)); } const responseData = await response.json(); if (responseData.user && responseData.user.ativo) { if (autoLogin) { if (responseData.access_token) { localStorage.setItem('token', responseData.access_token); setToken(responseData.access_token); } } const backendUser = responseData.user; const mappedUser: User = { id: backendUser.id, email: backendUser.email, name: backendUser.nome || backendUser.email.split('@')[0], phone: backendUser.phone || backendUser.telefone || backendUser.whatsapp, // Map phone role: backendUser.role as UserRole, ativo: backendUser.ativo, empresaId: backendUser.company_id || backendUser.empresa_id || backendUser.companyId, companyName: backendUser.company_name || backendUser.empresa_nome || backendUser.companyName, avatar: backendUser.avatar, allowedRegions: backendUser.allowed_regions || [], // Client specific fields cpf_cnpj: backendUser.cpf_cnpj, cep: backendUser.cep, endereco: backendUser.endereco, numero: backendUser.numero, complemento: backendUser.complemento, bairro: backendUser.bairro, cidade: backendUser.cidade, estado: backendUser.estado, functions: backendUser.functions || [], }; if (autoLogin) { setUser(mappedUser); } } return { success: true, userId: responseData.user?.id || responseData.userId, token: responseData.access_token }; } catch (err) { console.error('Registration error:', err); if (err instanceof Error) { return { success: false, error: err.message }; } return { success: false, error: "Erro desconhecido" }; } finally { if (!skipLoading) setIsLoading(false); } }; return ( {children} ); }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) throw new Error('useAuth must be used within an AuthProvider'); return context; };