from __future__ import annotations

import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from liverylab_core.game_file_writer import GameFileWriter
from liverylab_core.grid_presets import GRID_PRESETS_SCHEMA


BRIDGE_VERSION = 5
BRIDGE_DIR = "liverylab_grid_bridge"
BRIDGE_JSON_NAME = "current_grid.json"
BRIDGE_THUMBNAILS_DIR = "thumbnails"
BRIDGE_JS_NAME = "liverylab_grid_bridge.js"
INGAME_JS_NAME = "liverylab_grid_ingame.js"
DRIVERLABELS_JS_NAME = "liverylab_grid_driverlabels.js"
NAME_CLEANUP_JS_NAME = "liverylab_ai_name_cleanup.js"
CURRENT_GRID_JS_NAME = "liverylab_grid_current.js"
SMOKE_JS_NAME = "liverylab_grid_smoke_test.js"
CURRENT_GRID_SCRIPT_TAG = f"    <script defer src='js/{CURRENT_GRID_JS_NAME}'></script>"
BRIDGE_SCRIPT_TAG = f"    <script defer src='js/{BRIDGE_JS_NAME}'></script>"
INGAME_SCRIPT_TAG = f"    <script defer src='js/{INGAME_JS_NAME}'></script>"
DRIVERLABELS_SCRIPT_TAG = f"    <script defer src='js/{DRIVERLABELS_JS_NAME}'></script>"
NAME_CLEANUP_SCRIPT_TAG = f"    <script defer src='js/{NAME_CLEANUP_JS_NAME}'></script>"
SMOKE_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_grid_smoke_test\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
CURRENT_GRID_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_grid_current\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
BRIDGE_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_grid_bridge\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
INGAME_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_grid_ingame\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
DRIVERLABELS_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_grid_driverlabels\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
NAME_CLEANUP_SCRIPT_RE = re.compile(r"^[ \t]*<script\b[^>]*liverylab_ai_name_cleanup\.js[^>]*></script>[ \t]*\r?\n?", re.IGNORECASE | re.MULTILINE)
BACKUP_TIMESTAMP_RE = re.compile(r"\.(\d{8}_\d{6}_\d+)\.bak$", re.IGNORECASE)


BRIDGE_JS = r'''(function () {
    "use strict";

    const BRIDGE_VERSION = 5;
    const PANEL_ID = "liverylab-grid-bridge-panel";
    const NATIVE_GRID_BUTTON_ID = "liverylab-native-load-grid";
    const PRESET_URLS = [
        "liverylab_grid_bridge/current_grid.json",
        "/liverylab_grid_bridge/current_grid.json",
        "../liverylab_grid_bridge/current_grid.json"
    ];
    const PRESET_SCRIPT_URLS = [
        "js/liverylab_grid_current.js",
        "/js/liverylab_grid_current.js",
        "liverylab_grid_current.js"
    ];
    const REQUIRED_GAME_MODES = new Set([
        "GameModeType_INSTANT_RACE",
        "GameModeType_RACE_WEEKEND"
    ]);
    const VEHICLE_MODEL_STATES = [
        "CarSelectionOwnerShipState_OWNED",
        "CarSelectionOwnerShipState_ALL",
        "CarSelectionOwnerShipState_RENTAL"
    ];

    function hasManagers() {
        return !!(window.GAMEMODESELECTION && window.VEHICLES && window.engine);
    }

    function setStatus(message, isError) {
        const status = document.querySelector("#" + PANEL_ID + " .ll-grid-bridge-status");
        if (!status) return;
        status.textContent = message;
        status.style.color = isError ? "#ff7676" : "#b8ffca";
    }

    function setNativeButtonStatus(button, message, isError) {
        if (!button) return;
        button.title = message || "";
        button.classList.toggle("error", !!isError);
        button.dataset.liverylabStatus = message || "";
    }

    function waitForManagers(callback, attempts) {
        if (hasManagers()) {
            callback();
            return;
        }
        if ((attempts || 0) > 120) {
            setStatus("LiveryLab Grid could not find AC EVO managers.", true);
            return;
        }
        window.setTimeout(function () {
            waitForManagers(callback, (attempts || 0) + 1);
        }, 500);
    }

    function makeButton(label, handler) {
        const button = document.createElement("button");
        button.type = "button";
        button.textContent = label;
        button.style.cssText = [
            "display:block",
            "width:100%",
            "margin:0 0 9px 0",
            "padding:12px 15px",
            "background:#1d1f26",
            "border:1px solid #6e7688",
            "color:#f4f4f4",
            "font:18px Arial,sans-serif",
            "text-align:left",
            "cursor:pointer"
        ].join(";");
        let lastPress = 0;
        const press = function (event) {
            if (event && typeof event.preventDefault === "function") {
                event.preventDefault();
            }
            const now = Date.now();
            if (now - lastPress < 250) return;
            lastPress = now;
            handler(event);
        };
        button.addEventListener("click", press);
        button.addEventListener("mouseup", press);
        button.addEventListener("pointerup", press);
        return button;
    }

    function buildPanel() {
        if (document.getElementById(PANEL_ID)) return;

        const panel = document.createElement("div");
        panel.id = PANEL_ID;
        panel.style.cssText = [
            "position:fixed",
            "right:27px",
            "bottom:27px",
            "z-index:999999",
            "width:450px",
            "padding:18px",
            "background:rgba(7,8,12,.92)",
            "border:1px solid #8d95aa",
            "box-shadow:0 10px 30px rgba(0,0,0,.35)",
            "color:#f4f4f4",
            "font:18px Arial,sans-serif",
            "letter-spacing:0"
        ].join(";");

        const title = document.createElement("div");
        title.textContent = "LiveryLab Grid v" + BRIDGE_VERSION;
        title.style.cssText = "font-weight:700;margin:0 0 12px 0;color:#ffffff;font-size:19px";

        const status = document.createElement("div");
        status.className = "ll-grid-bridge-status";
        status.textContent = "Bridge v" + BRIDGE_VERSION + " active. Export a grid from LiveryLab, then apply it here.";
        status.style.cssText = "min-height:51px;margin:0 0 15px 0;color:#b8ffca;line-height:1.35";

        panel.appendChild(title);
        panel.appendChild(status);
        panel.appendChild(makeButton("Apply LiveryLab Grid", applyBridgeGrid));
        panel.appendChild(makeButton("Reload preset", loadPresetStatus));
        panel.appendChild(makeButton("Hide", function () {
            panel.style.display = "none";
        }));

        document.body.appendChild(panel);
        loadPresetStatus();
    }

    function getSessionZero() {
        return window.GAMEMODESELECTION
            && window.GAMEMODESELECTION.Current
            && window.GAMEMODESELECTION.Current.sessions
            && window.GAMEMODESELECTION.Current.sessions[0];
    }

    function getGameModeParameters() {
        const manager = window.GAMEMODESELECTION;
        const session = getSessionZero();
        if (!manager) {
            return Promise.reject(new Error("GameModeSelection manager is not available."));
        }
        if (session) {
            return Promise.resolve(manager.Current);
        }
        if (typeof manager.getGameModeParameters === "function") {
            return manager.getGameModeParameters().then(function () {
                return manager.Current;
            });
        }
        return Promise.reject(new Error("Current game mode is not available."));
    }

    function requestJson(url) {
        if (typeof fetch === "function") {
            return fetch(url, { cache: "no-store" }).then(function (response) {
                if (!response.ok) {
                    throw new Error("No LiveryLab grid exported yet.");
                }
                return response.json();
            });
        }
        return new Promise(function (resolve, reject) {
            const request = new XMLHttpRequest();
            request.open("GET", url, true);
            request.onreadystatechange = function () {
                if (request.readyState !== 4) return;
                if (request.status >= 200 && request.status < 300 || request.status === 0) {
                    try {
                        resolve(JSON.parse(request.responseText || "{}"));
                    } catch (error) {
                        reject(error);
                    }
                } else {
                    reject(new Error("No LiveryLab grid exported yet."));
                }
            };
            request.onerror = function () {
                reject(new Error("No LiveryLab grid exported yet."));
            };
            request.send();
        });
    }

    function requestPresetUrl(index) {
        if (index >= PRESET_URLS.length) {
            return Promise.reject(new Error("No LiveryLab grid exported yet."));
        }
        return requestJson(PRESET_URLS[index]).catch(function () {
            return requestPresetUrl(index + 1);
        });
    }

    function requestPresetScript(index) {
        if (index >= PRESET_SCRIPT_URLS.length) {
            return Promise.reject(new Error("No LiveryLab grid script exported yet."));
        }
        return new Promise(function (resolve, reject) {
            const script = document.createElement("script");
            script.async = true;
            script.src = PRESET_SCRIPT_URLS[index];
            script.onload = function () {
                resolve(window.LiveryLabGridPreset);
                script.remove();
            };
            script.onerror = function () {
                script.remove();
                reject(new Error("Preset script not available."));
            };
            document.head.appendChild(script);
        }).catch(function () {
            return requestPresetScript(index + 1);
        });
    }

    function loadPreset() {
        const loadedPreset = window.LiveryLabGridPreset;
        if (loadedPreset && Array.isArray(loadedPreset.entries) && loadedPreset.entries.length) {
            return Promise.resolve(loadedPreset);
        }
        return requestPresetScript(0).catch(function () {
            return requestPresetUrl(0);
        }).then(function (preset) {
            if (!preset || !Array.isArray(preset.entries) || !preset.entries.length) {
                throw new Error("Exported LiveryLab grid has no entries.");
            }
            return preset;
        });
    }

    function loadPresetStatus() {
        const requestedAt = new Date().toLocaleTimeString();
        setStatus("Reload requested " + requestedAt + "...", false);
        window.setTimeout(function () {
            loadPreset()
                .then(function (preset) {
                    setStatus("Preset reloaded " + new Date().toLocaleTimeString() + ": " + (preset.name || "Custom Grid") + " / " + preset.entries.length + " entries.", false);
                })
                .catch(function (error) {
                    setStatus(error.message || String(error), true);
                });
        }, 150);
    }

    function modelKey(model) {
        return [
            String(model && model.name || "").toLowerCase(),
            String(model && model.configuration && model.configuration.name || "").toLowerCase(),
            String(model && model.preset && model.preset.name || "").toLowerCase()
        ].join("|");
    }

    function uniqueModels(models) {
        const output = [];
        const seen = new Set();
        (models || []).forEach(function (model) {
            const key = modelKey(model);
            if (seen.has(key)) return;
            seen.add(key);
            output.push(model);
        });
        return output;
    }

    function modelSearchText(model) {
        try {
            return JSON.stringify(model || {}).toLowerCase();
        } catch (_error) {
            return "";
        }
    }

    function tokenStem(value) {
        const text = String(value || "")
            .replace(/\//g, "\\")
            .split("\\")
            .pop()
            .replace(/\.[^.]+$/, "")
            .trim()
            .toLowerCase();
        return text;
    }

    function entryMechanicalTokens(entry) {
        const tokens = [
            entry && entry.mechanicalPreset,
            entry && entry.mechanicalPresetPath,
            entry && entry.mechanicalPresetName,
            entry && entry.mechanicalPresetShortName
        ].map(tokenStem).filter(Boolean);
        const unique = [];
        tokens.forEach(function (token) {
            if (unique.indexOf(token) === -1) unique.push(token);
        });
        return unique;
    }

    function entryMechanicalPresetName(entry) {
        return tokenStem((entry && entry.mechanicalPreset) || (entry && entry.mechanicalPresetPath) || "");
    }

    function cssUrl(value) {
        return String(value || "").replace(/"/g, "%22").replace(/\\/g, "/");
    }

    function entryThumbnailUrl(entry) {
        const raw = String(entry && (entry.thumbnailUrl || entry.thumbnailPath) || "").trim();
        if (!raw) return "";
        if (/^(?:https?:|data:|\/)/i.test(raw)) return cssUrl(raw);
        return "/" + cssUrl(raw).replace(/^uiresources\//i, "");
    }

    function baseThumbnailUrl(model) {
        const car = model && model.name;
        const visual = model && model.preset && model.preset.name;
        const mechanical = model && (model.__liverylabBaseMechanical || (model.configuration && model.configuration.name));
        if (!car || !mechanical || !visual) return "";
        return "/thumbnail_bypreset/" + cssUrl(car) + "/" + cssUrl(mechanical) + "/" + cssUrl(visual);
    }

    function modelMatchesMechanical(model, tokens) {
        if (!tokens.length) return true;
        const search = modelSearchText(model);
        return tokens.some(function (token) {
            return search.indexOf(token) > -1;
        });
    }

    function modelMatchesSkin(model, skin) {
        if (!skin) return true;
        const presetName = String(model && model.preset && model.preset.name || "").toLowerCase();
        if (presetName === skin) return true;
        return modelSearchText(model).indexOf(skin) > -1;
    }

    function cloneModel(model) {
        try {
            return JSON.parse(JSON.stringify(model || {}));
        } catch (_error) {
            return Object.assign({}, model || {});
        }
    }

    function applyMechanicalOverride(model, entry) {
        const mechanicalPreset = entryMechanicalPresetName(entry || {});
        if (!mechanicalPreset) return model;
        const currentMechanical = String(model && model.configuration && model.configuration.name || "").toLowerCase();
        if (currentMechanical === mechanicalPreset.toLowerCase()) return model;

        const clone = cloneModel(model);
        clone.configuration = Object.assign({}, clone.configuration || {});
        clone.__liverylabBaseMechanical = clone.configuration.name || currentMechanical;
        clone.configuration.name = mechanicalPreset;
        clone.__liverylabThumbnailUrl = entryThumbnailUrl(entry || "");

        const displayName = String(entry.mechanicalPresetName || entry.mechanicalPresetShortName || "").trim();
        if (displayName) {
            clone.configuration.display_name = displayName;
            clone.translated = Object.assign({}, clone.translated || {});
            clone.translated.mech = displayName;
        }
        clone.__liverylabMechanicalOverride = mechanicalPreset;
        console.warn("[LiveryLab Grid] Applied mechanical override:", mechanicalPreset, entry.carModel || entry.car || "", entry.skinId || entry.preset || "");
        return clone;
    }

    function findModel(models, entry) {
        const car = String(entry.carModel || entry.car || "").toLowerCase();
        const skin = String(entry.skinId || entry.preset || entry.skin || "").toLowerCase();
        const candidates = (models || []).filter(function (model) {
            return String(model && model.name || "").toLowerCase() === car;
        });
        const mechanicalTokens = entryMechanicalTokens(entry || {});
        if (mechanicalTokens.length) {
            const mechanicalModel = candidates.find(function (model) {
                return modelMatchesSkin(model, skin) && modelMatchesMechanical(model, mechanicalTokens);
            });
            if (mechanicalModel) return mechanicalModel;
        }
        const skinModel = candidates.find(function (model) {
            return modelMatchesSkin(model, skin);
        }) || null;
        return mechanicalTokens.length && skinModel ? applyMechanicalOverride(skinModel, entry || {}) : skinModel;
    }

    function getVehicleModelsPool() {
        const vehicleManager = window.VEHICLES;
        const collected = [];

        if (!vehicleManager || typeof vehicleManager.getModelsLite !== "function") {
            return Promise.reject(new Error("Vehicle model list API is not available."));
        }

        let chain = Promise.resolve();
        VEHICLE_MODEL_STATES.forEach(function (state) {
            chain = chain.then(function () {
                return vehicleManager.getModelsLite("ClientCommandsMode_SINGLEPLAYER", state)
                    .then(function (data) {
                        if (data && Array.isArray(data.car_model)) {
                            collected.push.apply(collected, data.car_model);
                        }
                    })
                    .catch(function (error) {
                        console.warn("[LiveryLab Grid] Vehicle state failed:", state, error);
                    });
            });
        });

        return chain.then(function () {
            return uniqueModels(collected);
        });
    }

    function sanitizeDriverText(value) {
        return String(value || "")
            .replace(/^[\s\u2022\u00b7\u25cf\u25e6*.\-:;|]+/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function splitDriverName(name) {
        const parts = sanitizeDriverText(name).split(/\s+/).filter(Boolean);
        if (!parts.length) return { first: "LiveryLab", last: "Driver", short: "LL" };
        if (parts.length === 1) return { first: parts[0], last: "", short: parts[0].slice(0, 3).toUpperCase() };
        const last = parts.pop();
        const first = parts.join(" ");
        const short = (first + " " + last)
            .split(/\s+/)
            .map(function (part) { return part.replace(/[^a-z0-9]/ig, "").slice(0, 1); })
            .join("")
            .toUpperCase()
            .slice(0, 3);
        return { first: first, last: last, short: short || "LL" };
    }

    function applyDriverName(opponent, rawName, rawTeam) {
        const display = sanitizeDriverText(rawName || "LiveryLab Driver") || "LiveryLab Driver";
        const name = splitDriverName(display);
        const team = sanitizeDriverText(rawTeam || "");
        opponent.first_name = name.first;
        opponent.last_name = name.last;
        opponent.name = name.first;
        opponent.surname = name.last;
        opponent.short_name = name.short;
        opponent.shortcut_name = name.short;
        opponent.nickname = display;
        opponent.display_name = display;
        opponent.driver_name = name.first;
        opponent.driver_surname = name.last;
        opponent.full_name = display;
        opponent.firstName = name.first;
        opponent.lastName = name.last;
        opponent.shortName = name.short;
        opponent.team = team;
        return opponent;
    }

    function opponentDisplayName(opponent) {
        return [
            opponent && (opponent.first_name || opponent.driver_name || opponent.firstName || opponent.name),
            opponent && (opponent.last_name || opponent.driver_surname || opponent.lastName || opponent.surname)
        ].filter(Boolean).join(" ").trim();
    }

    function expectedDriverNames(entries) {
        return (entries || []).map(function (entry) {
            return String(entry.driverName || entry.displayName || entry.skinId || "").trim();
        }).filter(Boolean);
    }

    function countSavedDriverNames(savedOpponents, expected) {
        const actual = (savedOpponents || []).map(opponentDisplayName);
        let matches = 0;
        expected.forEach(function (name, index) {
            if (actual[index] === name) matches += 1;
        });
        return { matches: matches, actual: actual };
    }

    function patchGridItemThumbnail(item, model, entry) {
        const thumbnail = item && (item.thumbnail || (typeof item.querySelector === "function" && item.querySelector("#thumbnail")));
        if (!thumbnail || !thumbnail.style) return;
        const url = entryThumbnailUrl(entry) || (model && model.__liverylabThumbnailUrl) || baseThumbnailUrl(model);
        if (url) {
            thumbnail.style.backgroundImage = "url(\"" + url + "\")";
            thumbnail.textContent = "";
            thumbnail.dataset.liverylabThumbnail = "1";
            return;
        }
        thumbnail.style.backgroundImage = "none";
        thumbnail.textContent = "LL";
        thumbnail.style.display = "grid";
        thumbnail.style.placeItems = "center";
        thumbnail.style.color = "#ffffff";
        thumbnail.style.backgroundColor = "#171a21";
        thumbnail.style.font = "700 20px Arial,sans-serif";
        thumbnail.dataset.liverylabThumbnail = "placeholder";
    }

    function patchGridEditorThumbnails(models, entries) {
        const page = window.activePage;
        if (!page) return;
        const items = Array.isArray(page.items) && page.items.length
            ? page.items
            : Array.prototype.slice.call(page.querySelectorAll ? page.querySelectorAll("ks-griditem") : []);
        items.forEach(function (item, index) {
            patchGridItemThumbnail(item, models[index], entries && entries[index]);
        });
    }

    function scheduleGridEditorThumbnailPatch(models, entries) {
        [0, 250, 800, 1500].forEach(function (delay) {
            window.setTimeout(function () {
                patchGridEditorThumbnails(models, entries || []);
            }, delay);
        });
    }

    function chooseOpponents(opponents, entries) {
        const output = [];
        const source = Array.isArray(opponents) ? opponents : [];
        entries.forEach(function (entry, index) {
            const base = Object.assign({}, source[index % Math.max(1, source.length)] || {});
            output.push(applyDriverName(base, entry.driverName || entry.displayName || entry.skinId || "LiveryLab Driver", entry.team || ""));
        });
        return output;
    }

    function updateSessionWithGrid(session, models, opponents) {
        const piValues = models
            .map(function (model) { return Number(model.performance_indicator); })
            .filter(function (value) { return !Number.isNaN(value); });

        session.grid_type = "GameModeSelectionGridType_CUSTOM";
        session.num_opponents = models.length;
        session.models = models;
        session.opponents = opponents;
        session.min_strength = 80;
        session.max_strength = 100;
        session.aggressivness = "Normal";
        session.single_make = false;
        session.class_filter = [true, true, true, true, true, true, true, true, true];
        session.min_pi = piValues.length ? Math.floor(Math.min.apply(null, piValues)) : 0;
        session.max_pi = piValues.length ? Math.ceil(Math.max.apply(null, piValues)) : 10000;

        if (typeof session.starting_position === "number") {
            session.starting_position = Math.max(1, Math.min(session.starting_position || 1, models.length + 1));
        }
    }

    function refreshNativeGridEditor(models, opponents, entries) {
        const page = window.activePage;
        if (!page || typeof page.populateGrid !== "function") {
            return;
        }
        if (Array.isArray(page.currentModelGrid)) {
            page.currentModelGrid.length = 0;
            Object.assign(page.currentModelGrid, models);
        }
        if (Array.isArray(page.currentOpponentGrid)) {
            page.currentOpponentGrid.length = 0;
            Object.assign(page.currentOpponentGrid, opponents);
        }
        if (Array.isArray(page.backupModelGrid)) {
            page.backupModelGrid.length = 0;
            Object.assign(page.backupModelGrid, models);
        }
        if (Array.isArray(page.backupOpponentGrid)) {
            page.backupOpponentGrid.length = 0;
            Object.assign(page.backupOpponentGrid, opponents);
        }
        if (page.sliderOpponentNumber) {
            page.sliderOpponentNumber.value = Math.max(1, models.length);
        }
        page.populateGrid(models, opponents);
        scheduleGridEditorThumbnailPatch(models, entries || []);
        if (typeof page.clearDirty === "function") {
            page.clearDirty();
        }
    }

    function applyBridgeGrid() {
        waitForManagers(function () {
            let presetPayload = null;
            setStatus("Loading LiveryLab grid...", false);

            loadPreset()
                .then(function (preset) {
                    presetPayload = preset;
                    return getGameModeParameters();
                })
                .then(function (current) {
                    if (!REQUIRED_GAME_MODES.has(current.type)) {
                        throw new Error("Switch to Quick Race or Race Weekend first.");
                    }
                    const session = getSessionZero();
                    const maxOpponents = Math.max(1, Math.min(presetPayload.entries.length, ((session.track_item && session.track_item.max_drivers) || presetPayload.entries.length + 1) - 1));
                    setStatus("Generating native opponent pool...", false);
                    return window.GAMEMODESELECTION.generateGrid(
                        maxOpponents,
                        false,
                        [true, true, true, true, true, true, true, true, true],
                        "Normal",
                        80,
                        100,
                        0,
                        10000
                    ).then(function (gridData) {
                        return { current: current, session: session, gridData: gridData, count: maxOpponents };
                    });
                })
                .then(function (payload) {
                    setStatus("Loading installed vehicle presets...", false);
                    return getVehicleModelsPool().then(function (vehicleModels) {
                        payload.vehicleModels = vehicleModels;
                        return payload;
                    });
                })
                .then(function (payload) {
                    const aiModels = (payload.gridData.all_ai_models && payload.gridData.all_ai_models.length)
                        ? payload.gridData.all_ai_models
                        : payload.gridData.models || [];
                    const modelsSource = uniqueModels([].concat(payload.vehicleModels || [], aiModels));
                    const opponentsSource = (payload.gridData.all_opponents && payload.gridData.all_opponents.length)
                        ? payload.gridData.all_opponents
                        : payload.gridData.opponents || [];

                    const selectedEntries = presetPayload.entries.slice(0, payload.count);
                    const missing = [];
                    const models = [];
                    const matchedEntries = [];
                    selectedEntries.forEach(function (entry) {
                        const model = findModel(modelsSource, entry);
                        if (model) {
                            models.push(model);
                            matchedEntries.push(entry);
                        } else {
                            missing.push([entry.carModel || entry.car || "-", entry.skinId || entry.preset || "-"].join(" / "));
                        }
                    });

                    if (!models.length) {
                        throw new Error("No exported LiveryLab grid skins were found in AC EVO.");
                    }
                    const opponents = chooseOpponents(opponentsSource, matchedEntries);

                    updateSessionWithGrid(payload.session, models, opponents);
                    setStatus("Saving custom grid...", false);

                    return window.GAMEMODESELECTION.save(
                        "ClientCommandsMode_SINGLEPLAYER",
                        window.GAMEMODESELECTION.Current.sessions,
                        payload.current.type
                    ).then(function (saveData) {
                        if (typeof window.GAMEMODESELECTION.refreshDataModel === "function") {
                            window.GAMEMODESELECTION.refreshDataModel();
                        }
                        const savedSession = saveData && saveData.sessions && saveData.sessions[0]
                            ? saveData.sessions[0]
                            : getSessionZero();
                        const nameCheck = countSavedDriverNames(
                            savedSession && savedSession.opponents,
                            expectedDriverNames(matchedEntries)
                        );
                        const nameStatus = nameCheck.matches === models.length
                            ? " Driver names accepted."
                            : " Driver names replaced by AC EVO after save (" + nameCheck.matches + "/" + models.length + ").";
                        refreshNativeGridEditor(models, opponents, matchedEntries);
                        setStatus("Applied " + models.length + " entries from " + (presetPayload.name || "LiveryLab Grid") + (missing.length ? ". Missing: " + missing.join(", ") : ".") + nameStatus, missing.length > 0);
                        console.warn("[LiveryLab Grid] Applied drivers:", opponents.map(function (opponent) {
                            return [opponent.first_name, opponent.last_name].join(" ").trim();
                        }));
                        console.warn("[LiveryLab Grid] Saved drivers:", nameCheck.actual);
                    });
                })
                .catch(function (error) {
                    console.error("[LiveryLab Grid]", error);
                    setStatus(error.message || String(error), true);
                });
        });
    }

    function installNativeGridButton() {
        const generateButton = document.querySelector("ks-page-grideditor #btnGenerateGrid");
        if (!generateButton) {
            return;
        }
        const existingButton = document.getElementById(NATIVE_GRID_BUTTON_ID);
        if (existingButton && existingButton.dataset.liverylabNativeStyled === "2") {
            return;
        }

        const button = existingButton || document.createElement("ks-btnbasic");
        button.id = NATIVE_GRID_BUTTON_ID;
        button.className = "focusable";
        button.setAttribute("persist-handler", "");
        button.dataset.liverylabNativeStyled = "2";
        button.innerHTML = "<div slot=\"name\" style=\"color:#ffffff;text-transform:uppercase;letter-spacing:.08em\">LOAD LIVERYLAB GRID</div><div slot=\"icon\"></div>";
        button.style.cssText = [
            "position:absolute",
            "top:18.65rem",
            "left:1.5rem",
            "z-index:120",
            "width:23rem",
            "height:2.45rem",
            "display:flex",
            "align-items:center",
            "justify-content:center",
            "box-sizing:border-box",
            "padding:0.15rem 1.875rem",
            "background:#1f9d55",
            "border:1px solid #6ee7a3",
            "color:#ffffff",
            "font-size:1.25rem",
            "font-weight:700",
            "letter-spacing:.08em",
            "pointer-events:auto",
            "cursor:pointer"
        ].join(";");
        if (!existingButton) {
            generateButton.parentElement.appendChild(button);
        }

        let lastPress = 0;
        const press = function (event) {
            if (event && typeof event.preventDefault === "function") {
                event.preventDefault();
            }
            const now = Date.now();
            if (now - lastPress < 350) return;
            lastPress = now;
            setNativeButtonStatus(button, "Loading LiveryLab grid...", false);
            applyBridgeGrid();
        };

        button.onPressed = function (_sender, event) {
            press(event);
        };
        button.press = press;
        button.addEventListener("click", press);
        button.addEventListener("mouseup", press);
        button.addEventListener("pointerup", press);
        setNativeButtonStatus(button, "Loads the active LiveryLab grid exported from the app.", false);
        window.setTimeout(function () {
            button.onPressed = function (_sender, event) {
                press(event);
            };
            button.press = press;
        }, 250);
    }

    function watchNativeGridButton() {
        installNativeGridButton();
        const observer = new MutationObserver(function () {
            installNativeGridButton();
        });
        observer.observe(document.documentElement, { childList: true, subtree: true });
    }

    function boot() {
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", function () {
                waitForManagers(watchNativeGridButton);
            });
        } else {
            waitForManagers(watchNativeGridButton);
        }
    }

    boot();
})();'''


INGAME_JS = r'''(function () {
    "use strict";

    const BRIDGE_VERSION = 5;
    const PRESET_SCRIPT_URLS = [
        "js/liverylab_grid_current.js",
        "/js/liverylab_grid_current.js",
        "liverylab_grid_current.js"
    ];
    const state = {
        loading: false,
        preset: null,
        entries: [],
        lastExportedAt: "",
        domPatchCount: 0
    };

    function injectNamePatchCss() {
        if (document.getElementById("liverylab-grid-name-patch-style")) return;
        const style = document.createElement("style");
        style.id = "liverylab-grid-name-patch-style";
        style.textContent = [
            ".ll-grid-name-patched{list-style:none!important;}",
            ".ll-grid-name-patched::before{content:none!important;display:none!important;}",
            ".ll-grid-name-patched::marker{content:''!important;display:none!important;}",
            ".ll-grid-position-patched{list-style:none!important;}",
            ".ll-grid-position-patched::before{content:none!important;display:none!important;}",
            ".ll-grid-position-patched::marker{content:''!important;display:none!important;}",
            "ks-leaderboard .driver-name::before,ks-timetable .drivername::before,ks-timetablebtnrow .drivername::before{content:none!important;display:none!important;}",
            "ks-leaderboard .driver-name::marker,ks-timetable .drivername::marker,ks-timetablebtnrow .drivername::marker{content:''!important;display:none!important;}",
            "ks-leaderboard .raceposition::before{content:none!important;display:none!important;}",
            "ks-leaderboard .raceposition::marker{content:''!important;display:none!important;}"
        ].join("\n");
        document.head.appendChild(style);
    }

    function requestPresetScript(index) {
        if (index >= PRESET_SCRIPT_URLS.length) {
            return Promise.reject(new Error("No LiveryLab grid script exported yet."));
        }
        return new Promise(function (resolve, reject) {
            const script = document.createElement("script");
            script.async = true;
            script.src = PRESET_SCRIPT_URLS[index];
            script.onload = function () {
                resolve(window.LiveryLabGridPreset);
                script.remove();
            };
            script.onerror = function () {
                script.remove();
                reject(new Error("Preset script not available."));
            };
            document.head.appendChild(script);
        }).catch(function () {
            return requestPresetScript(index + 1);
        });
    }

    function loadPreset() {
        const loadedPreset = window.LiveryLabGridPreset;
        if (loadedPreset && Array.isArray(loadedPreset.entries) && loadedPreset.entries.length) {
            state.preset = loadedPreset;
            state.entries = loadedPreset.entries.map(prepareEntry);
            state.lastExportedAt = String(loadedPreset.exportedAt || "");
            window.LiveryLabGridIngamePreset = loadedPreset;
            return Promise.resolve(loadedPreset);
        }
        if (state.loading) return Promise.resolve(state.preset);
        state.loading = true;
        return requestPresetScript(0)
            .then(function (preset) {
                if (!preset || !Array.isArray(preset.entries) || !preset.entries.length) {
                    throw new Error("Exported LiveryLab grid has no entries.");
                }
                state.preset = preset;
                state.entries = preset.entries.map(prepareEntry);
                state.lastExportedAt = String(preset.exportedAt || "");
                window.LiveryLabGridIngamePreset = preset;
                console.warn("[LiveryLab Grid Ingame] v" + BRIDGE_VERSION + " loaded", preset.name, preset.entries.length);
                return preset;
            })
            .catch(function (error) {
                console.warn("[LiveryLab Grid Ingame]", error && error.message ? error.message : error);
                return null;
            })
            .then(function (preset) {
                state.loading = false;
                return preset;
            });
    }

    function sanitizeDriverText(value) {
        return String(value || "")
            .replace(/^[\s\u2022\u00b7\u25cf\u25e6*.\-:;|]+/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function splitDriverName(name) {
        const parts = sanitizeDriverText(name).split(/\s+/).filter(Boolean);
        if (!parts.length) return { first: "LiveryLab", last: "Driver", display: "LiveryLab Driver" };
        if (parts.length === 1) return { first: parts[0], last: "", display: parts[0] };
        const last = parts.pop();
        const first = parts.join(" ");
        return { first: first, last: last, display: (first + " " + last).trim() };
    }

    function carNumberFromEntry(entry) {
        var explicit = normalizeNumber(entry && (entry.carNumber || entry.car_number));
        if (explicit) return explicit;
        const text = [
            entry && entry.displayName,
            entry && entry.skinId,
            entry && entry.skinKey
        ].filter(Boolean).join(" ");
        const hashMatch = text.match(/#\s*([0-9]{1,4})\b/);
        if (hashMatch) return hashMatch[1];
        const suffixMatch = text.match(/(?:^|_)([0-9]{1,4})(?:\b|$)/);
        return suffixMatch ? suffixMatch[1] : "";
    }

    function prepareEntry(entry, index) {
        const name = splitDriverName(entry.driverName || entry.displayName || entry.skinId || "LiveryLab Driver");
        return {
            index: index,
            carModel: String(entry.carModel || "").toLowerCase(),
            skinId: String(entry.skinId || "").toLowerCase(),
            carNumber: carNumberFromEntry(entry),
            first: name.first,
            last: name.last,
            display: name.display
        };
    }

    function normalizeNumber(value) {
        return String(value == null ? "" : value).replace(/[^0-9]/g, "");
    }

    function entryForLine(line, index, used) {
        const number = normalizeNumber(line && line.car_number);
        if (number) {
            const byNumber = state.entries.find(function (entry) {
                return entry.carNumber === number && !used.has(entry.index);
            });
            if (byNumber) return byNumber;
        }
        const byIndex = state.entries[index % Math.max(1, state.entries.length)];
        return byIndex && !used.has(byIndex.index) ? byIndex : state.entries[index % Math.max(1, state.entries.length)];
    }

    function applyName(target, entry) {
        if (!target || !entry) return false;
        let changed = false;
        const first = sanitizeDriverText(entry.first);
        const last = sanitizeDriverText(entry.last);
        const display = sanitizeDriverText(entry.display);
        function setField(key, value) {
            if (target[key] === value) return;
            target[key] = value;
            changed = true;
        }
        setField("first_name", first);
        setField("last_name", last);
        setField("driver_name", first);
        setField("driver_surname", last);
        setField("name", display);
        setField("display_name", display);
        return changed;
    }

    function setNodeText(node, text) {
        if (!node) return false;
        const clean = sanitizeDriverText(text);
        if (!clean) return false;
        node.classList.add("ll-grid-name-patched");
        node.style.listStyle = "none";
        let changed = false;
        if (node.textContent !== clean) {
            node.textContent = clean;
            changed = true;
        }
        return changed;
    }

    function expectedRacePosition(line, index) {
        if (!line) return String(index + 1);
        const text = String(line.className || "");
        const classMatch = text.match(/\bdriver([0-9]+)\b/);
        if (classMatch) return String(Number(classMatch[1]) + 1);
        return String(index + 1);
    }

    function patchLeaderboardPositions() {
        const nodes = document.querySelectorAll("ks-leaderboard .leaderboardline .raceposition");
        let changed = false;
        Array.prototype.forEach.call(nodes, function (node, index) {
            const line = node.closest(".leaderboardline");
            const current = sanitizeDriverText(node.textContent);
            const expected = expectedRacePosition(line, index);
            node.classList.add("ll-grid-position-patched");
            node.style.listStyle = "none";
            if (!current || current === "." || current === "\u2022" || current === "\u00b7" || current === "\u25cf" || current === "\u25e6") {
                node.textContent = expected;
                changed = true;
            }
        });
        return changed;
    }

    function patchNameNodes(selector, lineSelector) {
        if (!state.entries.length) return false;
        const nodes = document.querySelectorAll(selector);
        const used = new Set();
        let changed = false;
        Array.prototype.forEach.call(nodes, function (node, index) {
            let entry = null;
            const line = lineSelector ? node.closest(lineSelector) : null;
            if (line) {
                const classMatch = String(line.className || "").match(/\bdriver([0-9]+)\b/);
                if (classMatch) {
                    const lineIndex = Number(classMatch[1]);
                    entry = state.entries[lineIndex % Math.max(1, state.entries.length)];
                }
            }
            if (!entry) {
                entry = state.entries[index % Math.max(1, state.entries.length)];
            }
            if (entry && !used.has(entry.index)) {
                used.add(entry.index);
            }
            if (setNodeText(node, entry && entry.display)) {
                changed = true;
            }
        });
        return changed;
    }

    function patchVisibleGridNames() {
        let changed = false;
        changed = patchNameNodes("ks-leaderboard .official-panel .leaderboardline .driver-name", ".leaderboardline") || changed;
        changed = patchNameNodes("ks-leaderboard .realtime-panel .leaderboardline .driver-name", ".leaderboardline") || changed;
        changed = patchNameNodes("ks-timetablebtnrow .slot-driver .drivername", "ks-timetablebtnrow") || changed;
        changed = patchNameNodes("ks-timetable .driverdetails .drivername", null) || changed;
        changed = patchLeaderboardPositions() || changed;
        if (changed) {
            state.domPatchCount += 1;
            window.LiveryLabGridDomPatchCount = state.domPatchCount;
        }
        return changed;
    }

    function patchLines(lines) {
        if (!Array.isArray(lines) || !state.entries.length) return false;
        const used = new Set();
        let changed = false;
        lines.forEach(function (line, index) {
            const entry = entryForLine(line, index, used);
            if (entry) used.add(entry.index);
            if (applyName(line, entry)) changed = true;
        });
        return changed;
    }

    function sanitizeVisibleNameText() {
        let changed = false;
        const nodes = document.querySelectorAll(".driver-name, .drivername, .entry");
        Array.prototype.forEach.call(nodes, function (node) {
            const clean = sanitizeDriverText(node.textContent);
            if (clean && node.textContent !== clean) {
                node.textContent = clean;
                changed = true;
            }
        });
        return changed;
    }

    function updateModel(model) {
        if (window.engine && model && typeof window.engine.updateWholeModel === "function") {
            try {
                window.engine.updateWholeModel(model);
            } catch (error) {
                console.warn("[LiveryLab Grid Ingame] model update failed", error);
            }
        }
    }

    function patchIngameModels() {
        let changedLeaderboard = false;
        let changedRealtime = false;
        let changedTimetable = false;

        if (window.ModelLeaderboard && patchLines(window.ModelLeaderboard.lines)) {
            changedLeaderboard = true;
        }
        if (window.ModelUIRealtimeLeaderboard && patchLines(window.ModelUIRealtimeLeaderboard.lines)) {
            changedRealtime = true;
        }
        if (window.TimeTable) {
            if (patchLines(window.TimeTable.drivers)) changedTimetable = true;
            if (window.TimeTable.driver && state.entries.length) {
                const entry = entryForLine(window.TimeTable.driver, window.TimeTable.driver.position || 0, new Set());
                if (applyName(window.TimeTable.driver, entry)) changedTimetable = true;
            }
        }

        if (changedLeaderboard) updateModel(window.ModelLeaderboard);
        if (changedRealtime) updateModel(window.ModelUIRealtimeLeaderboard);
        if (changedTimetable) updateModel(window.TimeTable);
        sanitizeVisibleNameText();
        patchVisibleGridNames();
    }

    function tick() {
        if (!state.preset) {
            loadPreset().then(patchIngameModels);
            return;
        }
        patchIngameModels();
    }

    window.LiveryLabGridIngame = {
        version: BRIDGE_VERSION,
        reload: function () {
            state.preset = null;
            state.entries = [];
            return loadPreset().then(function (preset) {
                patchIngameModels();
                return preset;
            });
        }
    };

    injectNamePatchCss();
    loadPreset().then(patchIngameModels);
    window.setInterval(tick, 250);
})();'''


DRIVERLABELS_JS = r'''(function () {
    "use strict";

    const BRIDGE_VERSION = 5;
    const PRESET_SCRIPT_URLS = [
        "js/liverylab_grid_current.js",
        "/js/liverylab_grid_current.js",
        "liverylab_grid_current.js"
    ];
    const state = {
        loading: false,
        preset: null,
        entries: [],
        patchCount: 0
    };

    function injectOverlayCss() {
        if (document.getElementById("liverylab-grid-driverlabels-style")) return;
        const style = document.createElement("style");
        style.id = "liverylab-grid-driverlabels-style";
        style.textContent = [
            "body,body>div,.entry{list-style:none!important;}",
            "body>div::before,body>div::marker,.entry::before,.entry::marker{content:''!important;display:none!important;}",
            ".entry{padding-left:0!important;text-indent:0!important;}"
        ].join("\n");
        document.head.appendChild(style);
    }

    function requestPresetScript(index) {
        if (index >= PRESET_SCRIPT_URLS.length) {
            return Promise.reject(new Error("No LiveryLab grid script exported yet."));
        }
        return new Promise(function (resolve, reject) {
            const script = document.createElement("script");
            script.async = true;
            script.src = PRESET_SCRIPT_URLS[index];
            script.onload = function () {
                resolve(window.LiveryLabGridPreset);
                script.remove();
            };
            script.onerror = function () {
                script.remove();
                reject(new Error("Preset script not available."));
            };
            document.head.appendChild(script);
        }).catch(function () {
            return requestPresetScript(index + 1);
        });
    }

    function loadPreset() {
        const loadedPreset = window.LiveryLabGridPreset;
        if (loadedPreset && Array.isArray(loadedPreset.entries) && loadedPreset.entries.length) {
            state.preset = loadedPreset;
            state.entries = loadedPreset.entries.map(prepareEntry);
            window.LiveryLabGridDriverlabelsPreset = loadedPreset;
            return Promise.resolve(loadedPreset);
        }
        if (state.loading) return Promise.resolve(state.preset);
        state.loading = true;
        return requestPresetScript(0)
            .then(function (preset) {
                if (!preset || !Array.isArray(preset.entries) || !preset.entries.length) {
                    throw new Error("Exported LiveryLab grid has no entries.");
                }
                state.preset = preset;
                state.entries = preset.entries.map(prepareEntry);
                window.LiveryLabGridDriverlabelsPreset = preset;
                console.warn("[LiveryLab Grid Driverlabels] v" + BRIDGE_VERSION + " loaded", preset.name, preset.entries.length);
                return preset;
            })
            .catch(function (error) {
                console.warn("[LiveryLab Grid Driverlabels]", error && error.message ? error.message : error);
                return null;
            })
            .then(function (preset) {
                state.loading = false;
                return preset;
            });
    }

    function sanitizeDriverText(value) {
        return String(value || "")
            .replace(/^[\s\u2022\u00b7\u25cf\u25e6*.\-:;|]+/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function prepareEntry(entry, index) {
        return {
            index: index,
            display: sanitizeDriverText(entry && (entry.driverName || entry.displayName || entry.skinId || "LiveryLab Driver"))
        };
    }

    function entryForIndex(index) {
        if (!state.entries.length) return null;
        return state.entries[index % state.entries.length];
    }

    function driverLabelName(value, index) {
        const entry = entryForIndex(Number(index) || 0);
        if (entry && entry.display) return entry.display;
        return sanitizeDriverText(value);
    }

    function updateModel(model) {
        if (window.engine && model && typeof window.engine.updateWholeModel === "function") {
            try {
                window.engine.updateWholeModel(model);
            } catch (error) {
                console.warn("[LiveryLab Grid Driverlabels] model update failed", error);
            }
        }
    }

    function patchDriverNameModel() {
        if (!window.MDriverNames || !Array.isArray(window.MDriverNames.drivers) || !state.entries.length) return false;
        let changed = false;
        window.MDriverNames.drivers.forEach(function (driver, index) {
            const entry = entryForIndex(index);
            if (!driver || !entry || !entry.display) return;
            const current = sanitizeDriverText(driver.name);
            if (current !== entry.display) {
                driver.name = entry.display;
                changed = true;
            }
        });
        if (changed) updateModel(window.MDriverNames);
        return changed;
    }

    function patchVisibleEntries() {
        if (!state.entries.length) return false;
        const nodes = document.querySelectorAll(".entry");
        let changed = false;
        Array.prototype.forEach.call(nodes, function (node, index) {
            const entry = entryForIndex(index);
            if (!entry || !entry.display) return;
            node.style.listStyle = "none";
            node.style.paddingLeft = "0";
            node.style.textIndent = "0";
            const current = sanitizeDriverText(node.textContent);
            if (current !== entry.display) {
                node.textContent = entry.display;
                changed = true;
            }
        });
        return changed;
    }

    function tick() {
        if (!state.preset) {
            loadPreset().then(tick);
            return;
        }
        const changedModel = patchDriverNameModel();
        const changedDom = patchVisibleEntries();
        if (changedModel || changedDom) {
            state.patchCount += 1;
            window.LiveryLabGridDriverlabelsPatchCount = state.patchCount;
        }
    }

    window.LiveryLabGridDriverlabels = {
        version: BRIDGE_VERSION,
        reload: function () {
            state.preset = null;
            state.entries = [];
            return loadPreset().then(function (preset) {
                tick();
                return preset;
            });
        }
    };
    window.liverylab_driverlabel_name = driverLabelName;

    injectOverlayCss();
    loadPreset().then(tick);
    window.setInterval(tick, 250);
})();'''


NAME_CLEANUP_JS = r'''(function () {
    "use strict";

    const VERSION = 1;
    const MARKER_RE = /^[\s\u2022\u00b7\u25cf\u25e6*.\-:;|]+/;
    const NAME_FIELDS = new Set([
        "first_name",
        "driver_name",
        "name",
        "full_name",
        "display_name",
        "nickname",
        "firstName",
        "driverName",
        "fullName",
        "displayName"
    ]);

    function cleanText(value) {
        if (typeof value !== "string") return value;
        const cleaned = value.replace(MARKER_RE, "").replace(/\s+/g, " ").trim();
        return cleaned || value;
    }

    function cleanField(object, field) {
        if (!object || typeof object[field] !== "string") return false;
        const cleaned = cleanText(object[field]);
        if (cleaned === object[field]) return false;
        object[field] = cleaned;
        return true;
    }

    function cleanNamedObject(object) {
        if (!object || typeof object !== "object") return false;
        let changed = false;
        NAME_FIELDS.forEach(function (field) {
            changed = cleanField(object, field) || changed;
        });
        return changed;
    }

    function cleanModelTree(value, depth, seen) {
        if (!value || depth > 6) return false;
        if (typeof value !== "object") return false;
        if (seen.has(value)) return false;
        seen.add(value);

        let changed = false;
        if (Array.isArray(value)) {
            value.forEach(function (item) {
                changed = cleanModelTree(item, depth + 1, seen) || changed;
            });
            return changed;
        }

        changed = cleanNamedObject(value) || changed;
        Object.keys(value).forEach(function (key) {
            const item = value[key];
            if (NAME_FIELDS.has(key) && typeof item === "string") {
                changed = cleanField(value, key) || changed;
                return;
            }
            if (item && typeof item === "object") {
                changed = cleanModelTree(item, depth + 1, seen) || changed;
            }
        });
        return changed;
    }

    function updateModel(model) {
        if (!window.engine || !model || typeof window.engine.updateWholeModel !== "function") return;
        try {
            window.engine.updateWholeModel(model);
        } catch (error) {
            console.warn("[LiveryLab AI Name Cleanup] model update failed", error);
        }
    }

    function cleanKnownModels() {
        let changed = false;
        [
            window.ModelLeaderboard,
            window.ModelUIRealtimeLeaderboard,
            window.TimeTable,
            window.MDriverNames
        ].forEach(function (model) {
            if (!model) return;
            const modelChanged = cleanModelTree(model, 0, new Set());
            if (modelChanged) {
                updateModel(model);
                changed = true;
            }
        });
        return changed;
    }

    function cleanVisibleNodes() {
        const nodes = document.querySelectorAll(".driver-name, .drivername, .entry");
        let changed = false;
        Array.prototype.forEach.call(nodes, function (node) {
            const current = String(node.textContent || "");
            const cleaned = cleanText(current);
            if (cleaned && cleaned !== current) {
                node.textContent = cleaned;
                changed = true;
            }
            if (node.style) {
                node.style.listStyle = "none";
                node.style.paddingLeft = "0";
                node.style.textIndent = "0";
            }
        });
        return changed;
    }

    function tick() {
        const changed = cleanKnownModels() || cleanVisibleNodes();
        if (changed) {
            window.LiveryLabAINameCleanupPatchCount = (window.LiveryLabAINameCleanupPatchCount || 0) + 1;
        }
    }

    if (window.LiveryLabAINameCleanup && window.LiveryLabAINameCleanup.version === VERSION) return;
    window.LiveryLabAINameCleanup = {
        version: VERSION,
        run: tick
    };

    tick();
    window.setInterval(tick, 100);
})();'''


def _now_iso() -> str:
    return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")


def paths(game_root: str | Path) -> dict[str, Path]:
    root = Path(game_root)
    ui_root = root / "uiresources"
    return {
        "root": root,
        "ui": ui_root,
        "singleplayer": ui_root / "singleplayer.html",
        "ingame": ui_root / "ingame.html",
        "hud": ui_root / "hud.html",
        "driverlabels": ui_root / "driverlabels.html",
        "bridge_js": ui_root / "js" / BRIDGE_JS_NAME,
        "ingame_js": ui_root / "js" / INGAME_JS_NAME,
        "driverlabels_js": ui_root / "js" / DRIVERLABELS_JS_NAME,
        "name_cleanup_js": ui_root / "js" / NAME_CLEANUP_JS_NAME,
        "current_grid_js": ui_root / "js" / CURRENT_GRID_JS_NAME,
        "smoke_js": ui_root / "js" / SMOKE_JS_NAME,
        "bridge_dir": ui_root / BRIDGE_DIR,
        "bridge_thumbnails": ui_root / BRIDGE_DIR / BRIDGE_THUMBNAILS_DIR,
        "bridge_json": ui_root / BRIDGE_DIR / BRIDGE_JSON_NAME,
        "manifest": ui_root / BRIDGE_DIR / "manifest.json",
    }


def _backup_sort_key(path: Path) -> str:
    match = BACKUP_TIMESTAMP_RE.search(path.name)
    return match.group(1) if match else path.name


def _backup_dirs_for(target: Path, game_root: str | Path | None = None, backup_root: str | Path | None = None) -> list[Path]:
    dirs: list[Path] = []
    if backup_root and game_root:
        try:
            rel = target.resolve().relative_to(Path(game_root).resolve())
            dirs.append(Path(backup_root) / rel.parent)
        except Exception:
            pass
    dirs.append(target.parent / "_acevo_backups")
    return dirs


def _earliest_backup_for(target: Path, game_root: str | Path | None = None, backup_root: str | Path | None = None) -> Path | None:
    backups: list[Path] = []
    for backup_dir in _backup_dirs_for(target, game_root, backup_root):
        if backup_dir.exists():
            backups.extend(backup_dir.glob(f"{target.name}.*.bak"))
    sorted_backups = sorted(backups, key=_backup_sort_key)
    return sorted_backups[0] if sorted_backups else None


def _normalized_text(data: bytes) -> str:
    return data.decode("utf-8-sig", errors="ignore").replace("\r\n", "\n").replace("\r", "\n")


def _differs_only_by_bytes(target: Path, backup: Path | None) -> bool:
    if backup is None or not target.exists() or not backup.exists():
        return False
    current = target.read_bytes()
    original = backup.read_bytes()
    return current != original and _normalized_text(current).strip() == _normalized_text(original).strip()


def _driverlabels_needs_exact_restore(
    path: Path,
    html: str,
    game_root: str | Path | None = None,
    backup_root: str | Path | None = None,
) -> bool:
    return _driverlabels_needs_default_restore(html) or _differs_only_by_bytes(path, _earliest_backup_for(path, game_root, backup_root))


def _current_grid_payload(p: dict[str, Path]) -> dict[str, Any]:
    if not p["bridge_json"].exists():
        return {}
    try:
        payload = json.loads(p["bridge_json"].read_text(encoding="utf-8-sig"))
    except (OSError, json.JSONDecodeError):
        return {"activeGridReadable": False}
    if not isinstance(payload, dict):
        return {"activeGridReadable": False}
    entries = payload.get("entries")
    return {
        "activeGridReadable": True,
        "activeGridId": str(payload.get("gridId") or "").strip(),
        "activeGridName": str(payload.get("name") or "").strip(),
        "activeGridDescription": str(payload.get("description") or "").strip(),
        "activeGridExportedAt": str(payload.get("exportedAt") or "").strip(),
        "activeGridEntryCount": len(entries) if isinstance(entries, list) else 0,
    }


def _bridge_manifest_payload(p: dict[str, Path]) -> dict[str, Any]:
    if not p["manifest"].exists():
        return {}
    try:
        data = json.loads(p["manifest"].read_text(encoding="utf-8-sig"))
    except (OSError, json.JSONDecodeError):
        return {}
    return data if isinstance(data, dict) else {}


def _bridge_script_has_current_version(p: dict[str, Path]) -> bool:
    if not p["bridge_js"].exists():
        return False
    try:
        text = p["bridge_js"].read_text(encoding="utf-8", errors="ignore")
    except OSError:
        return False
    return f"const BRIDGE_VERSION = {BRIDGE_VERSION}" in text


def bridge_status(game_root: str | Path, backup_root: str | Path | None = None) -> dict[str, Any]:
    p = paths(game_root)
    html = ""
    if p["singleplayer"].exists():
        html = p["singleplayer"].read_text(encoding="utf-8", errors="ignore")
    ingame_html = ""
    if p["ingame"].exists():
        ingame_html = p["ingame"].read_text(encoding="utf-8", errors="ignore")
    hud_html = ""
    if p["hud"].exists():
        hud_html = p["hud"].read_text(encoding="utf-8", errors="ignore")
    driverlabels_html = ""
    if p["driverlabels"].exists():
        driverlabels_html = p["driverlabels"].read_text(encoding="utf-8", errors="ignore")
    singleplayer_has_bridge = BRIDGE_JS_NAME in html
    singleplayer_has_current = CURRENT_GRID_JS_NAME in html
    ingame_has_current = CURRENT_GRID_JS_NAME in ingame_html
    ingame_has_names = INGAME_JS_NAME in ingame_html
    hud_has_current = CURRENT_GRID_JS_NAME in hud_html
    hud_has_names = INGAME_JS_NAME in hud_html
    driverlabels_has_current = CURRENT_GRID_JS_NAME in driverlabels_html
    driverlabels_has_names = DRIVERLABELS_JS_NAME in driverlabels_html
    driverlabels_has_patched_binding = "liverylab_driverlabel_name" in driverlabels_html
    driverlabels_needs_exact_restore = bool(
        p["driverlabels"].exists()
        and _driverlabels_needs_exact_restore(p["driverlabels"], driverlabels_html, game_root, backup_root)
    )
    manifest_payload = _bridge_manifest_payload(p)
    installed_bridge_version = manifest_payload.get("version") if isinstance(manifest_payload.get("version"), int) else None
    bridge_script_current = _bridge_script_has_current_version(p)
    bridge_version_current = bool(installed_bridge_version == BRIDGE_VERSION or (installed_bridge_version is None and bridge_script_current))
    required_hooks_installed = all(
        [
            singleplayer_has_bridge,
            singleplayer_has_current,
            p["bridge_js"].exists(),
            p["current_grid_js"].exists(),
            bridge_version_current,
        ]
    )
    current_grid_payload = _current_grid_payload(p)
    return {
        "installed": required_hooks_installed,
        "requiredHooksInstalled": required_hooks_installed,
        "bridgeVersion": BRIDGE_VERSION,
        "installedBridgeVersion": installed_bridge_version,
        "bridgeScriptCurrent": bridge_script_current,
        "bridgeVersionCurrent": bridge_version_current,
        "singleplayerPath": str(p["singleplayer"]),
        "ingamePath": str(p["ingame"]),
        "hudPath": str(p["hud"]),
        "driverlabelsPath": str(p["driverlabels"]),
        "bridgeScriptPath": str(p["bridge_js"]),
        "ingameScriptPath": str(p["ingame_js"]),
        "driverlabelsScriptPath": str(p["driverlabels_js"]),
        "currentGridScriptPath": str(p["current_grid_js"]),
        "currentGridPath": str(p["bridge_json"]),
        "backupRoot": str(backup_root or ""),
        "bridgeScriptExists": p["bridge_js"].exists(),
        "ingameScriptExists": p["ingame_js"].exists(),
        "driverlabelsScriptExists": p["driverlabels_js"].exists(),
        "currentGridScriptExists": p["current_grid_js"].exists(),
        "currentGridExists": p["bridge_json"].exists(),
        "singleplayerHasBridgeTag": singleplayer_has_bridge,
        "singleplayerHasCurrentGridTag": singleplayer_has_current,
        "singleplayerBridgeTagVersioned": f"{BRIDGE_JS_NAME}?v={BRIDGE_VERSION}" in html,
        "singleplayerBridgeTagPlain": f"js/{BRIDGE_JS_NAME}" in html,
        **current_grid_payload,
        "ingameHasCurrentGridTag": ingame_has_current,
        "ingameHasNameOverrideTag": ingame_has_names,
        "hudHasCurrentGridTag": hud_has_current,
        "hudHasNameOverrideTag": hud_has_names,
        "driverlabelsHasCurrentGridTag": driverlabels_has_current,
        "driverlabelsHasNameOverrideTag": driverlabels_has_names,
        "driverlabelsHasLegacyNameOverrideTag": INGAME_JS_NAME in driverlabels_html,
        "driverlabelsHasPatchedBinding": driverlabels_has_patched_binding,
        "driverlabelsNeedsExactRestore": driverlabels_needs_exact_restore,
        "requiresCleanup": (
            driverlabels_needs_exact_restore
            or singleplayer_has_bridge
            or singleplayer_has_current
            or ingame_has_current
            or ingame_has_names
            or hud_has_current
            or hud_has_names
            or driverlabels_has_current
            or driverlabels_has_names
            or p["bridge_js"].exists()
            or p["ingame_js"].exists()
            or p["driverlabels_js"].exists()
            or p["current_grid_js"].exists()
            or p["bridge_json"].exists()
            or p["manifest"].exists()
        ),
        "singleplayerHasSmokeTag": "liverylab_grid_smoke_test.js" in html,
    }


def name_cleanup_status(game_root: str | Path) -> dict[str, Any]:
    p = paths(game_root)
    ingame_html = p["ingame"].read_text(encoding="utf-8", errors="ignore") if p["ingame"].exists() else ""
    hud_html = p["hud"].read_text(encoding="utf-8", errors="ignore") if p["hud"].exists() else ""
    driverlabels_html = p["driverlabels"].read_text(encoding="utf-8", errors="ignore") if p["driverlabels"].exists() else ""
    ingame_has_cleanup = NAME_CLEANUP_JS_NAME in ingame_html
    hud_has_cleanup = NAME_CLEANUP_JS_NAME in hud_html
    driverlabels_has_cleanup = NAME_CLEANUP_JS_NAME in driverlabels_html
    required_hooks_installed = all(
        [
            ingame_has_cleanup,
            hud_has_cleanup,
            driverlabels_has_cleanup,
            p["name_cleanup_js"].exists(),
        ]
    )
    return {
        "installed": required_hooks_installed,
        "scriptPath": str(p["name_cleanup_js"]),
        "scriptExists": p["name_cleanup_js"].exists(),
        "ingameHasCleanupTag": ingame_has_cleanup,
        "hudHasCleanupTag": hud_has_cleanup,
        "driverlabelsHasCleanupTag": driverlabels_has_cleanup,
        "requiresCleanup": (
            ingame_has_cleanup
            or hud_has_cleanup
            or driverlabels_has_cleanup
            or p["name_cleanup_js"].exists()
        ),
    }


def _patch_singleplayer_html(html: str) -> str:
    patched = SMOKE_SCRIPT_RE.sub("", html)
    patched = CURRENT_GRID_SCRIPT_RE.sub("", patched)
    patched = BRIDGE_SCRIPT_RE.sub("", patched)
    patched = patched.replace("\n<link", "\n    <link")
    tags = CURRENT_GRID_SCRIPT_TAG + "\n" + BRIDGE_SCRIPT_TAG
    marker = "    <script type=\"module\" src='js/components.js'></script>"
    if marker in patched:
        return patched.replace(marker, marker + "\n" + tags, 1)
    marker_alt = "    <script type=\"module\" src=\"js/components.js\"></script>"
    if marker_alt in patched:
        return patched.replace(marker_alt, marker_alt + "\n" + tags, 1)
    return patched.replace("</head>", f"{tags}\n</head>", 1)


def _patch_script_html(html: str, script_re: re.Pattern[str], script_tag: str) -> str:
    patched = script_re.sub("", html)
    patched = CURRENT_GRID_SCRIPT_RE.sub("", patched)
    tags = CURRENT_GRID_SCRIPT_TAG + "\n" + script_tag
    marker = "    <script type=\"module\" src='js/components.js'></script>"
    if marker in patched:
        return patched.replace(marker, marker + "\n" + tags, 1)
    marker_alt = "    <script type=\"module\" src=\"js/components.js\"></script>"
    if marker_alt in patched:
        return patched.replace(marker_alt, marker_alt + "\n" + tags, 1)
    cohtml_marker = "    <script src='js/cohtml.js'></script>"
    if cohtml_marker in patched:
        return patched.replace(cohtml_marker, cohtml_marker + "\n" + tags, 1)
    cohtml_marker_alt = "    <script src=\"js/cohtml.js\"></script>"
    if cohtml_marker_alt in patched:
        return patched.replace(cohtml_marker_alt, cohtml_marker_alt + "\n" + tags, 1)
    return patched.replace("</head>", f"{tags}\n</head>", 1)


def _patch_standalone_script_html(html: str, script_re: re.Pattern[str], script_tag: str) -> str:
    patched = script_re.sub("", html)
    marker = "    <script type=\"module\" src='js/components.js'></script>"
    if marker in patched:
        return patched.replace(marker, marker + "\n" + script_tag, 1)
    marker_alt = "    <script type=\"module\" src=\"js/components.js\"></script>"
    if marker_alt in patched:
        return patched.replace(marker_alt, marker_alt + "\n" + script_tag, 1)
    cohtml_marker = "    <script src='js/cohtml.js'></script>"
    if cohtml_marker in patched:
        return patched.replace(cohtml_marker, cohtml_marker + "\n" + script_tag, 1)
    cohtml_marker_alt = "    <script src=\"js/cohtml.js\"></script>"
    if cohtml_marker_alt in patched:
        return patched.replace(cohtml_marker_alt, cohtml_marker_alt + "\n" + script_tag, 1)
    return patched.replace("</head>", f"{script_tag}\n</head>", 1)


def _default_driverlabels_html() -> str:
    return """<!DOCTYPE html>
<html lang="en">

<head>
    <script src='js/cohtml.js'></script>
    <!-- <script type="module" src='js/components.js'></script> -->

    <link rel='stylesheet' type='text/css' href='css/ui.css'>
    <!-- <link rel='stylesheet' type='text/css' href='css/uicomponents.css'> -->
    <style>
        body {
            width: 512px;
            height: 2048px;
            background-color: #333;



            display: flex;
            flex-direction: column;
        }

        .entry {
            display: flex;
            flex: 1 0 32px;
            justify-content: center;
            align-items: center;
            width: 512px;
            height: 32px;
            color: #fff;
            font-size: 2.2rem;
            font-weight: 700;
            white-space: nowrap;
        }
    </style>
</head>

<body class="">
    <div data-bind-for="item:{{MDriverNames.drivers}}">
        <div class="entry" data-bind-value="{{item.name}}"></div>
    </div>
</body>

</html>
"""


def _patch_driverlabels_html(html: str) -> str:
    if _driverlabels_needs_default_restore(html):
        html = _default_driverlabels_html()
    patched = INGAME_SCRIPT_RE.sub("", html)
    patched = _patch_script_html(patched, DRIVERLABELS_SCRIPT_RE, DRIVERLABELS_SCRIPT_TAG)
    patched = patched.replace(
        'data-bind-for="item:{{MDriverNames.drivers}}"',
        'data-bind-for="index,item:{{MDriverNames.drivers}}"',
    )
    patched = patched.replace(
        'data-bind-value="{{item.name}}"',
        'data-bind-value="liverylab_driverlabel_name({{item.name}},{{index}})"',
    )
    return patched


def _driverlabels_needs_default_restore(html: str) -> bool:
    return (
        "MDriverNames.drivers" not in html
        or "liverylab-driverlabels-diagnostic" in html
        or "liverylab_driverlabel_name" in html
    )


def _remove_grid_scripts_html(html: str) -> str:
    patched = BRIDGE_SCRIPT_RE.sub("", html)
    patched = INGAME_SCRIPT_RE.sub("", patched)
    patched = DRIVERLABELS_SCRIPT_RE.sub("", patched)
    patched = CURRENT_GRID_SCRIPT_RE.sub("", patched)
    patched = NAME_CLEANUP_SCRIPT_RE.sub("", patched)
    patched = patched.replace(
        'data-bind-for="index,item:{{MDriverNames.drivers}}"',
        'data-bind-for="item:{{MDriverNames.drivers}}"',
    )
    patched = patched.replace(
        'data-bind-value="liverylab_driverlabel_name({{item.name}},{{index}})"',
        'data-bind-value="{{item.name}}"',
    )
    return patched


def install_bridge(game_root: str | Path, backup_root: str | Path | None = None) -> dict[str, Any]:
    p = paths(game_root)
    if not p["singleplayer"].exists():
        raise FileNotFoundError(f"singleplayer.html not found: {p['singleplayer']}")
    writer = GameFileWriter(game_root, "install LiveryLab grid bridge", [p["ui"]], backup_root=backup_root)
    html = p["singleplayer"].read_text(encoding="utf-8", errors="ignore")
    patched = _patch_singleplayer_html(html)
    if patched != html:
        writer.write_bytes(p["singleplayer"], patched.encode("utf-8"), label="singleplayer grid bridge hook", backup=True)
    for key, label in (
        ("ingame", "remove legacy ingame grid name override hook"),
        ("hud", "remove legacy hud grid name override hook"),
        ("driverlabels", "remove legacy driverlabels grid name override hook"),
    ):
        if not p[key].exists():
            continue
        original_html = p[key].read_text(encoding="utf-8", errors="ignore")
        html = original_html
        if key == "driverlabels" and _driverlabels_needs_exact_restore(p[key], html, game_root, backup_root):
            backup = _earliest_backup_for(p[key], game_root, backup_root)
            if backup is not None:
                writer.copy_file(backup, p[key], label="restore original driverlabels overlay", backup=True)
                original_html = p[key].read_text(encoding="utf-8", errors="ignore")
                html = original_html
            else:
                html = _default_driverlabels_html().replace("\n", "\r\n")
        patched = _remove_grid_scripts_html(html)
        if patched != original_html:
            writer.write_bytes(p[key], patched.encode("utf-8"), label=label, backup=True)
    writer.write_bytes(p["bridge_js"], BRIDGE_JS.encode("utf-8"), label="LiveryLab grid bridge script", backup=True)
    writer.write_bytes(
        p["current_grid_js"],
        b"window.LiveryLabGridPreset = null;\n",
        label="LiveryLab current grid placeholder",
        backup=True,
    )
    writer.delete_file(p["ingame_js"], label="legacy LiveryLab grid ingame name override script", backup=True)
    writer.delete_file(p["driverlabels_js"], label="legacy LiveryLab grid driverlabels overlay script", backup=True)
    if p["smoke_js"].exists():
        writer.delete_file(p["smoke_js"], label="old LiveryLab grid smoke test script", backup=True)
    writer.write_bytes(
        p["manifest"],
        json.dumps({"schema": 1, "version": BRIDGE_VERSION, "installedAt": _now_iso()}, indent=2).encode("utf-8"),
        label="LiveryLab grid bridge manifest",
        backup=True,
    )
    name_cleanup = remove_name_cleanup(game_root, backup_root)
    return {
        "status": bridge_status(game_root, backup_root),
        "writer": writer.to_dict(),
        "legacyNameCleanupRemoval": name_cleanup,
        "nameCleanupStatus": name_cleanup.get("status", {}),
    }


def remove_bridge(
    game_root: str | Path,
    backup_root: str | Path | None = None,
    *,
    backup: bool = True,
    require_content_cars: bool = True,
) -> dict[str, Any]:
    p = paths(game_root)
    writer = GameFileWriter(
        game_root,
        "remove LiveryLab grid bridge",
        [p["ui"]],
        backup_root=backup_root,
        require_content_cars=require_content_cars,
    )
    for key, label in (
        ("singleplayer", "remove singleplayer grid bridge hook"),
        ("ingame", "remove ingame grid name override hook"),
        ("hud", "remove hud grid name override hook"),
        ("driverlabels", "remove driverlabels grid name override hook"),
    ):
        if p[key].exists():
            original_html = p[key].read_text(encoding="utf-8", errors="ignore")
            html = original_html
            if key == "driverlabels" and _driverlabels_needs_exact_restore(p[key], html, game_root, backup_root):
                backup_file = _earliest_backup_for(p[key], game_root, backup_root)
                if backup_file is not None:
                    writer.copy_file(backup_file, p[key], label="restore original driverlabels overlay", backup=backup)
                    original_html = p[key].read_text(encoding="utf-8", errors="ignore")
                    html = original_html
                else:
                    html = _default_driverlabels_html().replace("\n", "\r\n")
            patched = _remove_grid_scripts_html(html)
            if patched != original_html:
                writer.write_bytes(p[key], patched.encode("utf-8"), label=label, backup=backup)
    writer.delete_file(p["bridge_js"], label="LiveryLab grid bridge script", backup=backup)
    writer.delete_file(p["ingame_js"], label="LiveryLab grid ingame name override script", backup=backup)
    writer.delete_file(p["driverlabels_js"], label="LiveryLab grid driverlabels overlay script", backup=backup)
    writer.delete_file(p["name_cleanup_js"], label="LiveryLab AI name marker cleanup script", backup=backup)
    writer.delete_file(p["smoke_js"], label="old LiveryLab grid smoke test script", backup=backup)
    writer.delete_file(p["current_grid_js"], label="LiveryLab current grid script", backup=backup)
    writer.delete_file(p["bridge_json"], label="LiveryLab current grid JSON", backup=backup)
    if p["bridge_thumbnails"].exists():
        for thumbnail in p["bridge_thumbnails"].glob("*.png"):
            writer.delete_file(thumbnail, label="LiveryLab grid entry thumbnail", backup=backup)
    writer.delete_file(p["manifest"], label="LiveryLab grid bridge manifest", backup=backup)
    return {
        "status": bridge_status(game_root, backup_root),
        "writer": writer.to_dict(),
        "nameCleanupStatus": name_cleanup_status(game_root),
    }


def install_name_cleanup(game_root: str | Path, backup_root: str | Path | None = None) -> dict[str, Any]:
    p = paths(game_root)
    if not p["ingame"].exists() and not p["hud"].exists() and not p["driverlabels"].exists():
        raise FileNotFoundError(f"AC EVO UI files not found in: {p['ui']}")
    writer = GameFileWriter(game_root, "install LiveryLab AI name marker cleanup", [p["ui"]], backup_root=backup_root)
    for key, label in (
        ("ingame", "ingame AI name marker cleanup hook"),
        ("hud", "hud AI name marker cleanup hook"),
        ("driverlabels", "driverlabels AI name marker cleanup hook"),
    ):
        if not p[key].exists():
            continue
        html = p[key].read_text(encoding="utf-8", errors="ignore")
        patched = _patch_standalone_script_html(html, NAME_CLEANUP_SCRIPT_RE, NAME_CLEANUP_SCRIPT_TAG)
        if patched != html:
            writer.write_bytes(p[key], patched.encode("utf-8"), label=label, backup=True)
    writer.write_bytes(p["name_cleanup_js"], NAME_CLEANUP_JS.encode("utf-8"), label="LiveryLab AI name marker cleanup script", backup=True)
    return {"status": name_cleanup_status(game_root), "writer": writer.to_dict()}


def remove_name_cleanup(
    game_root: str | Path,
    backup_root: str | Path | None = None,
    *,
    backup: bool = True,
    require_content_cars: bool = True,
) -> dict[str, Any]:
    p = paths(game_root)
    writer = GameFileWriter(
        game_root,
        "remove LiveryLab AI name marker cleanup",
        [p["ui"]],
        backup_root=backup_root,
        require_content_cars=require_content_cars,
    )
    for key, label in (
        ("ingame", "remove ingame AI name marker cleanup hook"),
        ("hud", "remove hud AI name marker cleanup hook"),
        ("driverlabels", "remove driverlabels AI name marker cleanup hook"),
    ):
        if not p[key].exists():
            continue
        html = p[key].read_text(encoding="utf-8", errors="ignore")
        patched = NAME_CLEANUP_SCRIPT_RE.sub("", html)
        if patched != html:
            writer.write_bytes(p[key], patched.encode("utf-8"), label=label, backup=backup)
    writer.delete_file(p["name_cleanup_js"], label="LiveryLab AI name marker cleanup script", backup=backup)
    return {"status": name_cleanup_status(game_root), "writer": writer.to_dict()}


def _slug(value: Any, fallback: str = "item") -> str:
    text = str(value or "").strip().lower()
    text = re.sub(r"[^a-z0-9]+", "_", text).strip("_")
    return text or fallback


def _bridge_thumbnail_filename(index: int, entry: dict[str, Any]) -> str:
    car = _slug(entry.get("carModel") or entry.get("car_model"), "car")
    skin = _slug(entry.get("skinId") or entry.get("skin_id"), "skin")
    return f"{index + 1:02d}_{car}_{skin}.png"


def _sync_bridge_thumbnails(writer: GameFileWriter, p: dict[str, Path], payload: dict[str, Any]) -> dict[str, Any]:
    entries = payload.get("entries") if isinstance(payload.get("entries"), list) else []
    used_targets: set[Path] = set()
    copied = 0
    missing = 0
    p["bridge_thumbnails"].mkdir(parents=True, exist_ok=True)
    for index, entry in enumerate(entries):
        if not isinstance(entry, dict):
            continue
        source_text = str(entry.pop("_thumbnailSourcePath", "") or "").strip()
        if not source_text:
            missing += 1
            continue
        source = Path(source_text)
        if not source.is_file():
            missing += 1
            continue
        target = p["bridge_thumbnails"] / _bridge_thumbnail_filename(index, entry)
        writer.copy_file(source, target, label="LiveryLab grid entry thumbnail", backup=True)
        used_targets.add(target.resolve())
        copied += 1
        entry["thumbnailUrl"] = f"/{BRIDGE_DIR}/{BRIDGE_THUMBNAILS_DIR}/{target.name}"
    if p["bridge_thumbnails"].exists():
        for stale in p["bridge_thumbnails"].glob("*.png"):
            try:
                if stale.resolve() in used_targets:
                    continue
            except OSError:
                pass
            writer.delete_file(stale, label="stale LiveryLab grid entry thumbnail", backup=True)
    return {"copied": copied, "missing": missing, "directory": str(p["bridge_thumbnails"])}


def bridge_payload_from_preset(preset: dict[str, Any]) -> dict[str, Any]:
    entries = []
    source_entries = preset.get("entries", [])
    if not isinstance(source_entries, list):
        source_entries = []
    for entry in source_entries:
        if not isinstance(entry, dict):
            continue
        car_model = str(entry.get("carModel") or entry.get("car_model") or "").strip()
        skin_id = str(entry.get("skinId") or entry.get("skin_id") or "").strip()
        if not car_model or not skin_id:
            continue
        payload_entry = {
            "skinKey": str(entry.get("skinKey") or entry.get("skin_key") or "").strip(),
            "carModel": car_model,
            "skinId": skin_id,
            "displayName": str(entry.get("displayName") or entry.get("display_name") or skin_id).strip(),
            "driverName": str(entry.get("driverName") or entry.get("driver_name") or entry.get("displayName") or skin_id).strip(),
            "team": str(entry.get("team") or "").strip(),
            "carNumber": str(entry.get("carNumber") or entry.get("car_number") or "").strip(),
            "notes": str(entry.get("notes") or "").strip(),
        }
        thumbnail_source = str(entry.get("thumbnailPngDiskPath") or entry.get("thumbnail_png_disk_path") or "").strip()
        if thumbnail_source:
            payload_entry["_thumbnailSourcePath"] = thumbnail_source
        for key in (
            "aiVehicleSpec",
            "aiVehicleSpecResolved",
            "mechanicalPreset",
            "mechanicalPresetName",
            "mechanicalPresetShortName",
            "mechanicalPresetPath",
            "visualPresetPath",
            "carFinalStatePath",
        ):
            value = str(entry.get(key) or "").strip()
            if value:
                payload_entry[key] = value
        entries.append(payload_entry)
    if not entries:
        raise ValueError("Custom grid has no exportable entries")
    return {
        "schema": GRID_PRESETS_SCHEMA,
        "bridgeVersion": BRIDGE_VERSION,
        "gridId": str(preset.get("id") or preset.get("gridId") or "").strip(),
        "name": str(preset.get("name") or "LiveryLab Grid").strip(),
        "description": str(preset.get("description") or "").strip(),
        "exportedAt": _now_iso(),
        "entries": entries,
    }


def export_bridge_grid(game_root: str | Path, preset: dict[str, Any], backup_root: str | Path | None = None) -> dict[str, Any]:
    p = paths(game_root)
    payload = bridge_payload_from_preset(preset)
    writer = GameFileWriter(game_root, "export LiveryLab grid preset", [p["ui"]], backup_root=backup_root)
    thumbnail_result = _sync_bridge_thumbnails(writer, p, payload)
    writer.write_bytes(
        p["bridge_json"],
        json.dumps(payload, indent=2, ensure_ascii=False).encode("utf-8"),
        label="LiveryLab current grid JSON",
        backup=True,
    )
    script_text = "window.LiveryLabGridPreset = " + json.dumps(payload, indent=2, ensure_ascii=False) + ";\n"
    writer.write_bytes(
        p["current_grid_js"],
        script_text.encode("utf-8"),
        label="LiveryLab current grid script",
        backup=True,
    )
    return {"grid": payload, "thumbnails": thumbnail_result, "status": bridge_status(game_root, backup_root), "writer": writer.to_dict()}
