const MATCH_DATA = window.MATCH_DATA || {};
console.log("SCRIPT VERSION NEW PLAYER LINKS");
const TEAM_NAMES = MATCH_DATA.teamNames || {
    home: "",
    away: ""
};

const goalEditorState = {
    open: false,
    eventId: null,
    side: null,
    type: null
};

const squadEditState = {
    open: false,
    side: "home",
    starting: { home: [], away: [] },
    bench: { home: [], away: [] },
    coaches: { home: [], away: [] }
};

const refereeEditorState = {
    items: Array.isArray(MATCH_DATA.referees) ? [...MATCH_DATA.referees] : [],
    pool: Array.isArray(MATCH_DATA.refereePool) ? [...MATCH_DATA.refereePool] : []
};
refereeEditorState.items = refereeEditorState.items.map(normalizeReferee);
refereeEditorState.pool = refereeEditorState.pool.map(normalizeReferee);

const REFEREE_ROLE_OPTIONS = [
    "Главный судья",
    "Ассистент 1",
    "Ассистент 2",
    "Резервный судья",
    "VAR",
    "AVAR",
    "Инспектор"
];

const VMIX_TARGET_CLIENT_ID = MATCH_DATA.vmixTargetClientId || null;
const VMIX_TARGET_GROUP = MATCH_DATA.vmixTargetGroup || null;
const CURRENT_USER_LOGIN = MATCH_DATA.currentUser?.username || null;

function vmixCmd(path) {
    const value = String(path || "").trim();
    if (!value) return "";
    if (value.startsWith("/api/")) return value;
    return `/api/?${value.replace(/^\?/, "")}`;
}


let currentPeriod = null;
let matchClockSeconds = 0;
let timerId = null;
let periodStartedAt = null;
let pausedAccumulatedSeconds = 0;
let eventSeq = 1;
let matchEvents = [];
let scoreState = { home: 0, away: 0 };
let activeSubstitution = null;
let extraTimeMinutes = { first: 0, second: 0 };
let originalLineupState = null;

const SESSION_TOKEN = MATCH_DATA.sessionToken ?? null;
const MATCH_ID = MATCH_DATA.matchId ?? null;
const STORAGE_KEY = `match_admin_state_match_${MATCH_ID}`;

const formationState = {
    home: Array.isArray(MATCH_DATA.homeFormations) ? MATCH_DATA.homeFormations : [],
    away: Array.isArray(MATCH_DATA.awayFormations) ? MATCH_DATA.awayFormations : [],
};

const fallbackLineups = {
    home: Array.isArray(MATCH_DATA.homeStarting) ? MATCH_DATA.homeStarting : [],
    away: Array.isArray(MATCH_DATA.awayStarting) ? MATCH_DATA.awayStarting : [],
};

function getLiveClockSeconds() {
    if (!periodStartedAt) {
        return pausedAccumulatedSeconds;
    }

    return pausedAccumulatedSeconds + Math.max(0, Math.floor((Date.now() - periodStartedAt) / 1000));
}

function escapeHtml(value) {
    return String(value ?? "")
        .replaceAll("&", "&amp;")
        .replaceAll("<", "&lt;")
        .replaceAll(">", "&gt;")
        .replaceAll('"', "&quot;")
        .replaceAll("'", "&#039;");
}

function formatClock(totalSeconds) {
    const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, "0");
    const seconds = String(totalSeconds % 60).padStart(2, "0");
    return `${minutes}:${seconds}`;
}

function getFootballMinute(totalSeconds) {
    if (currentPeriod === "2H" || currentPeriod === "FT") {
        const secondHalfSeconds = Math.max(0, totalSeconds - 45 * 60);
        return 46 + Math.floor(secondHalfSeconds / 60);
    }

    return Math.floor(totalSeconds / 60) + 1;
}


function formatMinuteLabel(totalSeconds) {
    return getEventDisplayMinuteBySeconds(totalSeconds);
}



function getEventDisplayMinuteBySeconds(totalSeconds, periodType = null) {
    const safeSeconds = Math.max(0, Number(totalSeconds) || 0);

    if (periodType === "period_start_1h") {
        return "1'";
    }

    if (periodType === "period_start_2h") {
        return "46'";
    }

    const minute = Math.floor(safeSeconds / 60) + 1;

    // Добавленное время 1-го тайма — только если ещё не начался 2-й тайм
    if (
        safeSeconds > 45 * 60 &&
        safeSeconds < 90 * 60 &&
        (currentPeriod === "1H" || currentPeriod === "HT")
    ) {
        const extra = Math.floor((safeSeconds - 45 * 60) / 60);
        return `45'+${Math.max(1, extra)}`;
    }

    // Добавленное время 2-го тайма — только после 90 минут
    if (
        safeSeconds > 90 * 60 &&
        (currentPeriod === "2H" || currentPeriod === "FT")
    ) {
        const extra = Math.floor((safeSeconds - 90 * 60) / 60);
        return `90'+${Math.max(1, extra)}`;
    }

    return `${minute}'`;
}


function getEventSortPriority(event) {
    const type = String(event?.type || "");

    if (type === "period_ht" || type === "period_ft") {
        return 3;
    }

    if (type === "period_start_1h" || type === "period_start_2h") {
        return 1;
    }

    return 2;
}




function sortEventsForStorage(events) {
    return [...events].sort((a, b) => {
        const secondsDiff = (a.seconds || 0) - (b.seconds || 0);
        if (secondsDiff !== 0) return secondsDiff;

        const priorityDiff = getEventSortPriority(a) - getEventSortPriority(b);
        if (priorityDiff !== 0) return priorityDiff;

        return (a.id || 0) - (b.id || 0);
    });
}

function sortEventsForDisplay(events) {
    return [...events].sort((a, b) => {
        const secondsDiff = (b.seconds || 0) - (a.seconds || 0);
        if (secondsDiff !== 0) return secondsDiff;

        const priorityDiff = getEventSortPriority(b) - getEventSortPriority(a);
        if (priorityDiff !== 0) return priorityDiff;

        return (b.id || 0) - (a.id || 0);
    });
}



function addCoachEvent(side, cardType, coachName, btn = null) {
    console.log("addCoachEvent called", { side, cardType, coachName, btn });

    const counts = getCoachCardCounts(side, coachName);

    const sentOffByTwoYellows = counts.yellow >= 2;
    const sentOffByRed = counts.red >= 1;
    const sentOff = sentOffByTwoYellows || sentOffByRed;

    if (sentOff) {
        console.log("blocked: sent off");
        return;
    }

    if (cardType === "yellow" && counts.yellow >= 2) {
        console.log("blocked: yellow limit");
        return;
    }

    if (cardType === "red" && counts.red >= 1) {
        console.log("blocked: red limit");
        return;
    }

    const type = cardType === "yellow" ? "coach_yellow" : "coach_red";

    console.log("pushEvent", { side, type, coachName });

    pushEvent(side, type, coachName, "Тренер", null).then(() => {
        console.log("pushEvent done");
        updateCoachCardButtons();
    });

    if (btn) {
        setTimeout(updateCoachCardButtons, 0);
    }
}


function updateTimerUI() {
    matchClockSeconds = getLiveClockSeconds();
    const timer = document.getElementById("matchTimer");
    if (timer) timer.textContent = formatClock(matchClockSeconds);
}

function updateScoreUI() {
    const score = document.getElementById("matchScore");
    if (score) score.textContent = `${scoreState.home} : ${scoreState.away}`;
}

function updatePeriodBadge(text) {
    const badge = document.getElementById("periodBadge");
    if (badge) badge.textContent = text;
}

function getPersistedState() {
    try {
        const raw = localStorage.getItem(STORAGE_KEY);
        return raw ? JSON.parse(raw) : null;
    } catch (e) {
        return null;
    }
}

function saveMatchState() {
    try {
        matchClockSeconds = getLiveClockSeconds();

        const payload = {
            currentPeriod,
            matchClockSeconds,
            timerRunning: !!timerId,
            savedAt: Date.now(),
            periodStartedAt,
            pausedAccumulatedSeconds,
            eventSeq,
            matchEvents,
            scoreState,
            extraTimeMinutes,
            originalLineupState
        };

        localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
    } catch (e) {
        console.warn("Не удалось сохранить состояние матча", e);
    }
}

function restoreMatchState() {
    const state = getPersistedState();
    if (!state) return false;

    currentPeriod = state.currentPeriod ?? null;
    eventSeq = Number(state.eventSeq || 1);
    matchEvents = Array.isArray(state.matchEvents) ? state.matchEvents : [];
    scoreState = state.scoreState || { home: 0, away: 0 };
    extraTimeMinutes = state.extraTimeMinutes || { first: 0, second: 0 };
    // originalLineupState = state.originalLineupState || originalLineupState;

    pausedAccumulatedSeconds = Number(state.pausedAccumulatedSeconds ?? 0);
    periodStartedAt = state.periodStartedAt ?? null;

    if (!state.timerRunning) {
        periodStartedAt = null;
    }

    updatePeriodBadge(
    currentPeriod === "1H" ? (extraTimeMinutes.first ? `1-й тайм +${extraTimeMinutes.first}` : "1-й тайм") :
    currentPeriod === "HT" ? "Перерыв" :
    currentPeriod === "2H" ? (extraTimeMinutes.second ? `2-й тайм +${extraTimeMinutes.second}` : "2-й тайм") :
    currentPeriod === "FT" ? "Матч завершён" :
    "Матч не начат"
);

    updateTimerUI();
    updateScoreUI();
    renderEvents();

    if (typeof rebuildLineupsFromEvents === "function") {
        rebuildLineupsFromEvents();
    }

    if (state.timerRunning && (currentPeriod === "1H" || currentPeriod === "2H")) {
        if (timerId) clearInterval(timerId);
        timerId = setInterval(() => {
            updateTimerUI();
            saveMatchState();
        }, 1000);
    }
    updateCoachCardButtons();
    updatePeriodButtons();
    return true;
}

function stopTimer() {
    if (timerId) {
        clearInterval(timerId);
        timerId = null;
    }

    pausedAccumulatedSeconds = getLiveClockSeconds();
    periodStartedAt = null;
    matchClockSeconds = pausedAccumulatedSeconds;
}

function startTimer() {
    if (timerId) {
        clearInterval(timerId);
        timerId = null;
    }

    periodStartedAt = Date.now();
    timerId = setInterval(() => {
        updateTimerUI();
        saveMatchState();
    }, 1000);

    updateTimerUI();
    saveMatchState();
}

function applyExtraTime() {
    const input = document.getElementById("extraTimeMinutes");
    const value = Math.max(0, Number(input ? input.value : 0));

    if (!value) {
        saveMatchState();
        return;
    }

    if (currentPeriod === "1H" || currentPeriod === "HT") {
        extraTimeMinutes.first = value;
        updatePeriodBadge(`1-й тайм +${value}`);

        addPeriodMarker(
            "extra_time_first",
            `Добавлено ${value} мин. к 1-му тайму`,
            matchClockSeconds
        );
    } else if (currentPeriod === "2H" || currentPeriod === "FT") {
        extraTimeMinutes.second = value;
        updatePeriodBadge(`2-й тайм +${value}`);

        addPeriodMarker(
            "extra_time_second",
            `Добавлено ${value} мин. ко 2-му тайму`,
            matchClockSeconds
        );
    }

    saveMatchState();
}

async function saveEventToServer(eventData) {
    if (!SESSION_TOKEN) return null;
    try {
        const res = await fetch(`/admin/session/${SESSION_TOKEN}/event`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(eventData)
        });
        if (!res.ok) return null;
        return await res.json();
    } catch (e) {
        console.warn("Не удалось сохранить событие в БД", e);
        return null;
    }
}

async function deleteEvent(eventId) {
    if (eventId === null || eventId === undefined || eventId === "") return;

    try {
        const res = await fetch(`/admin/session/${SESSION_TOKEN}/event/${eventId}`, {
            method: "DELETE"
        });
        if (!res.ok) {
            console.warn("Удаление события не удалось", res.status);
            return;
        }
    } catch (e) {
        console.warn("Не удалось удалить событие", e);
        return;
    }

    matchEvents = matchEvents.filter((event) => String(event.id) !== String(eventId));

    scoreState = { home: 0, away: 0 };
    matchEvents.forEach((event) => {
        if (["goal", "penalty", "own_goal"].includes(event.type)) {
            scoreState[event.side] = (scoreState[event.side] || 0) + 1;
        }
    });

    updateScoreUI();
    renderEvents();
    rebuildLineupsFromEvents();
    saveMatchState();
    updateCoachCardButtons();
    updatePeriodButtons();
}

async function clearEventsOnServer() {
    if (!SESSION_TOKEN) return;
    try {
        await fetch(`/admin/session/${SESSION_TOKEN}/events`, { method: "DELETE" });
    } catch (e) {
        console.warn("Не удалось очистить события в БД", e);
    }
}

function getOppositeSide(side) {
    return side === "home" ? "away" : "home";
}

function getEventIcon(type) {
    const map = {
        goal: "⚽",
        own_goal: "АГ",
        penalty: "П",
        sub: "⇄",
        yellow: "🟨",
        red: "🟥",
        coach_yellow: "🟨",
        coach_red: "🟥",
        period_start_1h: "",
        period_ht: "",
        period_start_2h: "",
        period_ft: "",
        extra_time_first: "",
        extra_time_second: ""
    };
    return map[type] || "•";
}

function getEventTitle(type, name) {
    const map = {
        goal: "Гол",
        own_goal: "Автогол",
        penalty: "Пенальти",
        sub: "Замена",
        yellow: "Жёлтая карточка",
        red: "Красная карточка",
        coach_yellow: "Жёлтая карточка тренеру",
        coach_red: "Красная карточка тренеру",
        period_start_1h: "Начало 1-го тайма",
        period_ht: "Конец 1-го тайма",
        period_start_2h: "Начало 2-го тайма",
        period_ft: "Конец 2-го тайма",
        extra_time_first: "Добавлено время к 1-му тайму",
        extra_time_second: "Добавлено время ко 2-му тайму"
    };
    return `${map[type] || "Событие"}${name ? " — " + name : ""}`;
}


function addPeriodMarker(type, title, forcedSeconds = null) {
    const markerSeconds = forcedSeconds === null ? matchClockSeconds : forcedSeconds;

    const markerMinute = getEventDisplayMinuteBySeconds(markerSeconds, type);

    const item = {
        id: null,
        side: "center",
        type,
        icon: "",
        title,
        minute: markerMinute,
        seconds: markerSeconds,
        meta: null,
        player_name: title,
        player_id: null,
        player_out_id: null,
        player_in_id: null
    };

    matchEvents.push(item);
    // matchEvents = sortEventsForStorage(matchEvents);

    renderEvents();
    saveMatchState();

    const numericMinute =
        type === "period_start_1h" ? 1 :
        type === "period_start_2h" ? 46 :
        Math.floor(markerSeconds / 60) + 1;

    saveEventToServer({
        side: "center",
        type,
        player_name: title,
        minute: numericMinute,
        seconds: markerSeconds,
        meta: null,
        player_id: null,
        player_out_id: null,
        player_in_id: null
    }).then((saved) => {
        if (saved && saved.id) {
            item.id = saved.id;
            renderEvents();
            saveMatchState();
        }
    });
}





function setMatchPeriod(period) {
    const markerMap = {
        "1H": "period_start_1h",
        "HT": "period_ht",
        "2H": "period_start_2h",
        "FT": "period_ft"
    };

    const markerType = markerMap[period];
    const alreadyUsed = matchEvents.some((event) => event.type === markerType);
    if (alreadyUsed) {
        return;
    }

    currentPeriod = period;

    if (period === "1H") {
        if (timerId) {
            clearInterval(timerId);
            timerId = null;
        }

        pausedAccumulatedSeconds = 0;
        periodStartedAt = null;
        matchClockSeconds = 0;

        updateTimerUI();
        updatePeriodBadge(extraTimeMinutes.first ? `1-й тайм +${extraTimeMinutes.first}` : "1-й тайм");
        startTimer();
        addPeriodMarker("period_start_1h", "Начало 1-го тайма", 0);
        triggerVmixTimerAction("first_half_start");
        saveMatchState();
        clearExtraTimeInput();
        updatePeriodButtons();
        return;
    }

    if (period === "HT") {
    stopTimer();

    const actualEndSeconds = matchClockSeconds;

    updateTimerUI();
    updatePeriodBadge("Перерыв");
    addPeriodMarker("period_ht", "Конец 1-го тайма", actualEndSeconds);
    triggerVmixTimerAction("halftime");
    saveMatchState();
    clearExtraTimeInput();
    updatePeriodButtons();
    return;
}


    if (period === "2H") {
        if (timerId) {
            clearInterval(timerId);
            timerId = null;
        }

        pausedAccumulatedSeconds = 45 * 60;
        periodStartedAt = null;
        matchClockSeconds = 45 * 60;

        updateTimerUI();
        updatePeriodBadge(extraTimeMinutes.second ? `2-й тайм +${extraTimeMinutes.second}` : "2-й тайм");
        addPeriodMarker("period_start_2h", "Начало 2-го тайма", 45 * 60);
        triggerVmixTimerAction("second_half_start");
        startTimer();
        saveMatchState();
        updatePeriodButtons();
        return;
    }

    if (period === "FT") {
        stopTimer();
        updatePeriodBadge("Матч завершён");
        addPeriodMarker("period_ft", "Конец 2-го тайма", getLiveClockSeconds());
        triggerVmixTimerAction("full_time");
        saveMatchState();
        clearExtraTimeInput();
        updatePeriodButtons();
    }
}

function setBenchHint(side, visible) {
    const hint = document.getElementById(side === "home" ? "homeBenchHint" : "awayBenchHint");
    if (hint) hint.style.display = visible ? "block" : "none";
}

function clearBenchSelection() {
    document.querySelectorAll(".bench-pick-active").forEach(el => el.classList.remove("bench-pick-active"));
    setBenchHint("home", false);
    setBenchHint("away", false);
}

function beginSubstitution(side, playerId, playerName, btn) {
    const row = btn ? btn.closest("tr.lineup-row") : null;
    activeSubstitution = {
        side,
        outPlayerId: playerId,
        outPlayerName: playerName,
        outRow: row
    };
    clearBenchSelection();
    setBenchHint(side, true);
}

async function pushEvent(side, type, personName, meta = "", playerId = null) {
    let eventSide = side;

    if (type === "own_goal") {
        eventSide = getOppositeSide(side);
    }

    if (type === "goal" || type === "penalty" || type === "own_goal") {
        scoreState[eventSide] += 1;
        updateScoreUI();
    }

    const minuteLabel = formatMinuteLabel(matchClockSeconds);
    const numericMinute =
        parseInt(String(minuteLabel).split("+").pop().replace("'", ""), 10) ||
        getFootballMinute(matchClockSeconds);

    const item = {
        id: null,
        side: eventSide,
        type,
        icon: getEventIcon(type),
        title: getEventTitle(type, personName),
        teamName: TEAM_NAMES[eventSide] || "Команда",
        minute: minuteLabel,
        seconds: matchClockSeconds,
        meta,
        player_name: personName,
        player_id: playerId,
        player_out_id: null,
        player_in_id: null
    };

    matchEvents.push(item);

    renderEvents();
    rebuildLineupsFromEvents();
    saveMatchState();

    const saved = await saveEventToServer({
        side: eventSide,
        type,
        player_name: personName,
        minute: numericMinute,
        seconds: matchClockSeconds,
        meta,
        player_id: playerId,
        player_out_id: null,
        player_in_id: null
    });

    if (saved && saved.id) {
        item.id = saved.id;
        renderEvents();
        rebuildLineupsFromEvents();
        saveMatchState();
    }
}

function getCoachCardCounts(side, coachName) {
    const safeName = String(coachName || "").trim();

    let yellow = 0;
    let red = 0;

    matchEvents.forEach((event) => {
        if (event.side !== side) return;
        if (String(event.player_name || "").trim() !== safeName) return;

        if (event.type === "coach_yellow") yellow += 1;
        if (event.type === "coach_red") red += 1;
    });

    return { yellow, red };
}

function updateCoachCardButtons() {
    document.querySelectorAll("[data-card-type][data-coach-side][data-coach-name]").forEach((btn) => {
        const side = btn.dataset.coachSide;
        const coachName = btn.dataset.coachName;
        const cardType = btn.dataset.cardType;

        const counts = getCoachCardCounts(side, coachName);

        const sentOffByTwoYellows = counts.yellow >= 2;
        const sentOffByRed = counts.red >= 1;
        const sentOff = sentOffByTwoYellows || sentOffByRed;

        let disabled = false;

        if (sentOff) {
            disabled = true;
        } else if (cardType === "yellow") {
            disabled = counts.yellow >= 2;
        } else if (cardType === "red") {
            disabled = counts.red >= 1;
        }

        btn.disabled = disabled;
        btn.style.opacity = disabled ? "0.45" : "";
        btn.style.cursor = disabled ? "not-allowed" : "";
    });
}


function updatePeriodButtons() {
    const usedPeriods = new Set(
        matchEvents
            .filter((event) =>
                ["period_start_1h", "period_ht", "period_start_2h", "period_ft"].includes(event.type)
            )
            .map((event) => event.type)
    );

    const map = {
        "1H": "period_start_1h",
        "HT": "period_ht",
        "2H": "period_start_2h",
        "FT": "period_ft"
    };

    document.querySelectorAll("[data-period-btn]").forEach((btn) => {
        const period = btn.dataset.periodBtn;
        const eventType = map[period];
        const disabled = usedPeriods.has(eventType);

        btn.disabled = disabled;
        btn.style.opacity = disabled ? "0.45" : "";
        btn.style.cursor = disabled ? "not-allowed" : "";
    });
}

function addPlayerEvent(side, type, playerId, playerName) {
    pushEvent(side, type, playerName, `Игрок #${playerId ?? ""}`, playerId);
}


let editModeEnabled = false;

function isGameTabActive() {
    const activeTab = document.querySelector(".tab-link.active");
    return activeTab && activeTab.textContent.trim() === "Игра";
}

function setEditMode(enabled) {
    editModeEnabled = !!enabled;

    document.body.classList.toggle("edit-mode-on", editModeEnabled);
    document.querySelectorAll("[data-edit-lock-wrap]").forEach((el) => {
    el.style.display = editModeEnabled ? "flex" : "none";
});

    const btn = document.getElementById("editModeToggle");
    if (btn) {
        btn.title = editModeEnabled
            ? "Режим редактирования: ВКЛ"
            : "Режим редактирования: ВЫКЛ";

        btn.classList.toggle("active", editModeEnabled);
    }

    document.querySelectorAll("[data-edit-lock]").forEach((el) => {
        if ("disabled" in el) {
            el.disabled = !editModeEnabled;
        }
    });
    renderRefereesEditor();

    if (editModeEnabled && isGameTabActive()) {
        openSquadEditor();
    } else {
        closeSquadEditor();
    }
}

function toggleEditMode() {
    setEditMode(!editModeEnabled);
}


function updateEditModeUI() {
    const btn = document.getElementById("editModeToggle");
    if (btn) {
        btn.innerHTML = "✏️";
        btn.title = editModeEnabled
            ? "Режим редактирования: ВКЛ"
            : "Режим редактирования: ВЫКЛ";

        btn.classList.toggle("active", editModeEnabled);
    }

    // document.querySelectorAll("[data-edit-only]").forEach((el) => {
    //     el.style.display = editModeEnabled ? "" : "none";
    // });

    document.querySelectorAll("[data-edit-lock]").forEach((el) => {
        if ("disabled" in el) {
            el.disabled = !editModeEnabled;
        }
    });
}

function stepExtraTime(step) {
    const input = document.getElementById("extraTimeMinutes");
    if (!input) return;

    let value = Number(input.value) || 0;
    value = Math.max(0, value + step);

    input.value = value;
}

function clearMatchEvents() {
    if (timerId) {
        clearInterval(timerId);
        timerId = null;
    }

    periodStartedAt = null;
    pausedAccumulatedSeconds = 0;

    matchEvents = [];
    scoreState = { home: 0, away: 0 };
    eventSeq = 1;
    activeSubstitution = null;
    currentPeriod = null;
    matchClockSeconds = 0;
    extraTimeMinutes = { first: 0, second: 0 };

    clearBenchSelection();
    restoreOriginalLineupRows();

    const extraInput = document.getElementById("extraTimeMinutes");
    if (extraInput) extraInput.value = "";

    updateTimerUI();
    updatePeriodBadge("Матч не начат");
    updateScoreUI();
    renderEvents();

    saveMatchState();
    clearEventsOnServer();
    updateCoachCardButtons();
    updatePeriodButtons();
    triggerVmixTimerAction("clear_match");
}

async function updateEventOnServer(eventId, eventData) {
    if (!SESSION_TOKEN || !eventId) return false;

    try {
        const res = await fetch(`/admin/session/${SESSION_TOKEN}/event/${eventId}`, {
            method: "PUT",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(eventData)
        });

        return res.ok;
    } catch (e) {
        console.warn("Не удалось обновить событие в БД", e);
        return false;
    }
}

function getPlayersForSide(side) {
    return Array.from(document.querySelectorAll(`tr.lineup-row[data-side="${side}"]`))
        .filter((row) => row.dataset.temporary !== "1")
        .map((row) => ({
            player_id: row.dataset.playerId || "",
            player_name: row.dataset.playerName || "",
            player_number: row.dataset.playerNumber || ""
        }))
        .filter((p) => p.player_name.trim() !== "");
}


function openGoalEditor(eventId) {
    const event = matchEvents.find((e) => String(e.id) === String(eventId));
    if (!event) return;
    if (!["goal", "penalty", "own_goal"].includes(event.type)) return;

    const editorSide = event.type === "own_goal"
        ? getOppositeSide(event.side)
        : event.side;

    goalEditorState.open = true;
    goalEditorState.eventId = event.id;
    goalEditorState.side = editorSide;
    goalEditorState.type = event.type;

    const players = getPlayersForSide(editorSide);
    const wrap = document.getElementById("goalEditorList");
    const title = document.getElementById("goalEditorTitle");

    if (title) {
        title.textContent = `Изменить автора: ${event.player_name || "без игрока"}`;
    }

    if (wrap) {
        wrap.innerHTML = players.map((p) => `
            <button
                type="button"
                class="goal-editor-player-btn"
                data-player-id="${escapeHtml(String(p.player_id ?? ""))}"
                data-player-name="${escapeHtml(p.player_name)}"
                onclick="selectGoalScorer(this.dataset.playerId, this.dataset.playerName)"
            >
                <span class="goal-editor-player-number">${escapeHtml(p.player_number || "—")}</span>
                <span class="goal-editor-player-name">${escapeHtml(p.player_name)}</span>
            </button>
        `).join("");
    }

    document.getElementById("goalEditorModal")?.classList.add("show");
}




async function selectGoalScorer(playerId, playerName) {
    const event = matchEvents.find((e) => String(e.id) === String(goalEditorState.eventId));
    if (!event) return;

    const updated = {
        side: event.side,
        type: event.type,
        player_name: playerName,
        minute: parseInt(String(event.minute).split("+").pop().replace("'", ""), 10) || getFootballMinute(event.seconds || 0),
        seconds: event.seconds || 0,
        meta: event.meta || "",
        player_id: playerId ? Number(playerId) : null,
        player_out_id: event.player_out_id ?? null,
        player_in_id: event.player_in_id ?? null
    };

    const ok = await updateEventOnServer(event.id, updated);
    if (!ok) {
        alert("Не удалось обновить автора гола");
        return;
    }

    event.player_name = playerName;
    event.player_id = playerId ? Number(playerId) : null;
    event.title = getEventTitle(event.type, playerName);

    renderEvents();
    saveMatchState();
    closeGoalEditor();
}

function closeGoalEditor() {
    goalEditorState.open = false;
    goalEditorState.eventId = null;
    goalEditorState.side = null;
    goalEditorState.type = null;
    document.getElementById("goalEditorModal")?.classList.remove("show");
}


function getVmixTeamLogo(teamName) {
    const basePath = "D:\\Графика\\ФУТБОЛ\\Женская Суперлига 2026\\Teams Logos";
    let clean = String(teamName || "").replaceAll("«", "").replaceAll("»", "").trim();

    if (clean === "Зенит" || clean === "Динамо") {
        clean += "_Синий";
    }

    return `${basePath}\\${clean}.png`;
}

function getScoreSum() {
    return Number(scoreState?.home || 0) + Number(scoreState?.away || 0);
}

function getSubOutName(payload) {
    const raw = String(payload.player_name || "");
    if (raw.includes("→")) {
        return raw.split("→")[0].trim();
    }
    return String(payload.player_name_out || "").trim();
}

function getSubInName(payload) {
    const raw = String(payload.player_name || "");
    if (raw.includes("→")) {
        return raw.split("→")[1].trim();
    }
    return String(payload.player_name_in || "").trim();
}

function escapeVmixValue(value) {
    return String(value ?? "").trim();
}

function getVmixTeamLogo(teamName) {
    const basePath = "D:\\Графика\\ФУТБОЛ\\Женская Суперлига 2026\\Teams Logos";
    let clean = String(teamName || "").replaceAll("«", "").replaceAll("»", "").trim();

    if (clean === "Зенит" || clean === "Динамо") {
        clean += "_Синий";
    }

    return `${basePath}\\${clean}.png`;
}

function getScoreSum() {
    return Number(scoreState?.home || 0) + Number(scoreState?.away || 0);
}

function getSubOutName(payload) {
    const raw = String(payload.player_name || "");
    if (raw.includes("→")) return raw.split("→")[0].trim();
    return String(payload.player_name_out || "").trim();
}

function getSubInName(payload) {
    const raw = String(payload.player_name || "");
    if (raw.includes("→")) return raw.split("→")[1].trim();
    return String(payload.player_name_in || "").trim();
}

function buildVmixCommandsFromEvent(payload) {
    const eventType = String(payload?.event_type || "");
    const playerName = escapeVmixValue(payload?.player_name || "");
    const teamName = escapeVmixValue(payload?.team_name || "");
    const playerNumber = escapeVmixValue(payload?.player_number || "");
    const playerOutNumber = escapeVmixValue(payload?.player_out_number || "");
    const playerInNumber = escapeVmixValue(payload?.player_in_number || "");

    const teamLogo = getVmixTeamLogo(teamName);
    const scoreSum = getScoreSum();

    if (eventType === "period_start_1h") {
        return [
            vmixCmd("Function=SetText&Input=НИЖНИЙ СЧЕТ ВО ВРЕМЯ ИГРЫ&SelectedName=СтатусМатча.Text&Value=1-й тайм"),
            vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ ВО ВРЕМЯ ИГРЫ")
        ];
    }

    if (eventType === "period_ht") {
        return [
            scoreSum === 0
                ? vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ БЕЗ АВТОРОВ ГОЛОВ - ПЕРЕРЫВ")
                : vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ С АВТОРАМИ - ПЕРЕРЫВ")
        ];
    }

    if (eventType === "period_start_2h") {
        return [
            vmixCmd("Function=SetText&Input=НИЖНИЙ СЧЕТ ВО ВРЕМЯ ИГРЫ&SelectedName=СтатусМатча.Text&Value=2-й тайм"),
            vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ ВО ВРЕМЯ ИГРЫ")
        ];
    }

    if (eventType === "period_ft") {
        return [
            scoreSum === 0
                ? vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ БЕЗ АВТОРОВ ГОЛОВ - МАТЧ ОКОНЧЕН")
                : vmixCmd("Function=PreviewInput&Input=НИЖНИЙ СЧЕТ С АВТОРАМИ ГОЛОВ - МАТЧ ОКОНЧЕН")
        ];
    }

    if (eventType === "goal" || eventType === "penalty") {
        return [
            vmixCmd(`Function=SetText&Input=ГОЛ - НЕЙТРАЛЬНЫЙ&SelectedName=Игрок.Text&Value=${playerNumber} ${playerName}`),
            vmixCmd(`Function=SetImage&Input=ГОЛ - НЕЙТРАЛЬНЫЙ&SelectedName=Лого.Source&Value=${teamLogo}`),
            vmixCmd("Function=PreviewInput&Input=ГОЛ - НЕЙТРАЛЬНЫЙ")
        ];
    }

    if (eventType === "yellow") {
        return [
            vmixCmd(`Function=SetText&Input=ЖЕЛТАЯ - НЕЙТРАЛЬНАЯ&SelectedName=Игрок.Text&Value=${playerNumber} ${playerName}`),
            vmixCmd(`Function=SetImage&Input=ЖЕЛТАЯ - НЕЙТРАЛЬНАЯ&SelectedName=Лого.Source&Value=${teamLogo}`),
            vmixCmd("Function=PreviewInput&Input=ЖЕЛТАЯ - НЕЙТРАЛЬНАЯ")
        ];
    }

    if (eventType === "red") {
        return [
            vmixCmd(`Function=SetText&Input=КРАСНАЯ - НЕЙТРАЛЬНАЯ&SelectedName=Игрок.Text&Value=${playerNumber} ${playerName}`),
            vmixCmd(`Function=SetImage&Input=КРАСНАЯ - НЕЙТРАЛЬНАЯ&SelectedName=Лого.Source&Value=${teamLogo}`),
            vmixCmd("Function=PreviewInput&Input=КРАСНАЯ - НЕЙТРАЛЬНАЯ")
        ];
    }

    if (eventType === "sub") {
        const outName = escapeVmixValue(getSubOutName(payload));
        const inName = escapeVmixValue(getSubInName(payload));

        return [
            vmixCmd(`Function=SetText&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ&SelectedName=НомерИгрокУход.Text&Value=${playerOutNumber}`),
            vmixCmd(`Function=SetText&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ&SelectedName=ИгрокУходящий.Text&Value=${outName}`),
            vmixCmd(`Function=SetText&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ&SelectedName=НомерИгрокВыход.Text&Value=${playerInNumber}`),
            vmixCmd(`Function=SetText&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ&SelectedName=ИгрокВыходящий.Text&Value=${inName}`),
            vmixCmd(`Function=SetImage&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ&SelectedName=Лого.Source&Value=${teamLogo}`),
            vmixCmd("Function=PreviewInput&Input=ЗАМЕНА - НЕЙТРАЛЬНАЯ")
        ];
    }

    return [];
}

async function triggerVmixEventCommand(eventPayload) {
    const commands = buildVmixCommandsFromEvent(eventPayload);

    if (!commands.length) {
        console.warn("Для события нет команд vMix", eventPayload);
        return;
    }

    console.log("VMIX DIRECT EVENT:", eventPayload);
    console.log("VMIX DIRECT COMMANDS:", commands);

    // await executeVmixCommands(commands.join("\n"));
    await sendDirectVmixCommands(commands);
}


function getPlayerNumberByEvent(event) {
    if (!event) return "";

    if (event.type === "sub") {
        const outRow = findLineupRowByPlayerId(event.side, "starting", event.player_out_id)
            || findLineupRowByPlayerId(event.side, "bench", event.player_out_id);
        const inRow = findLineupRowByPlayerId(event.side, "starting", event.player_in_id)
            || findLineupRowByPlayerId(event.side, "bench", event.player_in_id);

        return {
            out: outRow?.dataset.playerNumber || "",
            in: inRow?.dataset.playerNumber || ""
        };
    }

    const row = findLineupRowByPlayerId(event.side, "starting", event.player_id)
        || findLineupRowByPlayerId(event.side, "bench", event.player_id);

    return row?.dataset.playerNumber || "";
}


function buildVmixClickAttr(payload) {
    const json = JSON.stringify(payload);
    const encoded = btoa(unescape(encodeURIComponent(json)));
    return `onclick="triggerVmixEventCommandFromEncoded('${encoded}')"`
}

function triggerVmixEventCommandFromEncoded(encodedPayload) {
    try {
        const json = decodeURIComponent(escape(atob(encodedPayload)));
        const payload = JSON.parse(json);
        triggerVmixEventCommand(payload);
    } catch (e) {
        console.warn("Не удалось разобрать payload для vMix", e);
    }
}

function syncFormationCaptainsInState(side) {
    const starting = side === "home"
        ? (window.MATCH_DATA.homeStarting || [])
        : (window.MATCH_DATA.awayStarting || []);

    const captainIds = new Set(
        starting
            .filter(p => !!p.is_captain)
            .map(p => String(p.player_id ?? p.id ?? p.number ?? ""))
    );

    if (!Array.isArray(formationState[side])) {
        formationState[side] = [];
    }

    formationState[side] = formationState[side].map(player => {
        const playerKey = String(player.player_id ?? player.id ?? player.number ?? "");
        return {
            ...player,
            is_captain: captainIds.has(playerKey)
        };
    });
}

function getPlayerNamesByEvent(event) {
    if (!event) return { out: "", in: "" };

    if (event.type !== "sub") {
        return {
            out: event.player_name || "",
            in: ""
        };
    }

    const outRow =
        findLineupRowByPlayerId(event.side, "starting", event.player_out_id) ||
        findLineupRowByPlayerId(event.side, "bench", event.player_out_id);

    const inRow =
        findLineupRowByPlayerId(event.side, "starting", event.player_in_id) ||
        findLineupRowByPlayerId(event.side, "bench", event.player_in_id);

    return {
        out: outRow?.dataset.playerName || "",
        in: inRow?.dataset.playerName || ""
    };
}

function renderEvents() {
    const list = document.getElementById("eventsList");
    if (!list) return;

    if (!matchEvents.length) {
        list.innerHTML = '<div class="empty-box">Событий пока нет.</div>';
        return;
    }

    const displayEvents = [...matchEvents].reverse();



    list.innerHTML = displayEvents.map((event) => {
        const compactTitle = String(event.title || "").includes(" — ")
            ? String(event.title).split(" — ").slice(1).join(" — ")
            : String(event.title || "");

        const deleteHtml = event.id !== null && event.id !== undefined && event.id !== ""
            ? `<button type="button" class="event-delete-btn" title="Удалить событие"
                onclick="event.stopPropagation(); deleteEvent('${String(event.id)}')">×</button>`
            : "";

        const canEditScorer = event.id !== null &&
            event.id !== undefined &&
            event.id !== "" &&
            ["goal", "penalty", "own_goal"].includes(event.type);

        const editHtml = canEditScorer
            ? `<button type="button" class="event-edit-btn" title="Изменить автора гола"
                onclick="event.stopPropagation(); openGoalEditor('${String(event.id)}')">✏️</button>`
            : "";

        if (event.side === "center") {
            const clickableTypes = [
                "period_start_1h",
                "period_ht",
                "period_start_2h",
                "period_ft",
                "extra_time_first",
                "extra_time_second"
            ];

            const isClickable = clickableTypes.includes(event.type);
            const eventNameData = getPlayerNamesByEvent(event);

            const clickAttr = isClickable
                ? buildVmixClickAttr({
                    event_type: event.type,
                    event_id: event.id ?? null,
                    side: event.side ?? "center",
                    minute: event.minute ?? "",
                    seconds: event.seconds ?? 0,
                    player_name: event.player_name ?? "",
                    player_id: event.player_id ?? null,
                    player_out_id: event.player_out_id ?? null,
                    player_in_id: event.player_in_id ?? null,
                    player_name_out: eventNameData.out ?? "",
                    player_name_in: eventNameData.in ?? "",
                    title: compactTitle ?? "",
                    team_name: ""
                })
                : "";

            const clickableClass = isClickable ? " event-card-clickable" : "";

            return `
                <div class="event-row event-row-center">
                    <div class="event-side"></div>
                    <div class="event-time event-time-center-card">
                        <div class="event-card center${clickableClass}" ${clickAttr}>
                            <div class="event-card-actions">${deleteHtml}</div>
                            <div class="event-title">${escapeHtml(compactTitle)}</div>
                        </div>
                    </div>
                    <div class="event-side"></div>
                </div>
            `;
        }

        let actionsHtml = "";
        if (event.side === "home") {
            actionsHtml = `${deleteHtml}${editHtml}`;
        } else {
            actionsHtml = `${editHtml}${deleteHtml}`;
        }

        const clickableTypes = [
            "goal",
            "penalty",
            "yellow",
            "red",
            "sub"
        ];

        const eventNumberData = getPlayerNumberByEvent(event);

        const isClickable = clickableTypes.includes(event.type);

        const clickAttr = isClickable
            ? buildVmixClickAttr({
                event_type: event.type,
                event_id: event.id ?? null,
                side: event.side ?? "",
                minute: event.minute ?? "",
                seconds: event.seconds ?? 0,
                player_name: event.player_name ?? "",
                player_id: event.player_id ?? null,
                player_out_id: event.player_out_id ?? null,
                player_in_id: event.player_in_id ?? null,
                player_number: typeof eventNumberData === "string" ? eventNumberData : "",
                player_out_number: typeof eventNumberData === "object" ? (eventNumberData.out ?? "") : "",
                player_in_number: typeof eventNumberData === "object" ? (eventNumberData.in ?? "") : "",
                title: compactTitle ?? "",
                team_name: event.teamName ?? TEAM_NAMES[event.side] ?? ""
            })
            : "";

        const clickableClass = isClickable ? " event-card-clickable" : "";

        const cardHtml = `
            <div class="event-card ${event.side}${clickableClass}" ${clickAttr}>
                <div class="event-card-actions">${actionsHtml}</div>
                <div class="event-badge">${escapeHtml(event.icon)}</div>
                <div class="event-title">${escapeHtml(compactTitle)}</div>
            </div>
        `;

        return `
            <div class="event-row">
                <div class="event-side">${event.side === "home" ? cardHtml : ""}</div>
                <div class="event-time">${escapeHtml(event.minute)}</div>
                <div class="event-side">${event.side === "away" ? cardHtml : ""}</div>
            </div>
        `;
    }).join("");
}

async function loadEventsFromServer() {
    if (!SESSION_TOKEN) return;

    try {
        const persisted = getPersistedState();
        const persistedEvents = Array.isArray(persisted?.matchEvents) ? persisted.matchEvents : [];

        const res = await fetch(`/admin/session/${SESSION_TOKEN}/events`);
        if (!res.ok) return;

        const data = await res.json();
        if (!Array.isArray(data)) return;

        matchEvents = data.map((event) => {
            const fallback = persistedEvents.find((p) =>
                String(p.type || "") === String(event.type || "") &&
                String(p.player_name || "") === String(event.player_name || "") &&
                String(p.seconds || "") === String(event.seconds || "")
            );
            const displayPlayerName = getDisplayNameForEvent(event);
            return {
                ...event,
                id: event.id ?? fallback?.id ?? null,
                side: event.side,
                icon: getEventIcon(event.type),
                title: getEventTitle(event.type, event.player_name),
                player_name: displayPlayerName,
                minute: typeof event.minute === "string" && event.minute.includes("'")
                    ? event.minute
                    : (fallback?.minute || getEventDisplayMinuteBySeconds(event.seconds || 0, event.type)),


                player_id: event.player_id ?? fallback?.player_id ?? null,
                player_out_id: event.player_out_id ?? fallback?.player_out_id ?? null,
                player_in_id: event.player_in_id ?? fallback?.player_in_id ?? null
            };
        });

        matchEvents = sortEventsForStorage(matchEvents);



        scoreState = { home: 0, away: 0 };
        matchEvents.forEach((event) => {
            if (["goal", "penalty", "own_goal"].includes(event.type)) {
                scoreState[event.side] = (scoreState[event.side] || 0) + 1;
            }
        });

        updateScoreUI();
        renderEvents();
        rebuildLineupsFromEvents();
        saveMatchState();
        updateCoachCardButtons();
        updatePeriodButtons();
    } catch (e) {
        console.warn("Не удалось загрузить события из БД", e);
    }
}

async function sendDirectVmixCommands(commands) {
    const items = Array.isArray(commands)
        ? commands.map(x => String(x || "").trim()).filter(Boolean)
        : String(commands || "").split("\n").map(x => x.trim()).filter(Boolean);

    if (!items.length) return;

    try {
        const resp = await fetch("/api/vmix/publish-command", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                match_id: MATCH_ID,
                target_client_id: VMIX_TARGET_CLIENT_ID,
                target_group: VMIX_TARGET_GROUP,
                target_login: CURRENT_USER_LOGIN,
                commands: items
            })
        });

        const data = await resp.json();

        if (!resp.ok) {
            console.warn("Ошибка публикации команд vMix:", data);
            alert("Не удалось отправить команды в vMix");
            return;
        }

        console.log("VMIX PUBLISH OK:", data);
    } catch (e) {
        console.warn("Ошибка отправки команд на сервер:", e);
        alert("Ошибка связи с сервером при отправке команд vMix");
    }
}


function syncFormationCaptainsFromMatchData(side) {
    const starting = side === "home"
        ? (window.MATCH_DATA.homeStarting || [])
        : (window.MATCH_DATA.awayStarting || []);

    if (!Array.isArray(formationState[side]) || formationState[side].length === 0) {
        formationState[side] = starting.slice(0, 11).map((p, idx) => ({
            player_id: p.player_id || p.id || p.number || null,
            player_name: p.player_name || `${p.last_name || ""} ${p.first_name || ""}`.trim(),
            last_name: p.last_name || p.player_name || "",
            first_name: p.first_name || "",
            number: p.number || "",
            position: p.position || "",
            is_captain: !!p.is_captain,
            photo: p.photo || "",
            x: 50,
            y: 12 + idx * 6,
        }));

        renderFormation(side);
        return;
    }

    const captainIds = new Set(
        starting
            .filter(p => !!p.is_captain)
            .map(p => String(p.player_id ?? p.id ?? p.number ?? ""))
    );

    formationState[side] = formationState[side].map(player => ({
        ...player,
        is_captain: captainIds.has(String(player.player_id ?? player.id ?? player.number ?? ""))
    }));

    renderFormation(side);
}


async function triggerVmixTimerAction(action) {
    const map = {
        first_half_start: [
            vmixCmd("Function=SetCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=02:00:00"),
            vmixCmd("Function=ChangeCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=00:00:00"),
            vmixCmd("Function=StartCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text"),
            vmixCmd("Function=PreviewInput&Input=ВЕРХНИЙ СЧЕТ")
        ],
        halftime: [
            vmixCmd("Function=PauseCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text"),
            vmixCmd("Function=OverlayInput1Out"),
            vmixCmd("Function=OverlayInput2Out")
        ],
        second_half_start: [
            vmixCmd("Function=SetCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=02:00:00"),
            vmixCmd("Function=ChangeCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=00:45:00"),
            vmixCmd("Function=StartCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text"),
            vmixCmd("Function=PreviewInput&Input=ВЕРХНИЙ СЧЕТ")
        ],
        full_time: [
            vmixCmd("Function=PauseCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text"),
            vmixCmd("Function=OverlayInput1Out"),
            vmixCmd("Function=OverlayInput2Out")
        ],
        clear_match: [
            vmixCmd("Function=SetCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=02:00:00"),
            vmixCmd("Function=ChangeCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=00:00:00"),
            vmixCmd("Function=PauseCountdown&Input=ВЕРХНИЙ СЧЕТ&SelectedName=Таймер.Text&Value=00:00:00"),
        ]
    };

    const commands = map[action] || [];
    if (!commands.length) return;

    await sendDirectVmixCommands(commands);
}

function clearExtraTimeInput() {
    const input = document.getElementById("extraTimeMinutes");
    if (input) input.value = "";
}

function clearTemporaryExpelledRows() {
    document.querySelectorAll('tr.lineup-row[data-temporary="1"]').forEach((row) => row.remove());
}

function getBenchTbody(side) {
    const benchTable = Array.from(document.querySelectorAll("tr.lineup-row"))
        .find((row) => row.dataset.side === side && row.dataset.role === "bench")
        ?.closest("tbody");
    return benchTable || null;
}

function createTemporaryExpelledRow(sourceRow, disciplineType) {
    const benchTbody = getBenchTbody(sourceRow.dataset.side);
    if (!benchTbody) return null;

    const tempRow = document.createElement("tr");
    tempRow.className = "lineup-row";
    tempRow.dataset.side = sourceRow.dataset.side || "";
    tempRow.dataset.role = "expelled";
    tempRow.dataset.temporary = "1";

    const player = getRowPlayer(sourceRow);
    player.disciplineType = disciplineType;
    setRowPlayer(tempRow, player);
    renderLineupRow(tempRow);
    tempRow.classList.add("lineup-row-sub-out");

    benchTbody.appendChild(tempRow);
    return tempRow;
}

function getPlayerDisciplineMap() {
    const map = new Map();

    matchEvents
        .filter((event) => event.side !== "center" && event.player_id)
        .sort((a, b) => {
            if ((a.seconds || 0) !== (b.seconds || 0)) return (a.seconds || 0) - (b.seconds || 0);
            return (a.id || 0) - (b.id || 0);
        })
        .forEach((event) => {
            const key = `${event.side}::${event.player_id}`;
            const current = map.get(key) || { yellow: 0, red: false, disciplineType: "" };

            if (event.type === "yellow") {
                current.yellow += 1;
                if (current.yellow >= 2 && !current.red) {
                    current.disciplineType = "yellow_red";
                }
            }

            if (event.type === "red") {
                current.red = true;
                current.disciplineType = "red";
            }

            map.set(key, current);
        });

    return map;
}

function applyDisciplinaryEvents() {
    clearTemporaryExpelledRows();

    Array.from(document.querySelectorAll("tr.lineup-row"))
        .filter((row) => row.dataset.temporary !== "1")
        .forEach((row) => {
            row.classList.remove("lineup-row-expelled-hidden");
            row.dataset.disciplineType = "";
            renderLineupRow(row);
        });

    const disciplineMap = getPlayerDisciplineMap();

    disciplineMap.forEach((value, key) => {
        if (!value.disciplineType) return;

        const [side, playerId] = key.split("::");

        let row = findLineupRowByPlayerId(side, "starting", playerId);
        if (row) {
            row.dataset.disciplineType = value.disciplineType;
            row.classList.add("lineup-row-expelled-hidden");
            createTemporaryExpelledRow(row, value.disciplineType);
            return;
        }

        row = findLineupRowByPlayerId(side, "bench", playerId);
        if (row) {
            row.dataset.disciplineType = value.disciplineType;
            renderLineupRow(row);
        }
    });

    sortLineupRows("home", "starting");
    sortLineupRows("home", "bench");
    sortLineupRows("away", "starting");
    sortLineupRows("away", "bench");
}

function sortLineupRows(side, role) {
    const rows = Array.from(document.querySelectorAll(
        `tr.lineup-row[data-side="${side}"][data-role="${role}"]`
    ));

    const tbody = rows[0]?.closest("tbody");
    if (!tbody) return;

    rows.sort((a, b) => {
        const pa = getRowPlayer(a);
        const pb = getRowPlayer(b);

        const gkA = isGoalkeeperPosition(pa.position) ? 1 : 0;
        const gkB = isGoalkeeperPosition(pb.position) ? 1 : 0;

        if (gkA !== gkB) return gkB - gkA;

        const numA = parseInt(pa.number, 10);
        const numB = parseInt(pb.number, 10);

        const safeA = Number.isFinite(numA) ? numA : 999;
        const safeB = Number.isFinite(numB) ? numB : 999;

        return safeA - safeB;
    });

    rows.forEach(row => tbody.appendChild(row));
}

function captureCurrentLineupState() {
    const rows = Array.from(document.querySelectorAll("tr.lineup-row"));
    return rows.map((row, index) => ({
        index,
        side: row.dataset.side || "",
        role: row.dataset.role || "",
        player: getRowPlayer(row)
    }));
}

function restoreOriginalLineupRows() {
    if (!Array.isArray(originalLineupState) || !originalLineupState.length) return;

    clearTemporaryExpelledRows();

    const rows = Array.from(document.querySelectorAll("tr.lineup-row"))
        .filter((row) => row.dataset.temporary !== "1");

    originalLineupState.forEach((item) => {
        const row = rows[item.index];
        if (!row) return;
        row.classList.remove("lineup-row-expelled-hidden", "lineup-row-sub-in", "lineup-row-sub-out", "bench-pick-active");
        setRowPlayer(row, { ...item.player, disciplineType: "" });
        renderLineupRow(row);
    });

    sortLineupRows("home", "starting");
    sortLineupRows("home", "bench");
    sortLineupRows("away", "starting");
    sortLineupRows("away", "bench");
}

function findLineupRowByPlayerId(side, role, playerId) {
    if (playerId === null || playerId === undefined || playerId === "") return null;

    return Array.from(document.querySelectorAll("tr.lineup-row")).find((row) =>
        row.dataset.temporary !== "1" &&
        String(row.dataset.side || "") === String(side) &&
        String(row.dataset.role || "") === String(role) &&
        String(row.dataset.playerId || "") === String(playerId)
    ) || null;
}

function swapLineupPlayers(startingRow, benchRow) {
    const startingPlayer = getRowPlayer(startingRow);
    const benchPlayer = getRowPlayer(benchRow);

    setRowPlayer(startingRow, benchPlayer);
    setRowPlayer(benchRow, startingPlayer);

    renderLineupRow(startingRow);
    renderLineupRow(benchRow);
    sortLineupRows(startingRow.dataset.side, "starting");
    sortLineupRows(startingRow.dataset.side, "bench");
    startingRow.classList.add("lineup-row-sub-in");
    benchRow.classList.add("lineup-row-sub-out");
    saveMatchState();
}

function rebuildLineupsFromEvents() {
    restoreOriginalLineupRows();

    const subEvents = matchEvents
        .filter((event) => event.type === "sub" && event.player_out_id && event.player_in_id)
        .sort((a, b) => {
            if ((a.seconds || 0) !== (b.seconds || 0)) return (a.seconds || 0) - (b.seconds || 0);
            return (a.id || 0) - (b.id || 0);
        });

    subEvents.forEach((event) => {
        const outRow = findLineupRowByPlayerId(event.side, "starting", event.player_out_id);
        const inRow = findLineupRowByPlayerId(event.side, "bench", event.player_in_id);
        if (outRow && inRow) swapLineupPlayers(outRow, inRow);
    });

    applyDisciplinaryEvents();
}

async function selectBenchPlayer(side, playerId, playerName, btn) {
    if (!activeSubstitution || activeSubstitution.side !== side) return;

    clearBenchSelection();
    if (btn) btn.classList.add("bench-pick-active");

    const benchRow = btn ? btn.closest("tr.lineup-row") : null;
    const outRow = activeSubstitution.outRow || null;

    const outName = outRow
        ? getRowDisplayName(outRow)
        : activeSubstitution.outPlayerName;

    const inName = benchRow
        ? getRowDisplayName(benchRow)
        : playerName;

    if (outRow && benchRow) {
        swapLineupPlayers(outRow, benchRow);
    }

    const minuteLabel = formatMinuteLabel(matchClockSeconds);
    const numericMinute =
        parseInt(String(minuteLabel).split("+").pop().replace("'", ""), 10) ||
        getFootballMinute(matchClockSeconds);

    const item = {
        id: null,
        side,
        type: "sub",
        icon: getEventIcon("sub"),
        title: `${outName} → ${inName}`,
        minute: minuteLabel,
        seconds: matchClockSeconds,
        player_name: `${outName} → ${inName}`,
        player_out_id: activeSubstitution.outPlayerId,
        player_in_id: playerId,
        player_id: null
    };

    matchEvents.push(item);
    renderEvents();
    rebuildLineupsFromEvents();
    saveMatchState();

    const saved = await saveEventToServer({
        side,
        type: "sub",
        player_name: `${outName} → ${inName}`,
        player_out_id: activeSubstitution.outPlayerId,
        player_in_id: playerId,
        player_id: null,
        minute: numericMinute,
        seconds: matchClockSeconds,
        meta: null
    });

    if (saved && saved.id) {
        item.id = saved.id;
        renderEvents();
        rebuildLineupsFromEvents();
        saveMatchState();
    }

    activeSubstitution = null;
    clearBenchSelection();
}


function ensureInitialState(side) {
    if (!formationState[side] || formationState[side].length === 0) {
        const currentStarting = side === "home"
            ? (window.MATCH_DATA.homeStarting || [])
            : (window.MATCH_DATA.awayStarting || []);

        formationState[side] = currentStarting.slice(0, 11).map((p, idx) => ({
            player_id: p.player_id || p.id || p.number || null,
            player_name: p.player_name || `${p.last_name || ""} ${p.first_name || ""}`.trim(),
            last_name: p.last_name || p.player_name || "",
            first_name: p.first_name || "",
            number: p.number || "",
            position: p.position || "",
            is_captain: !!p.is_captain,
            photo: p.photo || "",
            x: 50,
            y: 12 + idx * 6,
        }));
    }
}

function extractLastName(player) {
    return String(player.last_name || "").trim().toUpperCase();
}

function buildPlayerSuffix(player) {
    const pos = String(player.position || "").trim().toLowerCase();
    const isGoalkeeper = ["вр.", "вр", "вратарь", "gk", "goalkeeper"].includes(pos);
    const isCaptain = !!player.is_captain;

    if (isGoalkeeper && isCaptain) return " (ВР, К)";
    if (isGoalkeeper) return " (ВР)";
    if (isCaptain) return " (К)";
    return "";
}

function buildPlayerLabel(player) {
    const number = player.number || "";
    const lastName = extractLastName(player);
    return `${number} ${lastName}${buildPlayerSuffix(player)}`.trim();
}

function createPlayerNode(player, side) {
    const el = document.createElement("div");
    el.className = "player-node";
    el.dataset.playerId = player.player_id ?? "";
    el.dataset.side = side;
    const safeX = clampWebPercent(player.x);
    const safeY = clampWebPercent(player.y);

    el.style.left = `${safeX}%`;
    el.style.top = `${safeY}%`;


    const visual = player.photo
        ? `<img src="${player.photo}" alt="${player.player_name || ""}" class="player-photo">`
        : `<div class="player-silhouette"></div>`;

    el.innerHTML = `
        ${visual}
        <div class="player-label">
            <span class="player-label-text">${buildPlayerLabel(player)}</span>
        </div>
    `;

    makeDraggable(el);
    return el;
}


function clampWebPercent(value) {
    const n = Number(value);
    if (!Number.isFinite(n)) return 50;
    return Math.max(4, Math.min(96, n));
}

function renderFormation(side) {
    ensureInitialState(side);
    syncFormationCaptainsInState(side);

    const pitch = document.getElementById(`pitch-${side}`);
    if (!pitch) return;

    const overlays = pitch.querySelectorAll(".player-node");
    overlays.forEach(node => node.remove());

    const players = Array.isArray(formationState[side]) ? formationState[side] : [];

    players.forEach(player => {
        pitch.appendChild(createPlayerNode(player, side));
    });
}


function isGoalkeeperPosition(position) {
    const pos = String(position || "").trim().toLowerCase();
    return ["вр.", "вр", "вратарь", "gk", "goalkeeper"].includes(pos);
}

function getRowPlayer(row) {
    return {
        id: row.dataset.playerId || "",
        name: row.dataset.playerName || "",
        lastName: row.dataset.playerLast || "",
        firstName: row.dataset.playerFirst || "",
        number: row.dataset.playerNumber || "",
        position: row.dataset.playerPosition || "",
        isCaptain: row.dataset.playerCaptain === "1",
        disciplineType: row.dataset.disciplineType || ""
    };
}

function setRowPlayer(row, player) {
    row.dataset.playerId = player.id ?? "";
    row.dataset.playerName = player.name ?? "";
    row.dataset.playerLast = player.lastName ?? "";
    row.dataset.playerFirst = player.firstName ?? "";
    row.dataset.playerNumber = player.number ?? "";
    row.dataset.playerPosition = player.position ?? "";
    row.dataset.playerCaptain = player.isCaptain ? "1" : "0";
    row.dataset.disciplineType = player.disciplineType ?? "";
}

function getDisplayNameForEvent(event) {
    if (!event) return "";

    if (event.type === "sub") {
        const subNames = getSubstitutionDisplayNames(event);
        if (subNames.out || subNames.in) {
            return `${subNames.out} → ${subNames.in}`.trim();
        }
        return String(event.player_name || "").trim();
    }

    if (event.player_id) {
        const row =
            findLineupRowByPlayerId(event.side, "starting", event.player_id) ||
            findLineupRowByPlayerId(event.side, "bench", event.player_id);

        if (row) {
            return getRowDisplayName(row);
        }
    }

    return String(event.player_name || "").trim();
}

function buildPersonDisplayName(firstName, lastName, fallback = "") {
    const first = String(firstName || "").trim();
    const last = String(lastName || "").trim();
    const safeFallback = String(fallback || "").trim();

    if (first && last) return `${first} ${last}`;
    if (last) return last;
    if (first) return first;
    return safeFallback;
}

function getRowDisplayName(row) {
    if (!row) return "";
    return buildPersonDisplayName(
        row.dataset.playerFirst || "",
        row.dataset.playerLast || "",
        row.dataset.playerName || ""
    );
}

function getSubstitutionDisplayNames(event) {
    if (!event || event.type !== "sub") {
        return { out: "", in: "" };
    }

    const outRow =
        findLineupRowByPlayerId(event.side, "starting", event.player_out_id) ||
        findLineupRowByPlayerId(event.side, "bench", event.player_out_id);

    const inRow =
        findLineupRowByPlayerId(event.side, "starting", event.player_in_id) ||
        findLineupRowByPlayerId(event.side, "bench", event.player_in_id);

    return {
        out: getRowDisplayName(outRow),
        in: getRowDisplayName(inRow)
    };
}

function renderLineupRow(row) {
    const side = row.dataset.side;
    const role = row.dataset.role;
    const player = getRowPlayer(row);

    const displayName = String(player.name || "").trim()
    || buildPersonDisplayName(player.firstName, player.lastName, "");

    const gkBadge = isGoalkeeperPosition(player.position) ? '<span class="gk-badge">ВР</span>' : '';
    const captainBadge = player.isCaptain ? '<span class="captain-badge">К</span>' : '';
    const disciplineBadge = player.disciplineType === "yellow_red"
        ? '<span class="discipline-badge-yellow2">2ЖК</span>'
        : (player.disciplineType === "red"
            ? '<span class="discipline-badge-red">КК</span>'
            : '');

    const firstNameHtml = player.firstName
        ? `<span class="first-name"> ${escapeHtml(player.firstName)}</span>`
        : '';

    const idJs = JSON.stringify(player.id || player.number || "");
    const nameJs = JSON.stringify(displayName);

    let actionsHtml = '';
    if (role === "starting") {
        actionsHtml = `
            <div class="row-actions">
                <button type="button" class="icon-btn goal" title="Гол"
                    onclick='addPlayerEvent("${side}", "goal", ${idJs}, ${nameJs})'>⚽</button>
                <button type="button" class="icon-btn" title="Автогол"
                    onclick='addPlayerEvent("${side}", "own_goal", ${idJs}, ${nameJs})'>АГ</button>
                <button type="button" class="icon-btn" title="Пенальти"
                    onclick='addPlayerEvent("${side}", "penalty", ${idJs}, ${nameJs})'>П</button>
                <button type="button" class="icon-btn sub" title="Замена"
                    onclick='beginSubstitution("${side}", ${idJs}, ${nameJs}, this)'>⇄</button>
                <button type="button" class="icon-btn yellow-card" title="Жёлтая карточка"
                    onclick='addPlayerEvent("${side}", "yellow", ${idJs}, ${nameJs})'></button>
                <button type="button" class="icon-btn red-card" title="Красная карточка"
                    onclick='addPlayerEvent("${side}", "red", ${idJs}, ${nameJs})'></button>
            </div>
        `;
    } else if (role === "bench") {
        actionsHtml = `
            <div class="row-actions">
                <button type="button" class="icon-btn sub" title="Замена"
                    onclick='selectBenchPlayer("${side}", ${idJs}, ${nameJs}, this)'>⇄</button>
            </div>
        `;
    } else {
        actionsHtml = `<div class="row-actions"></div>`;
    }

    row.innerHTML = `
        <td class="num-cell">${escapeHtml(player.number || "")}</td>
        <td class="player-cell" title="Показать титр игрока">
            <button
                type="button"
                class="player-link-btn vmix-person-trigger"
                data-vmix-person-type="player"
                data-side="${escapeHtml(side)}"
                data-first-name="${escapeHtml(player.firstName || "")}"
                data-last-name="${escapeHtml(player.lastName || "")}"
                data-role="${escapeHtml(player.position || "")}"
            >
                <div class="player-inline">
                    <div class="player-inline-text">
                        <span class="last-name">${escapeHtml(player.lastName || player.name || "")}</span>${firstNameHtml}
                    </div>
                    <span class="badges">
                        ${gkBadge}
                        ${captainBadge}
                        ${disciplineBadge}
                    </span>
                </div>
            </button>
        </td>
        <td class="actions-col">${actionsHtml}</td>
    `;
}

document.addEventListener("click", async function (event) {
    const tab = event.target.closest(".tab-link[data-vmix-title]");
    if (!tab) return;

    event.preventDefault();

    const title = String(tab.dataset.vmixTitle || "").trim();

    try {
        if (title) {
            await sendDirectVmixCommands([
                vmixCmd(`Function=PreviewInput&Input=${encodeURIComponent(title)}`)
            ]);
        }
    } finally {
        window.location.href = tab.href;
    }
});


function makeDraggable(el) {
    let dragging = false;

    el.addEventListener("pointerdown", (e) => {
        if (!editModeEnabled) return;

        e.preventDefault();
        dragging = true;
        el.setPointerCapture(e.pointerId);
        el.classList.add("dragging");
    });

    el.addEventListener("pointermove", (e) => {
        if (!editModeEnabled || !dragging) return;

        const pitch = el.closest(".pitch-half");
        if (!pitch) return;

        const rect = pitch.getBoundingClientRect();

        let x = ((e.clientX - rect.left) / rect.width) * 100;
        let y = ((e.clientY - rect.top) / rect.height) * 100;

        x = Math.max(4, Math.min(96, x));
        y = Math.max(4, Math.min(96, y));

        el.style.left = `${x}%`;
        el.style.top = `${y}%`;

        const side = el.dataset.side;
        const playerId = el.dataset.playerId;
        const list = formationState[side] || [];
        const item = list.find((p) => String(p.player_id) === String(playerId));

        if (item) {
            item.x = x;
            item.y = y;
        }
    });

    const stopDrag = (e) => {
        if (!dragging) return;

        dragging = false;
        el.classList.remove("dragging");

        try {
            el.releasePointerCapture(e.pointerId);
        } catch (_) {}

        saveMatchState();
    };

    el.addEventListener("pointerup", stopDrag);
    el.addEventListener("pointercancel", stopDrag);
}

document.addEventListener("DOMContentLoaded", () => {
    setEditMode(false);
    renderRefereesEditor();
});


async function saveFormation(side) {
    syncFormationCaptainsInState(side);

    const response = await fetch(`${window.location.pathname}/formations/save`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            side,
            players: formationState[side]
        })
    });

    const data = await response.json();
    if (data.success) {
        await sendFormationToVmix(side);
        alert("Расстановка сохранена");
    } else {
        alert("Ошибка сохранения");
    }
}


restoreSelectedPreset("home");
restoreSelectedPreset("away");
rebuildFormationFromMatchData("home");
rebuildFormationFromMatchData("away");
sortLineupRows("home", "starting");
sortLineupRows("home", "bench");
sortLineupRows("away", "starting");
sortLineupRows("away", "bench");

// берём актуальный состав прямо из HTML, который пришёл с сервера
originalLineupState = captureCurrentLineupState();

const restored = restoreMatchState();
saveMatchState();

if (!restored) {
    updateTimerUI();
    updateScoreUI();
    renderEvents();
    saveMatchState();
}

loadEventsFromServer();
window.addEventListener("beforeunload", saveMatchState);
window.addEventListener("pagehide", saveMatchState);
document.addEventListener("visibilitychange", () => {
    if (document.visibilityState === "hidden") {
        saveMatchState();
    }
});

const VMIX_ORIGIN = {
    x: 0.003,
    y: -0.075
};

const VMIX_X_MIN = -0.338;
const VMIX_X_MAX = 0.346;
const VMIX_Y_TOP = 0;
const VMIX_Y_BOTTOM = -1.535;
// const VMIX_Y_OFFSET = -0.225; // подгонишь руками

function getFormationVmixInputName(side) {
    return side === "home"
        ? "РАССТАНОВКА - ХОЗЯЕВА"
        : "РАССТАНОВКА - ГОСТИ";
}


function clampVmixPercent(value) {
    const num = Number(value);
    if (!Number.isFinite(num)) return 0;
    return Math.max(0, Math.min(100, num));
}

function convertPitchXToPan(x) {
    const percent = clampVmixPercent(x) / 100;
    return VMIX_X_MIN + percent * (VMIX_X_MAX - VMIX_X_MIN);
}

function convertPitchYToPan(y) {
    const percent = clampVmixPercent(y) / 100;
    return VMIX_Y_TOP + percent * (VMIX_Y_BOTTOM - VMIX_Y_TOP);
}


function buildVmixCommandsFromFormation(side) {
    const players = Array.isArray(formationState[side]) ? formationState[side] : [];
    const inputName = getFormationVmixInputName(side);
    const commands = [];
    const playerPrefix = side === "home" ? "HOME" : "AWAY";

    if (players.length < 2) {
        return [];
    }

    // Сбрасываем только свою сторону
    for (let i = 1; i <= 10; i++) {
        commands.push(
            vmixCmd(`Function=ResetInput&Input=${encodeURIComponent(`${playerPrefix} - Игрок${i}`)}`)
        );
    }

    commands.push(
        vmixCmd(`Function=ResetInput&Input=${encodeURIComponent(inputName)}`)
    );

    // Вратарь players[0], полевые players[1..10]
    players.slice(1, 11).forEach((player, index) => {
        const layer = index + 1;
        const x = convertPitchXToPan(player.x).toFixed(3);
        const y = convertPitchYToPan(player.y).toFixed(3);

        commands.push(
            vmixCmd(`Function=SetLayer${layer}PanX&Input=${encodeURIComponent(inputName)}&Value=${x}`)
        );

        commands.push(
            vmixCmd(`Function=SetLayer${layer}PanY&Input=${encodeURIComponent(inputName)}&Value=${y}`)
        );
    });

    commands.push(
        vmixCmd(`Function=PreviewInput&Input=${encodeURIComponent(inputName)}`)
    );

    return commands;
}


async function sendFormationToVmix(side) {
    syncFormationCaptainsInState(side);

    const commands = buildVmixCommandsFromFormation(side);

    if (!commands.length) {
        alert("Нет данных для отправки расстановки в vMix");
        return;
    }

    console.log(`FORMATION VMIX ${side}`, formationState[side]);
    console.log(`FORMATION VMIX COMMANDS ${side}`, commands);

    await sendDirectVmixCommands(commands);
}
const FORMATION_COORDS = {
    "4-4-2": [
        { x: 50, y: 10 },
        { x: 0, y: 40 }, { x: 30, y: 30 }, { x: 70, y: 30 }, { x: 100, y: 40 },
        { x: 0, y: 75 }, { x: 30, y: 65 }, { x: 70, y: 65 }, { x: 100, y: 75 },
        { x: 25, y: 100 }, { x: 75, y: 100 }
    ]
};

const BASE_GEOMETRY = {
    goalkeeper: { x: 50, y: 10 },

    rowsY: {
        defenders: 35,
        midfielders: 70,
        forwards: 100
    },

    rowProfiles: {
        1: { xSlots: [50], arc: 0 },
        2: { xSlots: [25, 75], arc: 0 },

        3: { xSlots: [8, 50, 92], arc: 0 },
        4: { xSlots: [0, 30, 70, 100], arc: 10 },
        5: { xSlots: [0, 24, 50, 78, 100], arc: 20, yOffset: 5 },
        6: { xSlots: [0, 16, 36, 64, 84, 100], arc: 0 }
    }
};

function distributeLine(count, yCenter, profile) {
    const { minX, maxX, arc = 0, xSlots = null, yOffset = 0  } = profile;

    if (count <= 0) return [];
    if (count === 1) return [{ x: 50, y: yCenter }];

    const xs = Array.isArray(xSlots) && xSlots.length === count
        ? xSlots
        : Array.from({ length: count }, (_, i) => minX + ((maxX - minX) * i / (count - 1)));

    // Специальная полудуга для линии из 5 игроков
    if (count === 5 && arc > 0) {
        const offsets = [
            arc * 0.11,   // левый фланг чуть ниже базы
            -arc * 0.5,   // левый полуфланг выше
            -arc,         // центр выше всех
            -arc * 0.5,   // правый полуфланг выше
            arc * 0.11    // правый фланг чуть ниже базы
        ];

        return xs.map((x, i) => ({
            x,
            y: yCenter + offsets[i] + yOffset
        }));
    }

    // Общая логика для остальных линий
    const center = (count - 1) / 2;

    return xs.map((x, i) => {
        let y = yCenter;

        if (arc > 0) {
            const dist = Math.abs(i - center);
            const maxDist = center || 1;
            const normalized = 1 - dist / maxDist;
            y = yCenter - arc * normalized;
        }

        return { x, y: y + yOffset };
    });
}

const FORMATION_TEMPLATE_442 = {
    gk: { x: 50, y: 10 },

    rows: [
        { y: 35, xSlots: [0, 30, 70, 100] },   // защита 4
        { y: 70, xSlots: [0, 30, 70, 100] },   // полузащита 4
        { y: 100, xSlots: [25, 75] }           // нападение 2
    ]
};

// function getDistributedXSlots(count) {
//     if (count <= 0) return [];
//     if (count === 1) return [50];
//     if (count === 2) return [25, 75];
//     if (count === 3) return [8, 50, 92];
//     if (count === 4) return [0, 30, 70, 100];
//     if (count === 5) return [0, 18, 50, 82, 100];
//     if (count === 6) return [0, 16, 36, 64, 84, 100];

//     return Array.from({ length: count }, (_, i) => (i / (count - 1)) * 100);
// }

// function getArcYOffsetSlots(count, strength = 10) {
//     if (count < 4) {
//         return Array(count).fill(0);
//     }

//     if (count === 5) {
//         return [
//             strength * 0.11,   // левый фланг ниже
//             -strength * 0.5, // левый полуфланг чуть выше базы
//             -strength,        // центр выше всех
//             -strength * 0.5, // правый полуфланг чуть выше базы
//             strength * 0.11    // правый фланг ниже
//         ];
//     }

//     const center = (count - 1) / 2;

//     return Array.from({ length: count }, (_, i) => {
//         const dist = Math.abs(i - center);
//         const maxDist = center || 1;
//         const normalized = dist / maxDist;

//         return -strength * Math.pow(1 - normalized, 1.5);
//     });
// }

function clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
}

function lerp(a, b, t) {
    return a + (b - a) * t;
}

function distributeEvenly(count, start, end) {
    if (count <= 0) return [];
    if (count === 1) return [(start + end) / 2];

    const step = (end - start) / (count - 1);
    return Array.from({ length: count }, (_, i) => start + step * i);
}

function getRowSpacingFactor(rowIndex, rowCount, options = {}) {
    // можно управлять “сжатием” ближе к воротам или к атаке
    const {
        curve = 1 // 1 = линейно, >1 сильнее прижимает первые линии
    } = options;

    if (rowCount <= 1) return 1;
    const t = rowIndex / (rowCount - 1);
    return Math.pow(t, curve);
}

function getHorizontalInsetsForRow(count, rowType, options = {}) {
    const {
        sideInset = 8,
        wideRowExtraInset = 0,
        strikerInset = 18
    } = options;

    // можно по типу линии менять ширину доступной зоны
    if (rowType === "forwards" && count <= 2) {
        return { left: strikerInset, right: strikerInset };
    }

    if (count >= 5) {
        return {
            left: sideInset + wideRowExtraInset,
            right: sideInset + wideRowExtraInset
        };
    }

    return { left: sideInset, right: sideInset };
}

function getRowArcOffsets(count, arcStrength = 0, direction = "up") {
    if (count <= 1 || arcStrength === 0) {
        return Array(count).fill(0);
    }

    const center = (count - 1) / 2;
    const sign = direction === "down" ? 1 : -1;

    return Array.from({ length: count }, (_, i) => {
        const dist = Math.abs(i - center);
        const maxDist = center || 1;
        const normalized = 1 - (dist / maxDist); // центр = 1, края = 0
        return sign * arcStrength * Math.pow(normalized, 1.4);
    });
}

function getRowTopLift(profile, count) {
    const arc = Number(profile?.arc || 0);
    const yOffset = Number(profile?.yOffset || 0);

    if (count === 5 && arc > 0) {
        // у пятёрки самая верхняя точка — центр
        return Math.max(0, arc - yOffset);
    }

    if (count > 1 && arc > 0) {
        // для обычной дуги максимум подъёма в центре
        return Math.max(0, arc - yOffset);
    }

    return Math.max(0, -yOffset);
}



function buildFormationCoords(formation) {
    const parts = String(formation).split("-").map(Number).filter(Number.isFinite);
    const coords = [];

    coords.push(BASE_GEOMETRY.goalkeeper);

    if (!parts.length) {
        return coords;
    }
    const rowCount = parts.length;
    const firstRowY = 25;
    let rowGap = 23;

    if (formation === "4-1-4-1") {
        rowGap = 16;
    }

    let previousBottomY = firstRowY - rowGap;

    parts.forEach((count, idx) => {
        const profile = BASE_GEOMETRY.rowProfiles[count] || {
            minX: 10,
            maxX: 90,
            arc: 0
        };

        const topLift = getRowTopLift(profile, count);

        // стартуем новую линию так, чтобы её верхняя точка была ниже предыдущей линии + gap
        const yCenter = previousBottomY + rowGap + topLift;

        const rowCoords = distributeLine(count, yCenter, profile);
        coords.push(...rowCoords);

        previousBottomY = Math.max(...rowCoords.map(p => p.y));
    });

    return coords;
}

function generateFormationCoordsFromLayout(preset) {
    const layout = FORMATION_ASSIGN_LAYOUTS[preset];
    if (!layout) return [];

    return buildFormationCoords(preset);
}


function generateFormationCoordsFromLayout2(preset) {
    const layout = FORMATION_ASSIGN_LAYOUTS[preset];
    if (!layout || !layout.length) return [];

    const coords = [];

    const gk = FORMATION_COORDS["4-4-2"]?.[0] || { x: 50, y: 10 };
    coords.push({ x: gk.x, y: gk.y });

    const fieldRows = layout.slice(1);
    const rowCount = fieldRows.length;

    const yStart = 35;
    const yEnd = 100;

    fieldRows.forEach((row, rowIndex) => {
        const count = row.length;

        const baseY =
            rowCount === 1
                ? yEnd
                : yStart + ((yEnd - yStart) * rowIndex / (rowCount - 1));

        const xSlots = getDistributedXSlots(count);

        let arcStrength = count >= 4 ? 10 : 0;

        // Для 3-5-2 средняя линия из 5 должна быть ровно посередине
        if (preset === "3-5-2" && rowIndex === 1 && count === 5) {
            arcStrength = 20;
        }

        const arcOffsets = getArcYOffsetSlots(count, arcStrength);

        xSlots.forEach((x, i) => {
            coords.push({
                x,
                y: baseY + arcOffsets[i]
            });
        });
    });

    return coords;
}

const FORMATION_ASSIGN_LAYOUTS = {
    "4-4-2": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["LM", "LCM", "RCM", "RM"],
        ["LS", "RS"]
    ],
    "4-3-3": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["LCM", "CM", "RCM"],
        ["LW", "ST", "RW"]
    ],
    "4-2-3-1": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["LDM", "RDM"],
        ["LAM", "CAM", "RAM"],
        ["ST"]
    ],
    "3-5-2": [
        ["GK"],
        ["LCB", "CB", "RCB"],
        ["LM", "LCM", "CM", "RCM", "RM"],
        ["LS", "RS"]
    ],
    "5-3-2": [
        ["GK"],
        ["LWB", "LCB", "CB", "RCB", "RWB"],
        ["LCM", "CM", "RCM"],
        ["LS", "RS"]
    ],
        "4-1-4-1": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["CDM"],
        ["LM", "LCM", "RCM", "RM"],
        ["ST"]
    ],

    "3-4-3": [
        ["GK"],
        ["LCB", "CB", "RCB"],
        ["LM", "LCM", "RCM", "RM"],
        ["LW", "ST", "RW"]
    ],

    "4-5-1": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["LM", "LCM", "CM", "RCM", "RM"],
        ["ST"]
    ],

    "5-4-1": [
        ["GK"],
        ["LWB", "LCB", "CB", "RCB", "RWB"],
        ["LM", "LCM", "RCM", "RM"],
        ["ST"]
    ],

    "4-3-1-2": [
        ["GK"],
        ["LB", "LCB", "RCB", "RB"],
        ["LCM", "CM", "RCM"],
        ["CAM"],
        ["LS", "RS"]
    ],

    "3-6-1": [
        ["GK"],
        ["LCB", "CB", "RCB"],
        ["LM", "LCM", "CM", "RCM", "RM", "CAM"],
        ["ST"]
    ]
};

let formationAssignState = {
    side: null,
    preset: null
};

function getFormationAssignSquad(side) {
    return side === "home"
        ? (window.MATCH_DATA.homeStarting || [])
        : (window.MATCH_DATA.awayStarting || []);
}

function getFormationAssignTeamName(side) {
    return side === "home"
        ? (window.MATCH_DATA.teamNames?.home || "")
        : (window.MATCH_DATA.teamNames?.away || "");
}

function getFormationAssignPhoto(player) {
    return player.photo || player.player_photo || player.image || player.avatar || "";
}

function getFormationAssignPlayerName(player) {
    return `${player.last_name || player.player_name || ""} ${player.first_name || ""}`.trim();
}

function getExistingAssignedNumber(side, rowIndex, colIndex) {
    const list = Array.isArray(formationState[side]) ? formationState[side] : [];
    const item = list.find(p => Number(p.row_index) === rowIndex && Number(p.col_index) === colIndex);
    return item ? String(item.number || "") : "";
}

function openFormationAssignModal(side) {
    const presetSelect = document.getElementById(`${side}-preset`);
    if (!presetSelect) return;

    const preset = presetSelect.value;
    const layout = FORMATION_ASSIGN_LAYOUTS[preset];
    if (!layout) return;

    formationAssignState.side = side;
    formationAssignState.preset = preset;

    const subtitle = document.getElementById("formationAssignSubtitle");
    if (subtitle) {
        subtitle.textContent = `${getFormationAssignTeamName(side)} — схема ${preset}`;
    }

    const squad = getFormationAssignSquad(side);
    const wrap = document.getElementById("formationAssignTableWrap");
    if (!wrap) return;

    let html = `
        <table class="formation-input-table">
            <thead>
                <tr>
                    <th>Линия</th>
                    <th>Позиция</th>
                    <th>№ игрока</th>
                    <th>Доступные номера</th>
                </tr>
            </thead>
            <tbody>
    `;

    layout.forEach((row, rowIndex) => {
        row.forEach((pos, colIndex) => {
            const existingNumber = getExistingAssignedNumber(side, rowIndex, colIndex);

            html += `
                <tr>
                    <td>${rowIndex + 1}</td>
                    <td>${pos}</td>
                    <td>
                        <input
                            type="number"
                            min="1"
                            class="formation-number-input"
                            data-row="${rowIndex}"
                            data-col="${colIndex}"
                            data-pos="${pos}"
                            value="${existingNumber}"
                            placeholder="номер"
                        >
                    </td>
                    <td class="formation-available-numbers">${squad.map(p => p.number).filter(Boolean).join(", ")}</td>
                </tr>
            `;
        });
    });

    html += `</tbody></table>`;
    wrap.innerHTML = html;

    document.getElementById("formationAssignModal")?.classList.add("show");
}

function closeFormationAssignModal() {
    document.getElementById("formationAssignModal")?.classList.remove("show");
}

function confirmFormationAssign() {
    const side = formationAssignState.side;
    const preset = formationAssignState.preset;

    if (!side || !preset) return;

    const layout = FORMATION_ASSIGN_LAYOUTS[preset];
    const squad = getFormationAssignSquad(side);
    const inputs = document.querySelectorAll("#formationAssignTableWrap .formation-number-input");

    const usedNumbers = new Set();
    const players = [];

    for (const input of inputs) {
        const rawNumber = String(input.value || "").trim();
        if (!rawNumber) continue;

        if (usedNumbers.has(rawNumber)) {
            alert(`Номер ${rawNumber} указан дважды.`);
            return;
        }
        usedNumbers.add(rawNumber);

        const player = squad.find(p => String(p.number || "").trim() === rawNumber);
        if (!player) {
            alert(`Игрок с номером ${rawNumber} не найден в стартовом составе.`);
            return;
        }

        players.push({
            player_id: player.player_id || player.id || player.number || "",
            player_name: getFormationAssignPlayerName(player),
            last_name: player.last_name || player.player_name || "",
            first_name: player.first_name || "",
            number: player.number || "",
            position: player.position || "",
            is_captain: !!player.is_captain,
            photo: getFormationAssignPhoto(player),
            x: 50,
            y: 50,
            row_index: Number(input.dataset.row),
            col_index: Number(input.dataset.col),
            slot_position: input.dataset.pos || ""
        });
    }

    const expectedCount = layout.flat().length;
    if (players.length !== expectedCount) {
        alert(`Нужно заполнить все позиции. Сейчас заполнено ${players.length} из ${expectedCount}.`);
        return;
    }

    const rowCount = layout.length;

    const topPadding = 18;
    const bottomPadding = 14;
    const usableHeight = 100 - topPadding - bottomPadding;

    const coords = FORMATION_COORDS[preset] || generateFormationCoordsFromLayout(preset);

    players.forEach((item, index) => {
        if (coords[index]) {
            item.x = coords[index].x;
            item.y = coords[index].y;
        }
    });

    formationState[side] = players;
    syncFormationCaptainsInState(side);
    renderFormation(side);
    closeFormationAssignModal();

    renderFormation(side);
    closeFormationAssignModal();
}

async function applyPreset(side) {
    openFormationAssignModal(side);
}

const squadEditorState = {
    home: null,
    away: null
};

function normalizePlayer(item = {}) {
    return {
        player_id: item.player_id ?? item.id ?? item.number ?? "",
        player_name: item.player_name || `${item.last_name || ""} ${item.first_name || ""}`.trim(),
        last_name: item.last_name || item.player_name || "",
        first_name: item.first_name || "",
        number: item.number || "",
        position: item.position || "",
        is_captain: !!item.is_captain
    };
}

function normalizeCoach(item = {}) {
    return {
        coach_id: item.coach_id ?? item.id ?? item.coach_name ?? "",
        coach_name: item.coach_name || item.name || "",
        role: item.role || ""
    };
}

function cloneSideState(side) {
    return {
        starting: (side === "home" ? (window.MATCH_DATA.homeStarting || []) : (window.MATCH_DATA.awayStarting || []))
            .map(normalizePlayer),
        bench: (side === "home" ? (window.MATCH_DATA.homeBench || []) : (window.MATCH_DATA.awayBench || []))
            .map(normalizePlayer),
        coaches: (side === "home" ? (window.MATCH_DATA.homeCoaches || []) : (window.MATCH_DATA.awayCoaches || []))
            .map(normalizeCoach),
        squadPool: (side === "home" ? (window.MATCH_DATA.homeSquadPool || []) : (window.MATCH_DATA.awaySquadPool || []))
            .map(normalizePlayer),
        coachPool: (side === "home" ? (window.MATCH_DATA.homeCoachPool || []) : (window.MATCH_DATA.awayCoachPool || []))
            .map(normalizeCoach)
    };
}

async function openSquadEditor() {
    if (!editModeEnabled) return;

    const ok = await loadSquadEditorData();
    if (!ok) {
        alert("Не удалось загрузить игроков и тренеров из базы.");
        return;
    }

    squadEditorState.home = cloneSideState("home");
    squadEditorState.away = cloneSideState("away");

    renderSquadEditorSide("home");
    renderSquadEditorSide("away");

    document.getElementById("squadEditorModal")?.classList.add("show");
}


function closeSquadEditor() {
    document.getElementById("squadEditorModal")?.classList.remove("show");
}

function getSideEditorState(side) {
    return side === "home" ? squadEditorState.home : squadEditorState.away;
}

function getSideContainerId(side) {
    return side === "home" ? "squadEditorHome" : "squadEditorAway";
}

function getAvailableSquadPool(side) {
    const state = getSideEditorState(side);
    if (!state) return [];

    const used = new Set(
        [...state.starting, ...state.bench].map(p => String(p.player_id))
    );

    return state.squadPool.filter(p => !used.has(String(p.player_id)));
}

function buildPlayerOptionText(p) {
    const fullName = `${p.last_name || p.player_name || ""} ${p.first_name || ""}`.trim();
    const num = p.number ? `#${p.number}` : "без №";
    const pos = p.position ? ` • ${p.position}` : "";
    return `${num} • ${fullName}${pos}`;
}

function renderSquadEditorSide(side) {
    const state = getSideEditorState(side);
    const mount = document.getElementById(getSideContainerId(side));
    if (!state || !mount) return;

    const availablePlayers = getAvailableSquadPool(side);
    const coachOptions = state.coachPool;

    mount.innerHTML = `
        <div class="squad-editor-section">
            <div class="squad-editor-section-title">Добавить игрока</div>
            <div class="squad-editor-box">
                <select class="squad-editor-select" id="addPlayerSelect-${side}">
                    <option value="">Выбери игрока из базы команды</option>
                    ${availablePlayers.map(player => `
                        <option value="${escapeHtml(String(player.player_id))}">
                            ${escapeHtml(buildPlayerOptionText(player))}
                        </option>
                    `).join("")}
                </select>

                <div class="squad-editor-actions">
                    <button type="button" class="squad-editor-btn"
                        onclick="addPlayerFromPool('${side}', 'starting')">
                        В старт
                    </button>
                    <button type="button" class="squad-editor-btn"
                        onclick="addPlayerFromPool('${side}', 'bench')">
                        В запас
                    </button>
                </div>

                <div class="squad-editor-hint">
                    В списке показываются только игроки, которых ещё нет в текущем составе.
                </div>
            </div>
        </div>

        <div class="squad-editor-section">
            <div class="squad-editor-section-title">Основной состав</div>
            <div class="squad-editor-box">
                <div class="squad-editor-list">
                    ${state.starting.length ? state.starting.map(player => renderSquadEditorPlayerRow(side, "starting", player)).join("") : `<div class="squad-editor-hint">Пока пусто.</div>`}
                </div>
            </div>
        </div>

        <div class="squad-editor-section">
            <div class="squad-editor-section-title">Запасные</div>
            <div class="squad-editor-box">
                <div class="squad-editor-list">
                    ${state.bench.length ? state.bench.map(player => renderSquadEditorPlayerRow(side, "bench", player)).join("") : `<div class="squad-editor-hint">Пока пусто.</div>`}
                </div>
            </div>
        </div>

        <div class="squad-editor-section">
            <div class="squad-editor-section-title">Тренеры</div>
            <div class="squad-editor-box">
                ${renderCoachEditorSection(side, coachOptions)}
            </div>
        </div>
    `;
}

function renderSquadEditorPlayerRow(side, role, player) {
    const fullName = `${player.last_name || player.player_name || ""} ${player.first_name || ""}`.trim();
    const captainClass = player.is_captain ? "active" : "";
    const captainText = player.is_captain ? "Капитан" : "Сделать капитаном";

    return `
        <div class="squad-editor-row">
            <div class="squad-editor-player">
                <div class="squad-editor-player-name">${escapeHtml(fullName)}</div>
                <div class="squad-editor-player-meta">
                    #${escapeHtml(String(player.number || "—"))}
                    ${player.position ? ` • ${escapeHtml(player.position)}` : ""}
                </div>
            </div>

            <div class="squad-editor-actions">
                <button type="button" class="squad-editor-btn ${captainClass}"
                    onclick="setCaptainInEditor('${side}', '${String(player.player_id)}', '${role}')">
                    ${captainText}
                </button>

                ${role === "starting" ? `
                    <button type="button" class="squad-editor-btn"
                        onclick="movePlayerBetweenZones('${side}', '${String(player.player_id)}', 'starting', 'bench')">
                        В запас
                    </button>
                ` : `
                    <button type="button" class="squad-editor-btn"
                        onclick="movePlayerBetweenZones('${side}', '${String(player.player_id)}', 'bench', 'starting')">
                        В старт
                    </button>
                `}

                <button type="button" class="squad-editor-btn danger"
                    onclick="removePlayerFromEditor('${side}', '${String(player.player_id)}', '${role}')">
                    Удалить
                </button>
            </div>
        </div>
    `;
}

function renderCoachEditorSection(side, coachOptions) {
    const state = getSideEditorState(side);
    if (!state) return "";

    const existingRoles = state.coaches.length ? state.coaches : [{ coach_id: "", coach_name: "", role: "Главный тренер" }];

    return `
        <div class="squad-editor-list">
            ${existingRoles.map((coach, index) => `
                <div class="squad-editor-row">
                    <div class="squad-editor-player">
                        <div class="squad-editor-player-name">${escapeHtml(coach.role || "Тренер")}</div>
                        <div class="squad-editor-player-meta">${escapeHtml(coach.coach_name || "Не выбран")}</div>
                    </div>
                    <div style="min-width:260px;">
                        <select class="squad-editor-select" onchange="updateCoachInEditor('${side}', ${index}, this.value)">
                            <option value="">Выбери тренера</option>
                            ${coachOptions.map(item => `
                                <option value="${escapeHtml(String(item.coach_id))}" ${String(item.coach_id) === String(coach.coach_id) ? "selected" : ""}>
                                    ${escapeHtml(item.coach_name)}${item.role ? ` • ${escapeHtml(item.role)}` : ""}
                                </option>
                            `).join("")}
                        </select>
                    </div>
                </div>
            `).join("")}
        </div>
    `;
}

function addPlayerFromPool(side, targetRole) {
    const state = getSideEditorState(side);
    if (!state) return;

    const select = document.getElementById(`addPlayerSelect-${side}`);
    const playerId = String(select?.value || "").trim();
    if (!playerId) return;

    const player = state.squadPool.find(p => String(p.player_id) === playerId);
    if (!player) return;

    state[targetRole].push({ ...player });

    renderSquadEditorSide(side);
}

function removePlayerFromEditor(side, playerId, role) {
    const state = getSideEditorState(side);
    if (!state) return;

    const hasLinkedEvents = matchEvents.some(event =>
        String(event.side) === String(side) &&
        (
            String(event.player_id || "") === String(playerId) ||
            String(event.player_out_id || "") === String(playerId) ||
            String(event.player_in_id || "") === String(playerId)
        )
    );

    if (hasLinkedEvents) {
        alert("Нельзя удалить игрока: по нему уже есть события матча.");
        return;
    }

    state[role] = state[role].filter(p => String(p.player_id) !== String(playerId));
    renderSquadEditorSide(side);
}

function movePlayerBetweenZones(side, playerId, fromRole, toRole) {
    const state = getSideEditorState(side);
    if (!state) return;

    const idx = state[fromRole].findIndex(p => String(p.player_id) === String(playerId));
    if (idx === -1) return;

    const [player] = state[fromRole].splice(idx, 1);

    if (toRole === "bench") {
        player.is_captain = false;
    }

    state[toRole].push(player);

    const hasCaptainInStarting = state.starting.some(p => p.is_captain);
    if (!hasCaptainInStarting && state.starting.length) {
        state.starting[0].is_captain = true;
    }

    renderSquadEditorSide(side);
}


function setCaptainInEditor(side, playerId, role) {
    const state = getSideEditorState(side);
    if (!state) return;

    if (role !== "starting") {
        alert("Капитан должен быть в основном составе.");
        return;
    }

    state.starting.forEach(player => {
        player.is_captain = String(player.player_id) === String(playerId);
    });

    state.bench.forEach(player => {
        player.is_captain = false;
    });

    renderSquadEditorSide(side);
}

function updateCoachInEditor(side, index, coachId) {
    const state = getSideEditorState(side);
    if (!state) return;

    const coach = state.coachPool.find(c => String(c.coach_id) === String(coachId));
    if (!coach) return;

    state.coaches[index] = {
        coach_id: coach.coach_id,
        coach_name: coach.coach_name,
        role: coach.role || "Тренер"
    };

    renderSquadEditorSide(side);
}

function renderLiveCoachBlocks() {
    renderLiveCoachBlockForSide("home");
    renderLiveCoachBlockForSide("away");
    updateCoachCardButtons();
}

function renderLiveCoachBlockForSide(side) {
    const selector = side === "home" ? ".panel .coaches-block" : ".panel .coaches-block-right";
    const block = Array.from(document.querySelectorAll(selector)).find(Boolean);
    const source = side === "home" ? (window.MATCH_DATA.homeCoaches || []) : (window.MATCH_DATA.awayCoaches || []);
    if (!block) return;

    block.innerHTML = source.map(c => `
        <div class="coach-line">
            <button
                type="button"
                class="coach-left vmix-person-trigger player-link-btn coach-link-btn"
                data-vmix-person-type="coach"
                data-side="${escapeHtml(side)}"
                data-name="${escapeHtml(c.coach_name || "")}"
                data-role="${escapeHtml(c.role || "")}"
                title="Показать титр тренера"
            >
                <span class="coach-name">${escapeHtml(c.coach_name || "")}</span>
                ${c.role ? `<span class="coach-badge">${escapeHtml(c.role)}</span>` : ""}
            </button>
            <div class="coach-actions">
                <button
                    type="button"
                    class="icon-btn yellow-card"
                    title="Жёлтая карточка"
                    data-coach-side="${side}"
                    data-coach-name="${escapeHtml(c.coach_name || "")}"
                    data-card-type="yellow"
                    onclick="addCoachEvent('${side}', 'yellow', this.dataset.coachName, this)"
                ></button>

                <button
                    type="button"
                    class="icon-btn red-card"
                    title="Красная карточка"
                    data-coach-side="${side}"
                    data-coach-name="${escapeHtml(c.coach_name || "")}"
                    data-card-type="red"
                    onclick="addCoachEvent('${side}', 'red', this.dataset.coachName, this)"
                ></button>
            </div>
        </div>
    `).join("");
}

function rerenderLineupTable(side, role) {
    const rows = Array.from(document.querySelectorAll(`tr.lineup-row[data-side="${side}"][data-role="${role}"]`));
    const tbody = rows[0]?.closest("tbody");
    if (!tbody) return;

    rows.forEach(row => row.remove());

    const source = side === "home"
        ? (role === "starting" ? window.MATCH_DATA.homeStarting : window.MATCH_DATA.homeBench)
        : (role === "starting" ? window.MATCH_DATA.awayStarting : window.MATCH_DATA.awayBench);

    source.forEach(playerRaw => {
        const player = normalizePlayer(playerRaw);
        const row = document.createElement("tr");
        row.className = "lineup-row";
        row.dataset.side = side;
        row.dataset.role = role;
        setRowPlayer(row, {
    id: player.player_id,
    name: player.player_name,
    lastName: player.last_name,
    firstName: player.first_name,
    number: player.number,
    position: player.position,
    isCaptain: !!player.is_captain,
    disciplineType: ""
});
        renderLineupRow(row);
        tbody.appendChild(row);
    });

    sortLineupRows(side, role);
}

function rebuildFormationFromMatchData(side) {
    const starting = side === "home"
        ? (window.MATCH_DATA.homeStarting || [])
        : (window.MATCH_DATA.awayStarting || []);

    const prev = Array.isArray(formationState[side]) ? formationState[side] : [];

    formationState[side] = starting.slice(0, 11).map((p, idx) => {
        const playerId = String(p.player_id ?? p.id ?? p.number ?? "");
        const old = prev.find(x => String(x.player_id ?? x.id ?? x.number ?? "") === playerId);

        return {
            player_id: p.player_id || p.id || p.number || null,
            player_name: p.player_name || `${p.last_name || ""} ${p.first_name || ""}`.trim(),
            last_name: p.last_name || p.player_name || "",
            first_name: p.first_name || "",
            number: p.number || "",
            position: p.position || "",
            is_captain: !!p.is_captain,
            photo: p.photo || "",
            x: old?.x ?? 50,
            y: old?.y ?? (12 + idx * 6)
        };
    });

    renderFormation(side);
}


async function applySquadEditorChanges() {
    if (!squadEditorState.home || !squadEditorState.away) return;

    const payload = buildSquadSavePayload();
    const result = await saveSquadEditorChangesToServer(payload);

    if (!result.success) {
        alert(`Не удалось сохранить состав: ${result.error}`);
        return;
    }

    console.log("SAVE PAYLOAD HOME STARTING", payload.home_starting);
    window.MATCH_DATA.homeStarting = payload.home_starting.map(p => ({ ...p }));
    window.MATCH_DATA.homeBench = payload.home_bench.map(p => ({ ...p }));
    window.MATCH_DATA.homeCoaches = payload.home_coaches.map(c => ({ ...c }));

    window.MATCH_DATA.awayStarting = payload.away_starting.map(p => ({ ...p }));
    window.MATCH_DATA.awayBench = payload.away_bench.map(p => ({ ...p }));
    window.MATCH_DATA.awayCoaches = payload.away_coaches.map(c => ({ ...c }));
    fallbackLineups.home = window.MATCH_DATA.homeStarting;
    fallbackLineups.away = window.MATCH_DATA.awayStarting;
    rebuildFormationFromMatchData("home");
    rebuildFormationFromMatchData("away");
    console.log("MATCH_DATA HOME STARTING AFTER SAVE", window.MATCH_DATA.homeStarting);

    rerenderLineupTable("home", "starting");
    rerenderLineupTable("home", "bench");
    rerenderLineupTable("away", "starting");
    rerenderLineupTable("away", "bench");

    renderLiveCoachBlocks();

    originalLineupState = captureCurrentLineupState();
    rebuildLineupsFromEvents();
    saveMatchState();
    closeSquadEditor();

    alert("Состав сохранён");
}

async function loadSquadEditorData() {
    if (!SESSION_TOKEN) return false;

    try {
        const res = await fetch(`/admin/session/${SESSION_TOKEN}/squad-editor-data`, {
            method: "GET",
            headers: { "Accept": "application/json" }
        });

        if (!res.ok) {
            console.warn("Не удалось загрузить данные редактора состава", res.status);
            return false;
        }

        const data = await res.json();

        window.MATCH_DATA.homeStarting = Array.isArray(data.home_starting) ? data.home_starting : [];
        window.MATCH_DATA.awayStarting = Array.isArray(data.away_starting) ? data.away_starting : [];
        window.MATCH_DATA.homeBench = Array.isArray(data.home_bench) ? data.home_bench : [];
        window.MATCH_DATA.awayBench = Array.isArray(data.away_bench) ? data.away_bench : [];

        window.MATCH_DATA.homeCoaches = Array.isArray(data.home_coaches) ? data.home_coaches : [];
        window.MATCH_DATA.awayCoaches = Array.isArray(data.away_coaches) ? data.away_coaches : [];

        window.MATCH_DATA.homeSquadPool = Array.isArray(data.home_squad_pool) ? data.home_squad_pool : [];
        window.MATCH_DATA.awaySquadPool = Array.isArray(data.away_squad_pool) ? data.away_squad_pool : [];

        window.MATCH_DATA.homeCoachPool = Array.isArray(data.home_coach_pool) ? data.home_coach_pool : [];
        window.MATCH_DATA.awayCoachPool = Array.isArray(data.away_coach_pool) ? data.away_coach_pool : [];

        return true;
    } catch (e) {
        console.warn("Ошибка загрузки squad editor data", e);
        return false;
    }
}

function getFormationPresetStorageKey(side) {
    return `match_formation_preset_${MATCH_ID}_${side}`;
}

function saveSelectedPreset(side) {
    const select = document.getElementById(`${side}-preset`);
    if (!select) return;
    localStorage.setItem(getFormationPresetStorageKey(side), select.value);
}

function restoreSelectedPreset(side) {
    const select = document.getElementById(`${side}-preset`);
    if (!select) return;

    const saved = localStorage.getItem(getFormationPresetStorageKey(side));
    if (!saved) return;

    const exists = Array.from(select.options).some(opt => opt.value === saved);
    if (exists) {
        select.value = saved;
    }
}

function buildSquadSavePayload() {
    return {
        home_starting: (squadEditorState.home?.starting || []).map(p => ({ ...p })),
        home_bench: (squadEditorState.home?.bench || []).map(p => ({ ...p })),
        away_starting: (squadEditorState.away?.starting || []).map(p => ({ ...p })),
        away_bench: (squadEditorState.away?.bench || []).map(p => ({ ...p })),

        home_coaches: (squadEditorState.home?.coaches || []).map(c => ({ ...c })),
        away_coaches: (squadEditorState.away?.coaches || []).map(c => ({ ...c }))
    };
}

async function saveSquadEditorChangesToServer(payload) {
    if (!SESSION_TOKEN) return { success: false, error: "Нет SESSION_TOKEN" };

    try {
        const res = await fetch(`/admin/session/${SESSION_TOKEN}/squad-editor-save`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(payload)
        });

        const data = await res.json().catch(() => ({}));

        if (!res.ok || !data.success) {
            return {
                success: false,
                error: data.error || `HTTP ${res.status}`
            };
        }

        return { success: true, data };
    } catch (e) {
        return {
            success: false,
            error: e?.message || "Ошибка сети"
        };
    }
}


function normalizeReferee(item = {}) {
    return {
        referee_id: item.referee_id ?? item.id ?? "",
        referee_name: (item.referee_name || item.full_name || item.name || "").trim(),
        role: (item.role || "").trim()
    };
}

let filteredRefereePool = [];

function renderRefereesEditor() {
    const wrap = document.getElementById("refereesEditorWrap");
    if (!wrap) return;

    const items = Array.isArray(refereeEditorState.items)
        ? refereeEditorState.items.map(normalizeReferee)
        : [];

    const searchHtml = editModeEnabled ? `
        <div class="referee-search">
            <input type="text" id="refereeSearchInput"
                placeholder="Поиск судьи..."
                oninput="filterRefereeList(this.value)">
            <div id="refereeSearchList" class="referee-search-list"></div>

            <select id="newRefereeRole">
                <option value="">Выбери роль</option>
                ${REFEREE_ROLE_OPTIONS.map(r => `<option value="${r}">${r}</option>`).join("")}
            </select>
        </div>
    ` : "";

    wrap.innerHTML = `
        <table>
            <thead>
                <tr>
                    <th>Роль</th>
                    <th>ФИО</th>
                    ${editModeEnabled ? "<th></th>" : ""}
                </tr>
            </thead>
            <tbody>
                ${items.map((r, i) => `
                    <tr>
                        <td>${r.role}</td>
                        <td>${r.referee_name}</td>
                        ${editModeEnabled ? `<td><button
    type="button"
    class="referee-delete-btn"
    onclick="removeRefereeRow(${i})"
    title="Удалить судью"
>
    ✖
</button></td>` : ""}
                    </tr>
                `).join("")}
            </tbody>
        </table>
        ${searchHtml}
    `;
}

function filterRefereeList(q) {
    const used = new Set(refereeEditorState.items.map(r => String(r.referee_id)));
    filteredRefereePool = refereeEditorState.pool.filter(r =>
        r.referee_name.toLowerCase().includes(q.toLowerCase()) &&
        !used.has(String(r.referee_id))
    );
    renderRefereeSearchList();
}

function renderRefereeSearchList() {
    const list = document.getElementById("refereeSearchList");
    if (!list) return;

    list.innerHTML = filteredRefereePool.map(r =>
        `<div onclick="selectRefereeFromSearch('${r.referee_id}')">${r.referee_name}</div>`
    ).join("");
}

function selectRefereeFromSearch(id) {
    selectedRefereeForAdd = refereeEditorState.pool.find(r => String(r.referee_id) === String(id));
    document.getElementById("refereeSearchInput").value = selectedRefereeForAdd.referee_name;
    filteredRefereePool = [];
    renderRefereeSearchList();
}

function addRefereeFromSelect() {
    const role = document.getElementById("newRefereeRole").value;
    if (!selectedRefereeForAdd || !role) return alert("Выбери судью и роль");

    refereeEditorState.items.push({
        referee_id: selectedRefereeForAdd.referee_id,
        referee_name: selectedRefereeForAdd.referee_name,
        role
    });

    selectedRefereeForAdd = null;
    renderRefereesEditor();
}

function removeRefereeRow(i) {
    refereeEditorState.items.splice(i, 1);
    renderRefereesEditor();
}

async function saveReferees() {
    const payload = refereeEditorState.items
        .map(normalizeReferee)
        .filter(item => (item.referee_id || item.referee_name) && item.role);

    console.log("SAVE referees payload:", payload);

    if (!payload.length) {
        alert("Нельзя сохранить пустой список судей");
        return;
    }

    try {
        const response = await fetch(`${window.location.pathname}/referees/save`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ referees: payload })
        });

        const data = await response.json().catch(() => ({}));

        if (!response.ok || !data.success) {
            alert(data.error || `Ошибка сохранения судей (HTTP ${response.status})`);
            return;
        }

        window.MATCH_DATA.referees = payload;
        refereeEditorState.items = [...payload];
        filteredRefereePool = [];
        selectedRefereeForAdd = null;

        renderRefereesEditor();
        alert("Судьи сохранены");
        window.location.reload();
    } catch (e) {
        console.error("saveReferees failed", e);
        alert("Ошибка сети при сохранении судей");
    }
}