"use client"; import { useMemo, useState } from "react"; import PositionBadge from "@/components/PositionBadge"; import Image from "next/image"; type Player = { id: string; name: string; image: string | null; position: "GK" | "DEF" | "MID" | "FWD"; price: number; totalPoints: number; country: { name: string; code: string; flagUrl?: string | null; isEliminated?: boolean }; }; type TeamPlayer = { playerId: string; goldenCardId: string | null; isCaptain: boolean; isViceCaptain: boolean; isBench: boolean; positionIndex: number; player: Player; }; type Team = { id: string; name: string; budget: number; totalPoints: number; formation: string; status: string; players: TeamPlayer[]; } | null; type SpecialCard = { id: string; status: "SEALED" | "OPENED"; state: "IN_INVENTORY" | "IN_TEAM" | "SOLD"; acquiredDate: string; openedAt: string | null; player: Player; teamPlayer?: { playerId: string; teamId: string } | null; }; type ReplacementCandidate = { playerId: string; name: string; isBench: boolean; isSpecial: boolean; }; const FORMATIONS: Record = { "4-3-3": { label: "4-3-3", def: 4, mid: 3, fwd: 3 }, "4-4-2": { label: "4-4-2", def: 4, mid: 4, fwd: 2 }, "4-5-1": { label: "4-5-1", def: 4, mid: 5, fwd: 1 }, "3-5-2": { label: "3-5-2", def: 3, mid: 5, fwd: 2 }, "3-4-3": { label: "3-4-3", def: 3, mid: 4, fwd: 3 }, "5-3-2": { label: "5-3-2", def: 5, mid: 3, fwd: 2 }, "5-4-1": { label: "5-4-1", def: 5, mid: 4, fwd: 1 }, }; const POSITION_LABELS: Record = { GK: "دروازه‌بان", DEF: "مدافع", MID: "هافبک", FWD: "مهاجم", }; function formatSaleValue(price: number) { return Math.round(price * 0.7); } function isSpecialTeamPlayer(tp: TeamPlayer) { return Boolean(tp.goldenCardId); } export default function TeamBuilder({ team: initialTeam, allPlayers, initialSpecialCards, }: { team: Team; allPlayers: Player[]; initialSpecialCards: SpecialCard[]; }) { const [team, setTeam] = useState(initialTeam); const [specialCards, setSpecialCards] = useState(initialSpecialCards); const [teamName, setTeamName] = useState(""); const [formation, setFormation] = useState(initialTeam?.formation ?? "4-3-3"); const [filter, setFilter] = useState(""); const [posFilter, setPosFilter] = useState(""); const [loading, setLoading] = useState(false); const [msg, setMsg] = useState<{ text: string; type: "error" | "success" } | null>(null); const [submitLoading, setSubmitLoading] = useState(false); const [replacementDialog, setReplacementDialog] = useState<{ card: SpecialCard; candidates: ReplacementCandidate[]; } | null>(null); const specialPlayerIds = useMemo( () => new Set(specialCards.filter((card) => card.state !== "SOLD").map((card) => card.player.id)), [specialCards] ); const spent = team?.players .filter((tp) => !isSpecialTeamPlayer(tp)) .reduce((sum, tp) => sum + tp.player.price, 0) ?? 0; const remaining = (team?.budget ?? 100) - spent; const starters = team?.players.filter((tp) => !tp.isBench) ?? []; const bench = team?.players.filter((tp) => tp.isBench) ?? []; const specialSlotsUsed = team?.players.filter(isSpecialTeamPlayer).length ?? 0; const gkSlots = starters.filter((tp) => tp.player.position === "GK"); const defSlots = starters.filter((tp) => tp.player.position === "DEF"); const midSlots = starters.filter((tp) => tp.player.position === "MID"); const fwdSlots = starters.filter((tp) => tp.player.position === "FWD"); const myPlayerIds = new Set(team?.players.map((tp) => tp.playerId) ?? []); const filtered = allPlayers.filter( (p) => !myPlayerIds.has(p.id) && !specialPlayerIds.has(p.id) && (posFilter ? p.position === posFilter : true) && (filter ? p.name.includes(filter) || p.country.name.includes(filter) : true) ); const inventoryCards = specialCards.filter((card) => card.state === "IN_INVENTORY"); const inTeamCards = specialCards.filter((card) => card.state === "IN_TEAM"); const sealedCount = specialCards.filter((card) => card.status === "SEALED").length; async function createTeam() { if (!teamName.trim()) return; setLoading(true); const res = await fetch("/api/team", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: teamName, formation }), }); const data = await res.json(); if (res.ok) { setTeam({ ...data, players: [] }); setMsg(null); } else { setMsg({ text: data.error, type: "error" }); } setLoading(false); } async function addPlayer(playerId: string, isBench = false) { setLoading(true); const res = await fetch("/api/team/players", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ playerId, isBench }), }); const data = await res.json(); if (res.ok) { const player = allPlayers.find((p) => p.id === playerId)!; setTeam((current) => current ? { ...current, players: [...current.players, { ...data, goldenCardId: null, player }] } : current ); setMsg(null); } else { setMsg({ text: data.error, type: "error" }); } setLoading(false); } async function removePlayer(playerId: string) { setLoading(true); const teamPlayer = team?.players.find((tp) => tp.playerId === playerId) ?? null; const res = await fetch("/api/team/players", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ playerId }), }); const data = await res.json().catch(() => null); if (res.ok) { setTeam((current) => current ? { ...current, players: current.players.filter((tp) => tp.playerId !== playerId) } : current ); if (teamPlayer?.goldenCardId) { setSpecialCards((current) => current.map((card) => (card.id === teamPlayer.goldenCardId ? { ...card, state: "IN_INVENTORY" } : card)) ); setMsg({ text: "بازیکن ویژه از تیم خارج شد و به کارت ویژه برگشت", type: "success" }); } else { setMsg(null); } } else if (data?.error) { setMsg({ text: data.error, type: "error" }); } setLoading(false); } async function sellSpecialCard(cardId: string) { setLoading(true); const res = await fetch(`/api/golden-cards/${cardId}/sell`, { method: "POST" }); const data = await res.json(); if (res.ok) { setSpecialCards((current) => current.map((card) => (card.id === cardId ? { ...card, state: "SOLD" } : card))); setTeam((current) => { if (!current) return current; const soldCard = specialCards.find((card) => card.id === cardId); return { ...current, budget: current.budget + data.addedBudget, players: soldCard ? current.players.filter((tp) => tp.goldenCardId !== cardId) : current.players, }; }); setMsg({ text: `${data.addedBudget} میلیون به بودجه تیم اضافه شد`, type: "success" }); } else { setMsg({ text: data.error, type: "error" }); } setLoading(false); } function mergeSpecialPlayer(card: SpecialCard, teamPlayer: { playerId: string; isBench: boolean; goldenCardId: string }) { setTeam((current) => { if (!current) return current; const existing = current.players.find((tp) => tp.playerId === teamPlayer.playerId); if (existing) { return { ...current, players: current.players.map((tp) => tp.playerId === teamPlayer.playerId ? { ...tp, goldenCardId: card.id, isBench: teamPlayer.isBench } : tp ), }; } return { ...current, players: [ ...current.players, { playerId: card.player.id, goldenCardId: card.id, isCaptain: false, isViceCaptain: false, isBench: teamPlayer.isBench, positionIndex: 0, player: card.player, }, ], }; }); } async function addSpecialCardToTeam(card: SpecialCard, replacePlayerId?: string) { setLoading(true); const res = await fetch(`/api/golden-cards/${card.id}/add-to-team`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(replacePlayerId ? { replacePlayerId } : {}), }); const data = await res.json(); if (res.ok) { setReplacementDialog(null); setSpecialCards((current) => current.map((item) => (item.id === card.id ? { ...item, state: "IN_TEAM" } : item))); if (data.replacedGoldenCardId) { setSpecialCards((current) => current.map((item) => (item.id === data.replacedGoldenCardId ? { ...item, state: "IN_INVENTORY" } : item)) ); } if (data.replacedPlayerId) { setTeam((current) => current ? { ...current, players: current.players.filter((tp) => tp.playerId !== data.replacedPlayerId) } : current ); } mergeSpecialPlayer(card, { playerId: card.player.id, isBench: data.teamPlayer.isBench, goldenCardId: card.id, }); setMsg({ text: data.message ?? `بازیکن ویژه در ${data.placement} قرار گرفت`, type: "success" }); } else if (res.status === 409 && data.needsReplacement) { setReplacementDialog({ card, candidates: data.candidates }); } else { setMsg({ text: data.error, type: "error" }); } setLoading(false); } async function setCaptain(playerId: string, type: "captain" | "vice") { await fetch("/api/team/captain", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ playerId, type }), }); setTeam((current) => { if (!current) return current; return { ...current, players: current.players.map((tp) => ({ ...tp, isCaptain: type === "captain" ? tp.playerId === playerId : tp.isCaptain, isViceCaptain: type === "vice" ? tp.playerId === playerId : tp.isViceCaptain, })), }; }); } async function submitTeam() { setSubmitLoading(true); const res = await fetch("/api/team/submit", { method: "POST" }); const data = await res.json(); if (res.ok) { setTeam((current) => (current ? { ...current, status: "ACTIVE" } : current)); setMsg({ text: "تیم ثبت شد و وارد رقابت شد", type: "success" }); } else { setMsg({ text: data.error, type: "error" }); } setSubmitLoading(false); } const isComplete = starters.length === 11 && bench.length === 4; const canSubmit = isComplete && team?.status !== "ACTIVE"; if (!team) { return (

تیمت را بساز

با بودجه 100 میلیون، 15 بازیکن برای تیمت انتخاب کن.

setTeamName(e.target.value)} className="w-full border-2 rounded-xl px-4 py-3 mb-4 focus:outline-none focus:border-green-500 text-center text-lg" />
{Object.entries(FORMATIONS).map(([key]) => ( ))}
); } return (
{replacementDialog && (

پست {POSITION_LABELS[replacementDialog.card.player.position]} پر است

برای اضافه کردن {replacementDialog.card.player.name} یکی از بازیکنان این پست را جایگزین کنید.

{replacementDialog.candidates.map((candidate) => ( ))}
)}

{team.name}

{team.status === "ACTIVE" ? "فعال" : "در حال تکمیل"} ترکیب: {formation}
{msg && (
{msg.text}
)}

نیمکت

{bench.map((tp) => ( ))} {Array.from({ length: Math.max(0, 4 - bench.length) }).map((_, index) => ( ))}
{canSubmit && ( )}

کارت‌های ویژه

ظرفیت تیم: {specialSlotsUsed}/3
{sealedCount > 0 &&

{sealedCount} کارت ویژه هنوز باز نشده است.

}
{inventoryCards.map((card) => ( addSpecialCardToTeam(card)} onSell={() => sellSpecialCard(card.id)} /> ))} {inTeamCards.map((card) => ( sellSpecialCard(card.id)} /> ))} {inventoryCards.length === 0 && inTeamCards.length === 0 && (
کارت ویژه آماده استفاده ندارید.
)}

خرید بازیکن عادی

{["", "GK", "DEF", "MID", "FWD"].map((pos) => ( ))}
setFilter(e.target.value)} className="w-full border rounded-xl px-4 py-2 text-sm mb-3 focus:outline-none focus:ring-2 focus:ring-green-400" />
{filtered.map((p) => (
{p.image ? ( {p.name} ) : (
👤
)}
{p.name.split(" ").slice(-1)[0]}
{p.price}M | {p.totalPoints}pts
))} {filtered.length === 0 &&
بازیکنی پیدا نشد
}
); } function Metric({ label, value, tone }: { label: string; value: string | number; tone: string }) { return (
{value}
{label}
); } function PitchRow({ players, slots, onRemove, onCaptain, onSell, }: { players: TeamPlayer[]; slots: number; onRemove: (id: string) => void; onCaptain: (id: string, t: "captain" | "vice") => void; onSell: (cardId: string) => void; }) { return (
{Array.from({ length: slots }).map((_, index) => { const tp = players[index]; return tp ? ( ) : ( ); })}
); } function PitchCard({ tp, onRemove, onCaptain, onSell, small, }: { tp: TeamPlayer; onRemove: (id: string) => void; onCaptain: (id: string, t: "captain" | "vice") => void; onSell: (cardId: string) => void; small?: boolean; }) { const isEliminated = Boolean(tp.player.country?.isEliminated); const shortName = tp.player.name.split(" ").slice(-1)[0]; const special = isSpecialTeamPlayer(tp); return (
{tp.player.image ? ( {tp.player.name} ) : (
👤
)} {isEliminated && (
×
)}
{shortName}
{special &&
کارت ویژه
}
{tp.isCaptain &&
C
} {tp.isViceCaptain &&
V
}
{special && tp.goldenCardId ? ( ) : null}
); } function SpecialCardRow({ card, loading, onAdd, onSell, }: { card: SpecialCard; loading: boolean; onAdd?: () => void; onSell: () => void; }) { const canAdd = card.state === "IN_INVENTORY"; return (
{card.player.image ? ( {card.player.name} ) : (
👤
)}
{card.player.name}
{POSITION_LABELS[card.player.position]} | {card.player.price}M
{card.state === "IN_TEAM" ? "در تیم" : `فروش: ${formatSaleValue(card.player.price)}M`}
{canAdd && ( )}
); } function EmptySlot({ label }: { label: string }) { return (
{label}
); }