import React, { useState } from "react";
import { Camera, Server, Shield } from "lucide-react";
// ======= ДАННЫЕ ======= //
const DEVICE_CATALOG = [
{ id: "poe-b-power", name: "I-Pro PoE B Power", icon: Shield },
{ id: "poe-b", name: "I-Pro PoE B", icon: Shield },
{ id: "poe-b-ultra", name: "I-Pro PoE B Ultra", icon: Shield },
{ id: "poe-ultra-mini", name: "I-Pro PoE Ultra Mini", icon: Shield },
{ id: "poe-power-mini", name: "I-Pro PoE Power Mini", icon: Shield },
{ id: "poe-mini", name: "I-Pro PoE Mini", icon: Shield },
{ id: "gigabit-poe-plus", name: "I-Pro GIGABIT PoE+", icon: Shield },
{ id: "gigabit-poe-plus-lsa", name: "I-Pro GIGABIT PoE+ LSA", icon: Shield },
] as const;
const SEGMENTS = [
{ id: "camAF", label: "Камера PoE AF", icon: Camera },
{ id: "camAT", label: "Камера PoE AT", icon: Camera },
{ id: "camBT", label: "Камера PoE BT", icon: Camera },
{ id: "nvr", label: "Регистратор NVR", icon: Server },
{ id: "switchG", label: "Коммутатор Gigabit PoE", icon: Server },
{ id: "switchF", label: "Коммутатор FastEthernet 100M", icon: Server },
{ id: "switchDL", label: "Коммутатор D-Link", icon: Server },
] as const;
// ======= ПОЛНАЯ ТАБЛИЦА СОВМЕСТИМОСТИ (сокращённая вставка) ======= //
const COMPATIBILITY: Record<string, { survive: number; start: number; comment: string }> = {
// Камера PoE AF
"camAF_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
"camAF_poe-b": { survive: 80, start: 1, comment: "не совместима" },
"camAF_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
"camAF_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
"camAF_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
"camAF_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
"camAF_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"camAF_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Камера PoE AT
"camAT_poe-b-power": { survive: 90, start: 100, comment: "Всё совпадает" },
"camAT_poe-b": { survive: 80, start: 100, comment: "Всё совпадает" },
"camAT_poe-b-ultra": { survive: 99, start: 100, comment: "Всё совпадает" },
"camAT_poe-ultra-mini": { survive: 70, start: 100, comment: "Всё совпадает" },
"camAT_poe-power-mini": { survive: 60, start: 100, comment: "Всё совпадает" },
"camAT_poe-mini": { survive: 50, start: 100, comment: "Всё совпадает" },
"camAT_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"camAT_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Камера PoE BT
"camBT_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
"camBT_poe-b": { survive: 80, start: 1, comment: "не совместима" },
"camBT_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
"camBT_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
"camBT_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
"camBT_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
"camBT_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"camBT_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Регистратор NVR
"nvr_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
"nvr_poe-b": { survive: 80, start: 1, comment: "не совместима" },
"nvr_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
"nvr_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
"nvr_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
"nvr_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
"nvr_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"nvr_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Коммутатор Gigabit PoE
"switchG_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
"switchG_poe-b": { survive: 80, start: 1, comment: "не совместима" },
"switchG_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
"switchG_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
"switchG_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
"switchG_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
"switchG_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"switchG_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Коммутатор FastEthernet
"switchF_poe-b-power": { survive: 90, start: 100, comment: "Всё совпадает" },
"switchF_poe-b": { survive: 80, start: 100, comment: "Всё совпадает" },
"switchF_poe-b-ultra": { survive: 99, start: 100, comment: "Всё совпадает" },
"switchF_poe-ultra-mini": { survive: 70, start: 100, comment: "Всё совпадает" },
"switchF_poe-power-mini": { survive: 60, start: 100, comment: "Всё совпадает" },
"switchF_poe-mini": { survive: 50, start: 100, comment: "Всё совпадает" },
"switchF_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"switchF_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
// Коммутатор D-Link
"switchDL_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
"switchDL_poe-b": { survive: 80, start: 1, comment: "не совместима" },
"switchDL_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
"switchDL_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
"switchDL_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
"switchDL_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
"switchDL_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
"switchDL_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
};
function pairKey(segId: string, devId: string) {
return `${segId}_${devId}`;
}
function statusByRoll(segId: string, devId?: string): "alive" | "offline" | "dead" {
if (!devId) return "dead";
const entry = COMPATIBILITY[pairKey(segId, devId)];
if (!entry) return "dead";
if (Math.random() * 100 > entry.survive) return "dead";
if (Math.random() * 100 > entry.start) return "offline";
return "alive";
}
// ======= UI ======= //
function SegmentSlot({ seg, placed, onDrop }: { seg: any; placed?: any; onDrop: (id: string) => void }) {
const Icon = seg.icon;
const entry = placed ? COMPATIBILITY[pairKey(seg.id, placed.id)] : null;
return (
<div
className="border border-sky-300 rounded-2xl shadow p-4 bg-gradient-to-br from-white to-slate-50 hover:shadow-md transition"
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault();
const id = e.dataTransfer.getData("text/plain");
onDrop(id);
}}
>
<div className="flex items-center gap-2 mb-2 font-semibold text-slate-700">
<Icon className="h-5 w-5 text-sky-600" /> {seg.label}
</div>
{placed ? (
<div className="text-sm text-slate-800">
<div className="font-medium text-sky-700">{placed.name}</div>
{entry ? (
<div className="mt-2 text-xs space-y-1">
<div className="flex items-center gap-1"><span className="font-semibold text-sky-700">Шанс выживания:</span><span className="text-slate-800">{entry.survive}%</span></div>
<div className="flex items-center gap-1"><span className="font-semibold text-sky-700">Шанс запуска:</span><span className="text-slate-800">{entry.start}%</span></div>
<div className="text-slate-500 italic">{entry.comment}</div>
</div>
) : (
<div className="mt-1 text-xs text-red-600">Нет данных по совместимости</div>
)}
</div>
) : (
<div className="text-xs text-slate-500 italic">Перетащите устройство</div>
)}
</div>
);
}
function DeviceCard({ d }: { d: any }) {
const Icon = d.icon;
return (
<div
className="rounded-xl p-3 cursor-grab border shadow bg-gradient-to-br from-slate-50 to-slate-100 hover:shadow-md transition"
draggable
onDragStart={(e) => e.dataTransfer.setData("text/plain", d.id)}
>
<div className="flex items-center gap-2 text-slate-700">
<Icon className="h-4 w-4 text-sky-600" /> <span className="font-medium">{d.name}</span>
</div>
</div>
);
}
// ======= Слот-машина (синяя тема) ======= //
function SlotMachine({ onWin, mode }: { onWin: (dev: any) => void; mode: "project" | "expert" }) {
const [spinning, setSpinning] = useState(false);
const [slots, setSlots] = useState([DEVICE_CATALOG[0], DEVICE_CATALOG[1], DEVICE_CATALOG[2]]);
const spin = () => {
if (spinning) return;
setSpinning(true);
const winner = mode === "expert"
? DEVICE_CATALOG.find((d) => d.id === "gigabit-poe-plus")!
: DEVICE_CATALOG[Math.floor(Math.random() * DEVICE_CATALOG.length)];
const seq: typeof DEVICE_CATALOG[number][] = [];
for (let i = 0; i < 20; i++) seq.push(DEVICE_CATALOG[Math.floor(Math.random() * DEVICE_CATALOG.length)]);
seq.push(winner);
let i = 0;
const timer = setInterval(() => {
setSlots([seq[i % seq.length], seq[(i + 1) % seq.length], seq[(i + 2) % seq.length]]);
i++;
if (i >= seq.length) {
clearInterval(timer);
setSpinning(false);
onWin(winner);
}
}, 90);
};
return (
<div className="flex flex-col items-center">
<div className="bg-slate-900 border-2 border-sky-500 rounded-xl p-4 flex gap-2 w-80 justify-center shadow-inner">
{slots.map((s, i) => (
<div key={i} className="w-24 h-20 flex items-center justify-center bg-gradient-to-br from-slate-800 to-slate-600 text-white font-semibold rounded-md shadow">
{s.name}
</div>
))}
</div>
<button onClick={spin} disabled={spinning} className="mt-4 px-6 py-2 bg-sky-600 hover:bg-sky-700 disabled:opacity-60 text-white font-semibold rounded-full shadow border border-sky-300">
{mode === "expert" ? " Эксперт" : " Проектировщик"}
</button>
</div>
);
}
// ======= ОСНОВНОЙ КОМПОНЕНТ ======= //
export default function GamePrototype() {
const [inventory, setInventory] = useState<typeof DEVICE_CATALOG[number][]>([]);
const [placed, setPlaced] = useState<Record<string, typeof DEVICE_CATALOG[number] | undefined>>({});
const [stormResult, setStormResult] = useState<Record<string, { status: "alive" | "offline" | "dead"; comment?: string }>>({});
function placeDevice(segId: string, devId: string) {
const dev = DEVICE_CATALOG.find((d) => d.id === devId);
if (!dev) return;
setInventory((inv) => inv.filter((x, idx) => idx !== inv.findIndex((d) => d.id === devId)));
setPlaced((p) => ({ ...p, [segId]: dev }));
}
function startStorm() {
const res: Record<string, { status: "alive" | "offline" | "dead"; comment?: string }> = {};
SEGMENTS.forEach((seg) => {
const dev = placed[seg.id];
const status = statusByRoll(seg.id, dev?.id);
const entry = dev ? COMPATIBILITY[pairKey(seg.id, dev.id)] : undefined;
res[seg.id] = { status, comment: entry?.comment };
});
setStormResult(res);
}
function resetGame() {
setInventory([]);
setPlaced({});
setStormResult({});
}
return (
<div className="p-6 space-y-6 bg-gradient-to-b from-blue-950 via-slate-950 to-blue-950 min-h-screen text-white">
<h1 className="text-3xl md:text-4xl font-extrabold text-center text-sky-300 drop-shadow">⚡ Гроза над сетью — слот‑машина</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{SEGMENTS.map((seg) => (
<SegmentSlot key={seg.id} seg={seg} placed={placed[seg.id]} onDrop={(id) => placeDevice(seg.id, id)} />
))}
</div>
<div className="flex flex-col md:flex-row justify-center items-center gap-8">
<SlotMachine mode="project" onWin={(dev) => setInventory((inv) => [...inv, dev])} />
<SlotMachine mode="expert" onWin={(dev) => setInventory((inv) => [...inv, dev])} />
</div>
<div className="flex justify-center gap-3">
<button onClick={startStorm} className="px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-full font-semibold shadow border border-indigo-400">⚡ Запустить грозу</button>
<button onClick={resetGame} className="px-6 py-2 bg-slate-600 hover:bg-slate-700 text-white rounded-full font-semibold shadow border border-slate-400"> Сбросить</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{inventory.length === 0 && <div className="text-center text-sky-200 italic md:col-span-3">Сыграйте в слот‑машину, чтобы получить устройства</div>}
{inventory.map((d, i) => (
<DeviceCard key={`${d.id}-${i}`} d={d} />
))}
</div>
{Object.keys(stormResult).length > 0 && (
<div className="mt-6 bg-sky-50 text-slate-900 rounded-xl shadow p-4 border border-sky-300">
<h2 className="font-semibold mb-3 text-slate-800 text-lg">Результаты грозы</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
{SEGMENTS.map((seg) => {
const r = stormResult[seg.id];
if (!r) return null;
const badge = r.status === "alive" ? "✅ Работает" : r.status === "offline" ? "⚠️ Не запускается" : "❌ Повреждён";
return (
<div key={seg.id} className="flex items-center gap-2 font-medium">
<span className="min-w-[180px] text-slate-700">{seg.label}:</span> <span>{badge}</span>
{r.comment && <span className="text-sm text-slate-600 ml-2">— {r.comment}</span>}
</div>
);
})}
</div>
</div>
)}
</div>
);
}