import React, { useState, useEffect } from 'react'; import { StyleSheet, TextInput, TouchableOpacity, ActivityIndicator } from 'react-native'; import { View, Text, useTheme } from '@bettoredge/styles'; import { Ionicons } from '@expo/vector-icons'; import type { CalcuttaEscrowProps } from '@bettoredge/types'; import { useCalcuttaEscrow } from '../hooks/useCalcuttaEscrow'; import { formatCurrency } from '../helpers/formatting'; export interface CalcuttaEscrowComponentProps { calcutta_competition_id: string; escrow?: CalcuttaEscrowProps; market_type: string; max_escrow?: number; player_balance?: number; onDepositFunds?: (amount: number) => void; onUpdate: (escrow: CalcuttaEscrowProps) => void; readOnly?: boolean; } type EscrowAction = 'transfer_in' | 'transfer_out'; const QUICK_AMOUNTS = [10, 25, 50, 100]; export const CalcuttaEscrow: React.FC = ({ calcutta_competition_id, escrow: initialEscrow, market_type, max_escrow, player_balance, onDepositFunds, onUpdate, readOnly, }) => { const { theme } = useTheme(); const { escrow: hookEscrow, loading, error, fetchEscrow, deposit, withdraw } = useCalcuttaEscrow(calcutta_competition_id); const [action, setAction] = useState('transfer_in'); const [amount, setAmount] = useState(''); const currentEscrow = hookEscrow ?? initialEscrow; useEffect(() => { if (!hookEscrow) { fetchEscrow(); } }, [hookEscrow, fetchEscrow]); const available = Number(currentEscrow?.escrow_balance ?? 0); const committed = Number(currentEscrow?.committed_balance ?? 0); const total = available + committed; // Budget cap calculations const netDeposited = currentEscrow ? Number(currentEscrow.total_deposited) - Number(currentEscrow.total_withdrawn) : 0; const budgetRemaining = max_escrow != null && Number(max_escrow) > 0 ? Math.max(0, Number(max_escrow) - netDeposited) : null; const budgetFullyDeposited = budgetRemaining !== null && budgetRemaining <= 0; const budgetPct = max_escrow != null && Number(max_escrow) > 0 ? Math.min(100, (netDeposited / Number(max_escrow)) * 100) : null; const committedPct = total > 0 ? (committed / total) * 100 : 0; const numericAmount = parseFloat(amount) || 0; const hasBalance = player_balance != null; const insufficientBalance = hasBalance && action === 'transfer_in' && numericAmount > Number(player_balance); const shortfall = insufficientBalance ? numericAmount - Number(player_balance) : 0; const exceedsBudget = action === 'transfer_in' && budgetRemaining !== null && numericAmount > budgetRemaining; const exceedsWithdraw = action === 'transfer_out' && numericAmount > available; const [actionError, setActionError] = useState(''); const isInvalidAmount = numericAmount <= 0 || exceedsBudget || exceedsWithdraw || (insufficientBalance && !onDepositFunds); const getValidationMessage = (): string => { if (numericAmount <= 0) return ''; if (exceedsBudget) return `Budget cap: ${formatCurrency(Number(max_escrow), market_type)}. You can deposit up to ${formatCurrency(budgetRemaining ?? 0, market_type)} more.`; if (exceedsWithdraw) return `You only have ${formatCurrency(available, market_type)} available to withdraw.`; if (insufficientBalance && !onDepositFunds) return `Insufficient wallet balance. You have ${formatCurrency(player_balance, market_type)}.`; return ''; }; const validationMessage = getValidationMessage(); const handleAction = async () => { if (isNaN(numericAmount) || numericAmount <= 0) return; setActionError(''); if (insufficientBalance && onDepositFunds) { onDepositFunds(Math.ceil(shortfall * 100) / 100); return; } try { let result; if (action === 'transfer_in') { result = await deposit(numericAmount); } else { result = await withdraw(numericAmount); } if (result) { onUpdate(result); setAmount(''); setActionError(''); } } catch (e: any) { setActionError(e?.message || 'Transfer failed. Please try again.'); } }; const handleQuickAmount = (amt: number) => { // Cap quick amount to budget remaining for deposits if (action === 'transfer_in' && budgetRemaining !== null) { setAmount(String(Math.min(amt, budgetRemaining))); } else if (action === 'transfer_out') { setAmount(String(Math.min(amt, available))); } else { setAmount(String(amt)); } }; // ============================================ // READ-ONLY: big KPI cards only // ============================================ if (readOnly) { return ( Available {formatCurrency(available, market_type)} Committed {formatCurrency(committed, market_type)} {/* Usage bar */} {formatCurrency(committed, market_type)} committed of {formatCurrency(total, market_type)} total ); } // ============================================ // INTERACTIVE: full escrow management // ============================================ return ( {/* KPI Cards */} Available {formatCurrency(available, market_type)} Committed {formatCurrency(committed, market_type)} {/* Usage bar */} {/* Wallet balance */} {hasBalance && ( Wallet Balance {formatCurrency(player_balance, market_type)} )} {/* Budget cap */} {budgetRemaining !== null && ( Budget Cap {budgetFullyDeposited ? 'Fully deposited' : `${formatCurrency(budgetRemaining, market_type)} remaining`} {formatCurrency(netDeposited, market_type)} of {formatCurrency(Number(max_escrow), market_type)} )} {/* Transfer In / Out toggle */} setAction('transfer_in')} activeOpacity={0.7} > Transfer In setAction('transfer_out')} activeOpacity={0.7} > Transfer Out {/* Quick amount buttons */} {QUICK_AMOUNTS.map(amt => ( handleQuickAmount(amt)} activeOpacity={0.7} > {formatCurrency(amt, market_type)} ))} {/* Amount input + submit */} $ {loading ? ( ) : ( <> {insufficientBalance ? 'Deposit & Transfer' : action === 'transfer_in' ? 'Transfer In' : 'Transfer Out'} )} {/* Validation message (budget cap, withdraw limit, etc.) */} {validationMessage ? ( {validationMessage} ) : null} {/* Insufficient wallet balance */} {insufficientBalance && !validationMessage && ( You need {formatCurrency(shortfall, market_type)} more in your wallet. Tap to deposit first. )} {/* API error */} {(error || actionError) && ( {actionError || error} )} ); }; const styles = StyleSheet.create({ container: { padding: 14, }, // KPI Cards kpiRow: { flexDirection: 'row', gap: 10, }, kpiCard: { flex: 1, borderRadius: 12, borderWidth: 1, padding: 14, alignItems: 'center', }, kpiIcon: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center', marginBottom: 8, }, kpiLabel: { marginBottom: 2, }, // Usage bar barWrap: { marginTop: 12, }, barTrack: { height: 6, borderRadius: 3, overflow: 'hidden', }, barFill: { height: 6, borderRadius: 3, }, barLabel: { marginTop: 4, textAlign: 'center', }, // Wallet walletRow: { flexDirection: 'row', alignItems: 'center', padding: 12, borderRadius: 10, borderWidth: 1, marginTop: 12, }, // Budget budgetWrap: { marginTop: 12, }, // Transfer toggle toggleRow: { flexDirection: 'row', marginTop: 16, }, toggleButton: { flex: 1, flexDirection: 'row', height: 48, borderRadius: 10, alignItems: 'center', justifyContent: 'center', }, // Quick amounts quickRow: { flexDirection: 'row', marginTop: 12, gap: 8, }, quickBtn: { flex: 1, height: 40, borderRadius: 8, borderWidth: 1, alignItems: 'center', justifyContent: 'center', }, // Input inputRow: { flexDirection: 'row', alignItems: 'center', marginTop: 12, gap: 10, }, inputWrap: { flex: 1, flexDirection: 'row', alignItems: 'center', height: 48, borderRadius: 10, borderWidth: 1, paddingHorizontal: 14, }, input: { flex: 1, fontSize: 18, lineHeight: 24, height: 48, padding: 0, }, submitBtn: { flexDirection: 'row', height: 48, paddingHorizontal: 16, borderRadius: 10, alignItems: 'center', justifyContent: 'center', }, // Warnings warningRow: { flexDirection: 'row', alignItems: 'center', padding: 12, borderRadius: 10, marginTop: 10, }, });