import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'; import { supabase } from '@/lib/supabase'; import { setCurrentUserId } from '@/lib/p2p-fiat'; // Telegram WebApp types declare global { interface Window { Telegram?: { WebApp: { initData: string; initDataUnsafe: { user?: { id: number; first_name: string; last_name?: string; username?: string; language_code?: string; photo_url?: string; }; auth_date: number; hash: string; }; ready: () => void; expand: () => void; close: () => void; MainButton: { text: string; show: () => void; hide: () => void; onClick: (callback: () => void) => void; }; HapticFeedback: { impactOccurred: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => void; notificationOccurred: (type: 'error' | 'success' | 'warning') => void; selectionChanged: () => void; }; }; }; } } interface TelegramUser { id: number; first_name: string; last_name?: string; username?: string; photo_url?: string; } interface User { id: string; // Supabase user ID telegram_id: number; telegram_username?: string; display_name: string; avatar_url?: string; wallet_address?: string; created_at: string; } interface AuthContextType { user: User | null; telegramUser: TelegramUser | null; isLoading: boolean; isAuthenticated: boolean; error: string | null; login: () => Promise; logout: () => void; linkWallet: (address: string) => Promise; } const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const [telegramUser, setTelegramUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Get Telegram user from WebApp const getTelegramUser = useCallback((): TelegramUser | null => { const tg = window.Telegram?.WebApp; if (!tg?.initDataUnsafe?.user) { return null; } return tg.initDataUnsafe.user; }, []); // Login with Telegram const login = useCallback(async () => { setIsLoading(true); setError(null); try { const tg = window.Telegram?.WebApp; if (!tg?.initData) { throw new Error('Telegram WebApp not available. Open from Telegram.'); } // Call Supabase Edge Function to verify initData and get/create user const { data, error: fnError } = await supabase.functions.invoke('telegram-auth', { body: { initData: tg.initData } }); if (fnError) throw fnError; if (!data?.user) { throw new Error('Authentication failed'); } setUser(data.user); // Use auth_user_id for P2P operations (balance queries, etc.) // This is the auth.users ID used by p2p_deposit_withdraw_requests FK const p2pUserId = data.auth_user_id || data.user.id; setCurrentUserId(p2pUserId); setTelegramUser(getTelegramUser()); // Store session token and auth_user_id if provided if (data.session_token) { localStorage.setItem('p2p_session', data.session_token); } if (data.auth_user_id) { localStorage.setItem('p2p_auth_user_id', data.auth_user_id); } window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); } catch (err) { const message = err instanceof Error ? err.message : 'Login failed'; setError(message); window.Telegram?.WebApp.HapticFeedback.notificationOccurred('error'); console.error('Login error:', err); } finally { setIsLoading(false); } }, [getTelegramUser]); // Logout const logout = useCallback(() => { setUser(null); setCurrentUserId(null); // Clear user ID for p2p-fiat functions localStorage.removeItem('p2p_session'); window.Telegram?.WebApp.HapticFeedback.impactOccurred('medium'); }, []); // Link wallet address const linkWallet = useCallback(async (address: string) => { if (!user) throw new Error('Not authenticated'); const { error: updateError } = await supabase .from('p2p_users') .update({ wallet_address: address }) .eq('telegram_id', user.telegram_id); if (updateError) throw updateError; setUser(prev => prev ? { ...prev, wallet_address: address } : null); window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); }, [user]); // Login via URL params (from mini-app redirect with session_token) const loginViaParams = useCallback(async () => { const params = new URLSearchParams(window.location.search); const sessionToken = params.get('session_token'); const from = params.get('from'); // Check if coming from miniapp with session_token if (!sessionToken || from !== 'miniapp') { return false; } setIsLoading(true); try { // Verify session token with backend const { data, error: fnError } = await supabase.functions.invoke('telegram-auth', { body: { sessionToken } }); if (fnError) throw fnError; if (!data?.user) { throw new Error('Authentication failed'); } setUser(data.user); // Use auth_user_id for P2P operations const p2pUserId = data.auth_user_id || data.user.id; setCurrentUserId(p2pUserId); // Store session token and auth_user_id if (data.session_token) { localStorage.setItem('p2p_session', data.session_token); } if (data.auth_user_id) { localStorage.setItem('p2p_auth_user_id', data.auth_user_id); } // Clear URL params after successful login window.history.replaceState({}, '', window.location.pathname); return true; } catch (err) { console.error('Session token login error:', err); return false; } finally { setIsLoading(false); } }, []); // Auto-login on mount useEffect(() => { const initAuth = async () => { const tg = window.Telegram?.WebApp; // Check for existing session first const sessionToken = localStorage.getItem('p2p_session'); const storedAuthUserId = localStorage.getItem('p2p_auth_user_id'); if (sessionToken) { try { const { data, error } = await supabase.functions.invoke('telegram-auth', { body: { sessionToken } }); if (!error && data?.user) { setUser(data.user); // Use stored or returned auth_user_id for P2P operations const p2pUserId = data.auth_user_id || storedAuthUserId || data.user.id; setCurrentUserId(p2pUserId); if (data.auth_user_id) { localStorage.setItem('p2p_auth_user_id', data.auth_user_id); } setIsLoading(false); return; } } catch { localStorage.removeItem('p2p_session'); localStorage.removeItem('p2p_auth_user_id'); } } // Try Telegram WebApp auth if (tg?.initData) { tg.ready(); tg.expand(); setTelegramUser(getTelegramUser()); await login(); return; } // Try URL params auth (from mini-app redirect with session_token) const params = new URLSearchParams(window.location.search); if (params.get('from') === 'miniapp' && params.get('session_token')) { const success = await loginViaParams(); if (success) return; } setIsLoading(false); }; initAuth(); }, [getTelegramUser, login, loginViaParams]); const value: AuthContextType = { user, telegramUser, isLoading, isAuthenticated: !!user, error, login, logout, linkWallet }; return ( {children} ); } export function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within AuthProvider'); } return context; }