import React, { useState, useEffect } from 'react'; import { X, Check, ArrowLeftRight, DollarSign, Loader2, Edit, RefreshCcw, History, AlertTriangle } from 'lucide-react'; import { useAuth } from '../contexts/AuthContext'; import { useToast } from '../contexts/ToastContext'; import { Trade, TradeHistoryEntry, getTradeVersionHistory } from '../services/tradesService'; import { getUserCollection, getCardsByIds } from '../services/api'; import { Card } from '../types'; import TradeCreator from './TradeCreator'; interface TradeDetailProps { trade: Trade; onClose: () => void; onAccept: (tradeId: string) => Promise; onDecline: (tradeId: string) => Promise; onTradeUpdated: () => void; } interface TradeCardItem { card: Card; quantity: number; } interface CollectionItem { card: Card; quantity: number; } function calculateTotalPrice(items: TradeCardItem[]): number { return items.reduce((total, { card, quantity }) => { const price = card.prices?.usd ? parseFloat(card.prices.usd) : 0; return total + (price * quantity); }, 0); } export default function TradeDetail({ trade, onClose, onAccept, onDecline, onTradeUpdated, }: TradeDetailProps) { const { user } = useAuth(); const toast = useToast(); const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(false); const [senderCards, setSenderCards] = useState([]); const [receiverCards, setReceiverCards] = useState([]); const [showHistory, setShowHistory] = useState(false); const [history, setHistory] = useState([]); const [showEditMode, setShowEditMode] = useState(false); const [editReceiverCollection, setEditReceiverCollection] = useState([]); const isUser1 = trade.user1_id === user?.id; const isUser2 = trade.user2_id === user?.id; const otherUser = isUser1 ? trade.user2 : trade.user1; const myUserId = user?.id || ''; const otherUserId = isUser1 ? trade.user2_id : trade.user1_id; useEffect(() => { loadTradeCards(); loadTradeHistory(); }, [trade]); const loadTradeCards = async () => { setLoading(true); try { const allCardIds = trade.items?.map(item => item.card_id) || []; if (allCardIds.length === 0) { setSenderCards([]); setReceiverCards([]); return; } const cards = await getCardsByIds(allCardIds); const cardMap = new Map(); cards.forEach(card => cardMap.set(card.id, card)); const myItems: TradeCardItem[] = []; const theirItems: TradeCardItem[] = []; trade.items?.forEach(item => { const card = cardMap.get(item.card_id); if (!card) return; if (item.owner_id === myUserId) { myItems.push({ card, quantity: item.quantity }); } else { theirItems.push({ card, quantity: item.quantity }); } }); setSenderCards(myItems); setReceiverCards(theirItems); } catch (error) { console.error('Error loading trade cards:', error); toast.error('Failed to load trade details'); } finally { setLoading(false); } }; const loadTradeHistory = async () => { try { const historyData = await getTradeVersionHistory(trade.id); setHistory(historyData); } catch (error) { console.error('Error loading trade history:', error); } }; const handleAccept = async () => { setProcessing(true); try { await onAccept(trade.id); onClose(); } catch (error) { console.error('Error accepting trade:', error); } finally { setProcessing(false); } }; const handleDecline = async () => { setProcessing(true); try { await onDecline(trade.id); onClose(); } catch (error) { console.error('Error declining trade:', error); } finally { setProcessing(false); } }; const handleEdit = async () => { try { // Load the other user's collection for editing const collectionMap = await getUserCollection(otherUserId); const cardIds = Array.from(collectionMap.keys()); const cards = await getCardsByIds(cardIds); const collection = cards.map((card) => ({ card, quantity: collectionMap.get(card.id) || 0, })); setEditReceiverCollection(collection); setShowEditMode(true); } catch (error) { console.error('Error loading collection for edit:', error); toast.error('Failed to load collection'); } }; // In the symmetric model, counter-offer is the same as edit const handleCounterOffer = handleEdit; // senderCards = myCards, receiverCards = theirCards (already calculated correctly) const yourCards = senderCards; const theirCards = receiverCards; const yourPrice = calculateTotalPrice(yourCards); const theirPrice = calculateTotalPrice(theirCards); // For edit mode, pre-populate with current cards // In the symmetric model, both edit and counter-offer use the same perspective: // - Your cards (what you're offering) // - Their cards (what you want) // Include quantity in the card object so TradeCreator can preserve it const editInitialSenderCards = yourCards.map(c => ({ ...c.card, quantity: c.quantity })); const editInitialReceiverCards = theirCards.map(c => ({ ...c.card, quantity: c.quantity })); if (showEditMode) { return ( { setShowEditMode(false); onClose(); }} onTradeCreated={() => { setShowEditMode(false); onTradeUpdated(); onClose(); }} editMode={true} existingTradeId={trade.id} initialSenderCards={editInitialSenderCards} initialReceiverCards={editInitialReceiverCards} initialMessage={trade.message || ''} /> ); } return (
{/* Header */}

Trade Details {trade.version > 1 && `(v${trade.version})`}

With: {otherUser?.username}

{/* Content */}
{loading ? (
) : (
{/* Invalid Trade Warning */} {trade.status === 'pending' && !trade.is_valid && (

Trade No Longer Valid

One or more cards in this trade are no longer available in the required quantities. This trade cannot be accepted until it is updated.

)}
{/* Your Side */}

You Give

{yourPrice.toFixed(2)}
{yourCards.length === 0 ? (

Gift (no cards)

) : (
{yourCards.map((item, idx) => (
{item.card.name} {item.quantity > 1 && (
x{item.quantity}
)} {item.card.prices?.usd && (
${item.card.prices.usd}
)}
))}
)}
{/* Their Side */}

You Receive

{theirPrice.toFixed(2)}
{theirCards.length === 0 ? (

Gift (no cards)

) : (
{theirCards.map((item, idx) => (
{item.card.name} {item.quantity > 1 && (
x{item.quantity}
)} {item.card.prices?.usd && (
${item.card.prices.usd}
)}
))}
)}
{/* Message */} {trade.message && (

Message:

{trade.message}

)} {/* Price Difference */} {!loading && (yourPrice > 0 || theirPrice > 0) && (
Value Difference: 5 ? 'text-yellow-400' : 'text-gray-300'}> ${Math.abs(yourPrice - theirPrice).toFixed(2)} {yourPrice > theirPrice ? ' in your favor' : yourPrice < theirPrice ? ' in their favor' : ' (balanced)'}
)} {/* History */} {history.length > 0 && (
{showHistory && (
{history.map((entry) => (
Version {entry.version} Edited by {entry.editor?.username} • {new Date(entry.created_at).toLocaleDateString()}
{entry.message && (

{entry.message}

)}
))}
)}
)}
)}
{/* Actions - Only for pending trades */} {trade.status === 'pending' && !loading && (
{/* Only the user who DIDN'T make the last edit can respond */} {trade.editor_id && trade.editor_id !== user?.id ? ( /* User receives the last edit - can accept/decline/counter */ <>
) : trade.editor_id === user?.id ? ( /* User made the last edit - can still edit while waiting for response */ <>

Waiting for {otherUser?.username} to respond...

) : ( /* No editor yet (initial trade) */ <> {isUser1 ? ( /* User1 (initiator) can edit their initial offer */ ) : ( /* User2 (partner) can accept/decline/counter */ <>
)} )}
)}
); }