import React, { useState } from 'react' import { useParams, useNavigate, Link } from 'react-router-dom' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { ArrowLeft, Trash2, ChevronDown, ChevronUp, ExternalLink, Swords, Zap, Sparkles, Shield, Cog, Star, Mountain, X } from 'lucide-react' import { decksApi } from '@/api' import { Button, Badge, Spinner, Panel, EmptyState } from '@/components/ui' import type { Deck, DeckCard, CardSlot } from '@/types' // ─── Slot metadata ──────────────────────────────────────────────────────────── const SLOT_META: Record = { creature: { label: 'Creatures', icon: , color: 'text-green-400' }, instant: { label: 'Instants', icon: , color: 'text-blue-400' }, sorcery: { label: 'Sorceries', icon: , color: 'text-purple-400' }, enchantment: { label: 'Enchantments', icon: , color: 'text-yellow-400' }, artifact: { label: 'Artifacts', icon: , color: 'text-slate-400' }, planeswalker: { label: 'Planeswalkers', icon: , color: 'text-pink-400' }, land: { label: 'Lands', icon: , color: 'text-amber-600' }, battle: { label: 'Battles', icon: , color: 'text-red-400' }, } const SLOT_ORDER: CardSlot[] = [ 'creature', 'instant', 'sorcery', 'enchantment', 'artifact', 'planeswalker', 'land', 'battle', ] // ─── Card image helper ──────────────────────────────────────────────────────── function cardImageUrl(card: DeckCard, size: 'small' | 'normal' = 'small'): string | null { const sf = card.scryfall_data if (!sf) return null const uris = sf.image_uris if (uris) return uris[size] ?? null const faces = sf.card_faces if (faces?.length) return faces[0].image_uris?.[size] ?? null return null } // ─── Reasoning drawer ───────────────────────────────────────────────────────── function ReasoningDrawer({ card, onClose, }: { card: DeckCard onClose: () => void }) { const img = cardImageUrl(card, 'normal') return (
{img && ( {card.card_name} )}

{card.card_name}

{card.scryfall_data?.type_line && (

{card.scryfall_data.type_line}

)}
{card.is_owned ? 'Owned' : 'Unowned'} {SLOT_META[card.slot]?.label ?? card.slot} {card.scryfall_data?.prices?.usd && ( ${card.scryfall_data.prices.usd} )}
{card.ai_reasoning && (
{card.ai_reasoning}
)}
{card.scryfall_id && ( )}
) } // ─── Card tile ──────────────────────────────────────────────────────────────── function CardTile({ card, onClick }: { card: DeckCard; onClick: () => void }) { const img = cardImageUrl(card, 'small') const price = card.scryfall_data?.prices?.usd return ( ) } // ─── Slot group ─────────────────────────────────────────────────────────────── function SlotGroup({ slot, cards }: { slot: CardSlot; cards: DeckCard[] }) { const [open, setOpen] = useState(true) const [selectedCard, setSelectedCard] = useState(null) const meta = SLOT_META[slot] const totalCount = cards.reduce((s, c) => s + (c.quantity || 1), 0) return (
{open && (
{cards.map((card) => ( setSelectedCard(card)} /> ))}
)} {selectedCard && ( setSelectedCard(null)} /> )}
) } // ─── Cuts section ───────────────────────────────────────────────────────────── function CutsSection({ cuts }: { cuts: { name: string; reasoning: string }[] }) { const [open, setOpen] = useState(false) if (!cuts.length) return null return ( {open && (
{cuts.map((cut, i) => (
#{i + 1}

{cut.name}

{cut.reasoning}

))}
)}
) } // ─── Main page ──────────────────────────────────────────────────────────────── export function DeckViewPage() { const { id } = useParams<{ id: string }>() const navigate = useNavigate() const queryClient = useQueryClient() const { data: deck, isLoading, error } = useQuery({ queryKey: ['deck', id], queryFn: async () => (await decksApi.get(Number(id))).data, enabled: !!id, }) const deleteMutation = useMutation({ mutationFn: () => decksApi.delete(Number(id)), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['decks'] }) navigate('/decks') }, }) if (isLoading) { return (
) } if (error || !deck) { return (
) } // Group cards by slot (excluding commander) const nonCommanderCards = deck.cards.filter((c) => !c.is_commander) const commander = deck.cards.find((c) => c.is_commander) const bySlot = SLOT_ORDER.reduce>( (acc, slot) => { acc[slot] = nonCommanderCards.filter((c) => c.slot === slot) return acc }, {} as Record ) const ownedCount = nonCommanderCards.filter((c) => c.is_owned).length const totalNonBasics = nonCommanderCards.filter((c) => c.slot !== 'land').length const totalCards = nonCommanderCards.reduce((s, c) => s + (c.quantity || 1), 0) const cuts = deck.ai_reasoning?.cuts ?? [] const unresolved = deck.ai_reasoning?.unresolved_cards ?? [] return (
{/* Header */}

{deck.name}

{deck.mode} {deck.commander} {deck.playstyle && ( <> {deck.playstyle} )}
{/* Stats bar */}
{[ { label: 'Total cards', value: totalCards + 1 }, { label: 'Owned', value: `${ownedCount}/${totalCards}` }, { label: 'Non-lands', value: totalNonBasics }, ].map((stat) => (

{stat.value}

{stat.label}

))}
{/* Strategy summary */} {deck.ai_reasoning?.strategy_summary && (

Strategy

{deck.ai_reasoning.strategy_summary}

)} {/* Commander */} {commander && (

Commander

{commander.card_name} {commander.is_owned ? 'Owned' : 'Unowned'}
)} {/* Unresolved cards warning */} {unresolved.length > 0 && (

Unresolved cards ({unresolved.length})

These card names could not be verified on Scryfall:{' '} {unresolved.join(', ')}

)} {/* Card grid by slot */}
{SLOT_ORDER.map((slot) => { const cards = bySlot[slot] if (!cards.length) return null return })}
{/* Cuts */} {cuts.length > 0 && }
) }