import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react'; import { BrowserProvider, formatEther } from 'ethers'; import type { AuthState, WalletInfo, PortalUser, UserRole, Permission } from '../types/portal'; interface AuthContextType extends AuthState { connectWallet: (provider: 'metamask' | 'walletconnect' | 'coinbase') => Promise; disconnect: () => void; error: string | null; } const AuthContext = createContext(null); const ROLE_PERMISSIONS: Record = { admin: [ 'accounts.view', 'accounts.manage', 'accounts.create', 'transactions.view', 'transactions.create', 'transactions.approve', 'transactions.execute', 'treasury.view', 'treasury.manage', 'treasury.rebalance', 'compliance.view', 'compliance.manage', 'compliance.override', 'reports.view', 'reports.generate', 'reports.export', 'settlements.view', 'settlements.approve', 'admin.users', 'admin.settings', 'admin.audit', ], treasurer: [ 'accounts.view', 'accounts.manage', 'transactions.view', 'transactions.create', 'transactions.approve', 'treasury.view', 'treasury.manage', 'treasury.rebalance', 'reports.view', 'reports.generate', 'reports.export', 'settlements.view', 'settlements.approve', ], analyst: [ 'accounts.view', 'transactions.view', 'treasury.view', 'reports.view', 'reports.generate', 'settlements.view', ], compliance_officer: [ 'accounts.view', 'transactions.view', 'treasury.view', 'compliance.view', 'compliance.manage', 'reports.view', 'reports.generate', 'reports.export', 'settlements.view', ], auditor: [ 'accounts.view', 'transactions.view', 'treasury.view', 'compliance.view', 'reports.view', 'reports.export', 'settlements.view', 'admin.audit', ], viewer: ['accounts.view', 'transactions.view', 'treasury.view', 'reports.view', 'settlements.view'], }; const AUTH_STORAGE_KEY = 'solace-auth'; function generateUser(address: string): PortalUser { return { id: `usr-${address.slice(2, 10)}`, displayName: `${address.slice(0, 6)}...${address.slice(-4)}`, role: 'admin', permissions: ROLE_PERMISSIONS['admin'], institution: 'Solace Bank Group PLC', department: 'Treasury Operations', lastLogin: new Date(), walletAddress: address, }; } export function AuthProvider({ children }: { children: ReactNode }) { const [state, setState] = useState({ isAuthenticated: false, wallet: null, user: null, loading: true, }); const [error, setError] = useState(null); useEffect(() => { const saved = localStorage.getItem(AUTH_STORAGE_KEY); if (saved) { try { const parsed = JSON.parse(saved); setState({ isAuthenticated: true, wallet: parsed.wallet, user: { ...parsed.user, lastLogin: new Date(parsed.user.lastLogin) }, loading: false, }); return; } catch { /* ignore */ } } setState(prev => ({ ...prev, loading: false })); }, []); const connectWallet = useCallback(async (providerType: 'metamask' | 'walletconnect' | 'coinbase') => { setError(null); setState(prev => ({ ...prev, loading: true })); try { let address: string; let chainId: number; let balance: string; const ethereum = (window as unknown as Record).ethereum as { request: (args: { method: string; params?: unknown[] }) => Promise; isMetaMask?: boolean; isCoinbaseWallet?: boolean; chainId?: string; } | undefined; if (ethereum && (providerType === 'metamask' || providerType === 'coinbase')) { const accounts = await ethereum.request({ method: 'eth_requestAccounts' }) as string[]; if (!accounts || accounts.length === 0) throw new Error('No accounts returned'); const provider = new BrowserProvider(ethereum as never); const signer = await provider.getSigner(); address = await signer.getAddress(); const network = await provider.getNetwork(); chainId = Number(network.chainId); const bal = await provider.getBalance(address); balance = formatEther(bal); } else { // Demo mode — simulate wallet connection for environments without MetaMask await new Promise(resolve => setTimeout(resolve, 1200)); address = '0x' + Array.from({ length: 40 }, () => Math.floor(Math.random() * 16).toString(16)).join(''); chainId = 1; balance = (Math.random() * 100).toFixed(4); } const wallet: WalletInfo = { address, chainId, balance, provider: providerType }; const user = generateUser(address); const newState: AuthState = { isAuthenticated: true, wallet, user, loading: false }; setState(newState); localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify({ wallet, user })); } catch (err) { const msg = err instanceof Error ? err.message : 'Failed to connect wallet'; setError(msg); setState(prev => ({ ...prev, loading: false })); } }, []); const disconnect = useCallback(() => { setState({ isAuthenticated: false, wallet: null, user: null, loading: false }); localStorage.removeItem(AUTH_STORAGE_KEY); }, []); return ( {children} ); } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth must be used within AuthProvider'); return ctx; }