TCAT - Torn Chain Alert & Tool

Provides chain timer alerts, Discord webhooks, Chain Tool with Faction Import/Sorting, API status, and per-alert custom emergency URLs.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         TCAT - Torn Chain Alert & Tool
// @namespace    http://torn.com/HeyItzWerty
// @version      1.2.2
// @description  Provides chain timer alerts, Discord webhooks, Chain Tool with Faction Import/Sorting, API status, and per-alert custom emergency URLs.
// @author       HeyItzWerty [3626448]
// @match        https://www.torn.com/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_openInTab
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      i.imgur.com
// @connect      discord.com
// @connect      discordapp.com
// @connect      api.torn.com
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    if (window.self !== window.top) { return; }
    const SCRIPT_VERSION = GM_info.script.version || '1.2';
    console.log(`[TCAT] Script execution started (v${SCRIPT_VERSION}).`);

    const NEW_LOGO_URL = "https://i.imgur.com/UKjrvGW.png";
    const DEFAULT_SETTINGS = {
        fontSize: '13px',
        menuWidth: '650px', // New
        menuHeight: '550px', // New
        tabTitleAlert: true, flashTitle: true, solidTabAlert: false,
        preWarningColor4min: '#2196F3', preWarningColor3min: '#4CAF50', preWarningColor2min: '#FFEB3B', preWarningColor1min: '#FF9800', emergencyColor: '#F44336',
        preWarningEmoji4min: '🔵', preWarningEmoji3min: '🟢', preWarningEmoji2min: '🟡', preWarningEmoji1min: '🟠', emergencyEmoji: '🔴',
        checkIntervalChain: 3000,
        emergencyProfiles: ['', '', ''],
        emergencyUrl: '',
        alert4minActionsEnable: false, alert4minCustomUrl: '',
        alert3minActionsEnable: false, alert3minCustomUrl: '',
        alert2minActionsEnable: false, alert2minCustomUrl: '',
        alert1minActionsEnable: false, alert1minCustomUrl: '',
        alert30secActionsEnable: false, alert30secCustomUrl: '',
        alert4minEnable: false, alert4minBorder: true, alert4minTab: true,
        alert3minEnable: true, alert3minBorder: true, alert3minTab: true,
        alert2minEnable: true, alert2minBorder: true, alert2minTab: true,
        alert1minEnable: true, alert1minBorder: true, alert1minTab: true,
        alert30secEnable: true, alert30secBorder: true, alert30secTab: true,
        enableWebhook: false, webhookUrl: '',
        webhookMessageFormat: '🚨 **TORN ALERT!** Event: {action}. Details: {description}. Attacker: {attackerName} [{attackerId}] at {timeString}. Your Name: {yourName} [{yourId}]',
        userTornName: '',
        userTornId: '',
        webhookUseUserInfo: false,
        colorScheme: 'dark_orange',
        enableApiIntegration: false,
        apiKey: '',
        factionImportConfirmSize: 100, // Kept for logic, UI input removed
        factionImportGroupSize: 10,
        enableRetaliationWebhook: false,
        // webhookTCATLogoUrl: NEW_LOGO_URL, // Removed - NEW_LOGO_URL will be used directly
        retaliationEmbedColor: '#F44336',
        webhookRetaliationMessage: 'You were {action} by {attackerName} [{attackerId}]!',
    };
    const SETTINGS_MENU_ID = 'tcat-settings-menu';
    const BORDER_OVERLAY_ID = 'tcat-border-overlay';
    const MENU_CSS_PREFIX = 'tcat-';
    const STATUS_INDICATOR_ID = 'tcat-status-indicator';
    const API_CALL_COUNT_DISPLAY_ID = `${MENU_CSS_PREFIX}api-call-count-display`;
    const TAB_PREVIEW_ID = 'tcat-tab-preview';
    const CHAIN_TIMER_SELECTOR = '#sidebar > div:nth-child(1) > div > div.user-information___VBSOk > div > div.toggle-content___BJ9Q9 > div > div:nth-child(5) > a.chain-bar___vjdPL.bar-desktop___F8PEF > div.bar-stats___E_LqA > p.bar-timeleft___B9RGV';
    const CHAIN_LIST_STORAGE_KEY = 'tcatChainLists';
    const CHAIN_TOOL_STATE_KEY = 'tcatChainToolState';
    const CHAIN_TOOL_LIST_SELECT_ID = `${MENU_CSS_PREFIX}chain-list-select`;
    const CHAIN_TOOL_LIST_NAME_INPUT_ID = `${MENU_CSS_PREFIX}chain-list-name`;
    const CHAIN_TOOL_ID_INPUT_ID = `${MENU_CSS_PREFIX}chain-id-input`;
    const CHAIN_TOOL_ID_LIST_DISPLAY_ID = `${MENU_CSS_PREFIX}chain-id-list-display`;
    const CHAIN_TOOL_CURRENT_TARGET_DISPLAY_ID = `${MENU_CSS_PREFIX}chain-current-target`;
    const CHAIN_TOOL_FACTION_ID_INPUT_ID = `${MENU_CSS_PREFIX}faction-id-input`;
    const CHAIN_TOOL_VERIFIED_FACTION_DISPLAY_ID = `${MENU_CSS_PREFIX}verified-faction-display`;
    const CHAIN_TOOL_IMPORT_MODE_SELECT_ID = `${MENU_CSS_PREFIX}faction-import-mode`;
    const CHAIN_TOOL_IMPORT_COUNT_INPUT_ID = `${MENU_CSS_PREFIX}faction-import-count`;
    const DEFAULT_CHAIN_LIST_NAME = "Default List";
    const FORUM_THREAD_URL = "https://www.torn.com/forums.php#/p=threads&f=67&t=16466635&b=0&a=0";

    const COLOR_SCHEMES = {
        dark_orange: { '--tcat-bg-color': '#1e1e1e', '--tcat-bg-secondary-color': '#2a2a2a', '--tcat-content-bg-color': '#222222', '--tcat-text-color': '#dddddd', '--tcat-text-light-color': '#bbbbbb', '--tcat-border-color': '#444444', '--tcat-border-light-color': '#555555', '--tcat-accent-color': '#ff9800', '--tcat-accent-hover-color': '#e68a00', '--tcat-button-bg-color': '#333333', '--tcat-button-text-color': '#dddddd', '--tcat-button-hover-bg-color': '#444444', '--tcat-input-bg-color': '#282828', '--tcat-input-border-color': '#555555', '--tcat-input-text-color': '#dddddd', '--tcat-danger-color': '#f44336', '--tcat-danger-text-color': '#ffffff', '--tcat-danger-hover-color': '#d32f2f', '--tcat-log-bg-color': '#252525', '--tcat-log-header-bg-color': '#303030', '--tcat-log-border-color': '#484848', '--tcat-log-alt-row-color': '#282828', '--tcat-tab-inactive-bg': '#333333', '--tcat-tab-hover-bg': '#444444', '--tcat-color-yellow': '#FFEB3B', '--tcat-color-orange': '#FF9800'},
        dark_blue: { '--tcat-bg-color': '#1a202c', '--tcat-bg-secondary-color': '#2d3748', '--tcat-content-bg-color': '#1c2431', '--tcat-text-color': '#e2e8f0', '--tcat-text-light-color': '#a0aec0', '--tcat-border-color': '#4a5568', '--tcat-border-light-color': '#718096', '--tcat-accent-color': '#4299e1', '--tcat-accent-hover-color': '#2b6cb0', '--tcat-button-bg-color': '#2d3748', '--tcat-button-text-color': '#e2e8f0', '--tcat-button-hover-bg-color': '#4a5568', '--tcat-input-bg-color': '#2d3748', '--tcat-input-border-color': '#718096', '--tcat-input-text-color': '#e2e8f0', '--tcat-danger-color': '#e53e3e', '--tcat-danger-text-color': '#ffffff', '--tcat-danger-hover-color': '#c53030', '--tcat-log-bg-color': '#202835', '--tcat-log-header-bg-color': '#283141', '--tcat-log-border-color': '#404a5c', '--tcat-log-alt-row-color': '#232c3b', '--tcat-tab-inactive-bg': '#2d3748', '--tcat-tab-hover-bg': '#4a5568', '--tcat-color-yellow': '#ECC94B', '--tcat-color-orange': '#ED8936'},
        dark_red: { '--tcat-bg-color': '#1e1e1e', '--tcat-bg-secondary-color': '#2a2a2a', '--tcat-content-bg-color': '#222222', '--tcat-text-color': '#dddddd', '--tcat-text-light-color': '#bbbbbb', '--tcat-border-color': '#444444', '--tcat-border-light-color': '#555555', '--tcat-accent-color': '#e74c3c', '--tcat-accent-hover-color': '#c0392b', '--tcat-button-bg-color': '#333333', '--tcat-button-text-color': '#dddddd', '--tcat-button-hover-bg-color': '#444444', '--tcat-input-bg-color': '#282828', '--tcat-input-border-color': '#555555', '--tcat-input-text-color': '#dddddd', '--tcat-danger-color': '#f44336', '--tcat-danger-text-color': '#ffffff', '--tcat-danger-hover-color': '#d32f2f', '--tcat-log-bg-color': '#252525', '--tcat-log-header-bg-color': '#303030', '--tcat-log-border-color': '#484848', '--tcat-log-alt-row-color': '#282828', '--tcat-tab-inactive-bg': '#333333', '--tcat-tab-hover-bg': '#444444', '--tcat-color-yellow': '#F1C40F', '--tcat-color-orange': '#E67E22'},
        dark_green: { '--tcat-bg-color': '#1e1e1e', '--tcat-bg-secondary-color': '#2a2a2a', '--tcat-content-bg-color': '#222222', '--tcat-text-color': '#dddddd', '--tcat-text-light-color': '#bbbbbb', '--tcat-border-color': '#444444', '--tcat-border-light-color': '#555555', '--tcat-accent-color': '#2ecc71', '--tcat-accent-hover-color': '#27ae60', '--tcat-button-bg-color': '#333333', '--tcat-button-text-color': '#dddddd', '--tcat-button-hover-bg-color': '#444444', '--tcat-input-bg-color': '#282828', '--tcat-input-border-color': '#555555', '--tcat-input-text-color': '#dddddd', '--tcat-danger-color': '#e74c3c', '--tcat-danger-text-color': '#ffffff', '--tcat-danger-hover-color': '#c0392b', '--tcat-log-bg-color': '#252525', '--tcat-log-header-bg-color': '#303030', '--tcat-log-border-color': '#484848', '--tcat-log-alt-row-color': '#282828', '--tcat-tab-inactive-bg': '#333333', '--tcat-tab-hover-bg': '#444444', '--tcat-color-yellow': '#F1C40F', '--tcat-color-orange': '#E67E22'},
    };
    const FONT_SIZE_OPTIONS = [ {value: '11px', text: '11px'}, {value: '12px', text: '12px'}, {value: '13px', text: '13px (Default)'}, {value: '14px', text: '14px'}, {value: '15px', text: '15px'}, {value: '16px', text: '16px'} ];
    const MENU_SIZE_OPTIONS = {
        width: [
            { value: '600px', text: 'Compact (600px)' },
            { value: '650px', text: 'Standard (650px)' },
            { value: '750px', text: 'Medium (750px)' },
            { value: '850px', text: 'Large (850px)' }
        ],
        height: [
            { value: '500px', text: 'Short (500px)' },
            { value: '550px', text: 'Standard (550px)' },
            { value: '650px', text: 'Tall (650px)' },
            { value: '700px', text: 'Extra Tall (700px)' }
        ]
    };


    // --- Globals ---
    let settings = {}; let settingsMenu = null; let borderOverlay = null;
    let originalTitle = document.title; let tabPreviewInterval = null;
    let chainIntervalId = null;
    let statusIntervalId = null; let isChainCurrentlyActive = false; let alerted4min = false, alerted3min = false, alerted2min = false, alerted1min = false, alerted30sec = false; let currentBorderLevel = null; let isFlashing = false; let solidTitleActive = false;
    let chainLists = {};
    let chainToolState = { currentListName: null, currentIndex: -1, currentListIDs: [], verifiedFactionId: null, verifiedFactionName: null, enrichedMemberDataCache: {} };
    let isTestingAlert = false;
    let apiRequestOngoing = false;
    let apiConnectionStatus = 'UNKNOWN';
    let isSortingInProgress = false;

    // API Rate Limiter Globals
    let tcatApiCallsLastMinute = 0;
    let tcatApiCallTimestamps = [];
    let tcatIsApiPaused = false;
    const TCAT_API_LIMIT_WARN = 50;
    const TCAT_API_LIMIT_URGENT = 75;
    const TCAT_API_LIMIT_MAX_TRIGGER = 95;
    const TCAT_API_MINUTE_MS = 60000;
    let apiCallDisplayIntervalId = null;


    // --- API Call Management ---
    function tcatUpdateApiCallDisplay() { const now = Date.now(); tcatApiCallTimestamps = tcatApiCallTimestamps.filter(ts => now - ts < TCAT_API_MINUTE_MS); tcatApiCallsLastMinute = tcatApiCallTimestamps.length; const displayElem = document.getElementById(API_CALL_COUNT_DISPLAY_ID); if (displayElem) { displayElem.textContent = `API Calls (Last Min): ${tcatApiCallsLastMinute}/100`; if (tcatApiCallsLastMinute > TCAT_API_LIMIT_MAX_TRIGGER) { displayElem.style.color = 'var(--tcat-danger-color)'; } else if (tcatApiCallsLastMinute >= TCAT_API_LIMIT_URGENT) { displayElem.style.color = 'var(--tcat-color-orange, orange)'; } else if (tcatApiCallsLastMinute >= TCAT_API_LIMIT_WARN) { displayElem.style.color = 'var(--tcat-color-yellow, yellow)'; } else { displayElem.style.color = 'var(--tcat-text-light-color)'; } } if (tcatIsApiPaused && tcatApiCallsLastMinute < TCAT_API_LIMIT_MAX_TRIGGER - 10) { tcatIsApiPaused = false; console.log("[TCAT API Limiter] API calls resumed."); showUserMessage("API Resumed", "API calls are no longer paused.", "info"); updateChainToolUI(); } }
    function tcatCanMakeApiCall(isCritical = false) { tcatUpdateApiCallDisplay(); if (tcatIsApiPaused) { console.warn("[TCAT API Limiter] API call blocked: API is paused."); if (!isCritical) { showUserMessage("API Paused", "TCAT API calls are temporarily paused due to rate limiting. Please wait.", "warning"); } return false; } if (tcatApiCallsLastMinute >= 99 && !isCritical) { console.warn(`[TCAT API Limiter] API call blocked: Detected very high recent API activity (${tcatApiCallsLastMinute}).`); showUserMessage("API Limit Critical", "Very high API activity detected. TCAT calls paused to prevent exceeding limit.", "error"); tcatIsApiPaused = true; return false; } return true; }
    function tcatRecordApiCall() { tcatApiCallTimestamps.push(Date.now()); tcatUpdateApiCallDisplay(); if (tcatApiCallsLastMinute >= TCAT_API_LIMIT_MAX_TRIGGER + 1 && !tcatIsApiPaused) { tcatIsApiPaused = true; console.warn(`[TCAT API Limiter] API call limit trigger (${tcatApiCallsLastMinute}). Pausing further TCAT calls.`); showUserMessage("API Limit Approaching!", "Slowing down with your API usage! TCAT API calls temporarily paused.", "warning"); updateChainToolUI(); } }
    function makeManagedApiRequest(options, operationName = "API Request") { if (!tcatCanMakeApiCall(options.isCritical || false)) { showUserMessage(`${operationName} Blocked`, "API calls paused due to rate limiting. Please wait.", "warning"); if (options.onRateLimited) options.onRateLimited(); if (options.onFinally) options.onFinally(); return; } tcatRecordApiCall(); const originalOnload = options.onload; const originalOnerror = options.onerror; const originalOntimeout = options.ontimeout; const onFinallyCallback = options.onFinally; const onComplete = () => { if (onFinallyCallback) onFinallyCallback(); }; options.onload = (response) => { if (originalOnload) originalOnload(response); onComplete(); }; options.onerror = (response) => { if (originalOnerror) originalOnerror(response); onComplete(); }; options.ontimeout = (response) => { if (originalOntimeout) originalOntimeout(response); onComplete(); }; GM_xmlhttpRequest(options); }

    // --- Helper Functions ---
    function showUserMessage(title, message, type = 'info') { if (type === 'error' || type === 'success' || type === 'warning' || type === 'info') { console.log(`[TCAT ${type.toUpperCase()}]: ${title} - ${message}`); if (typeof unsafeWindow !== 'undefined' && unsafeWindow.TCAT_DISPLAY_MESSAGE) { unsafeWindow.TCAT_DISPLAY_MESSAGE(title, message, type); } else { alert(`[TCAT ${type.toUpperCase()}] ${title}: ${message}`); } } }
    const safeAppend = (parent, childFactory, factoryArgs = [], elementName = 'element') => { if (!parent || !(parent instanceof Node)) { console.error(`[TCAT v${SCRIPT_VERSION}] Invalid parent for ${elementName}:`, parent); return null; } try { const child = childFactory(...factoryArgs); if (child instanceof Node) { parent.appendChild(child); return child; } else { console.error(`[TCAT v${SCRIPT_VERSION}] ${elementName} factory did not return a Node:`, child); return null; } } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating/appending ${elementName} in parent:`, e); return null; } };
    function parseTimeString(timeString) { if (!timeString) return 0; try { const parts = String(timeString).trim().split(':').map(Number); if (parts.some(isNaN)) return 0; if (parts.length === 2) return (parts[0] * 60) + parts[1]; if (parts.length === 3) return (parts[0] * 3600) + (parts[1] * 60) + parts[2]; return 0; } catch(e) { console.error("Error parsing time string:", timeString, e); return 0; } }
    function openProfiles(profiles) { try { if (!Array.isArray(profiles)) { return; } const validProfiles = profiles.map(p => String(p).trim()).filter(id => id && /^\d+$/.test(id)); if (validProfiles.length === 0) return; validProfiles.forEach(id => { const attackUrl = `https://www.torn.com/loader.php?sid=attack&user2ID=${id}`; try { GM_openInTab(attackUrl, { active: false, setParent: true }); } catch(e){ console.error("Error opening attack loader tab:", id, e); } }); } catch(e){ console.error("Error in openProfiles:", e); } }
    function openUrl(url) { try { const trimmedUrl = String(url || '').trim(); if (trimmedUrl && (trimmedUrl.startsWith('http:') || trimmedUrl.startsWith('https:'))) { GM_openInTab(trimmedUrl, { active: true, setParent: true }); } } catch (e) { console.error("[TCAT] Failed to open URL:", url, e); } }
    function formatTimestamp(unixTimestamp) { if (!unixTimestamp) return '?'; try { return new Date(unixTimestamp * 1000).toLocaleString(); } catch (e) { return '?'; } }
    function isValidPlayerID(id) { return /^\d{1,8}$/.test(String(id).trim()); }
    function isValidFactionID(id) { return /^\d{1,5}$/.test(String(id).trim()); }

    // --- Chain Tool: Storage Functions ---
    function loadChainLists() { try { const storedLists = GM_getValue(CHAIN_LIST_STORAGE_KEY, null); if (storedLists) { chainLists = JSON.parse(storedLists); if (typeof chainLists !== 'object' || chainLists === null || Array.isArray(chainLists)) { console.warn("[TCAT ChainTool] Invalid format for stored chain lists, resetting."); chainLists = {}; GM_deleteValue(CHAIN_LIST_STORAGE_KEY); } } else { chainLists = {}; } const storedState = GM_getValue(CHAIN_TOOL_STATE_KEY, null); if (storedState) { const parsedState = JSON.parse(storedState); if (parsedState.currentListName && chainLists[parsedState.currentListName]) { chainToolState.currentListName = parsedState.currentListName; chainToolState.currentListIDs = [...chainLists[parsedState.currentListName]]; chainToolState.currentIndex = parsedState.currentIndex ?? -1; chainToolState.verifiedFactionId = parsedState.verifiedFactionId || null; chainToolState.verifiedFactionName = parsedState.verifiedFactionName || null; chainToolState.enrichedMemberDataCache = parsedState.enrichedMemberDataCache || {}; if (chainToolState.currentIndex >= chainToolState.currentListIDs.length) { chainToolState.currentIndex = chainToolState.currentListIDs.length - 1; } if (chainToolState.currentIndex < -1) { chainToolState.currentIndex = -1; } } else { resetChainToolState(); } } else { resetChainToolState(); } if (Object.keys(chainLists).length === 0 && !chainLists[DEFAULT_CHAIN_LIST_NAME]) { chainLists[DEFAULT_CHAIN_LIST_NAME] = []; saveChainLists(); chainToolState.currentListName = DEFAULT_CHAIN_LIST_NAME; chainToolState.currentListIDs = []; chainToolState.currentIndex = -1; } else if (!chainToolState.currentListName && Object.keys(chainLists).length > 0) { chainToolState.currentListName = Object.keys(chainLists)[0]; chainToolState.currentListIDs = [...chainLists[chainToolState.currentListName]]; chainToolState.currentIndex = -1; } else if (!chainToolState.currentListName && chainLists[DEFAULT_CHAIN_LIST_NAME]) { chainToolState.currentListName = DEFAULT_CHAIN_LIST_NAME; chainToolState.currentListIDs = [...chainLists[DEFAULT_CHAIN_LIST_NAME]]; chainToolState.currentIndex = -1; } saveChainToolState(); } catch (e) { console.error("[TCAT ChainTool] Error loading chain lists or state:", e); chainLists = {}; resetChainToolState(); } }
    function saveChainLists() { try { GM_setValue(CHAIN_LIST_STORAGE_KEY, JSON.stringify(chainLists)); } catch (e) { console.error("[TCAT ChainTool] Error saving chain lists:", e); showUserMessage("Save Error", "Failed to save chain lists.", "error"); } }
    function saveChainToolState() { try { const stateToSave = { currentListName: chainToolState.currentListName, currentIndex: chainToolState.currentIndex, verifiedFactionId: chainToolState.verifiedFactionId, verifiedFactionName: chainToolState.verifiedFactionName, enrichedMemberDataCache: chainToolState.enrichedMemberDataCache }; GM_setValue(CHAIN_TOOL_STATE_KEY, JSON.stringify(stateToSave)); } catch (e) { console.error("[TCAT ChainTool] Error saving chain tool state:", e); } }
    function resetChainToolState() { chainToolState.currentListName = null; chainToolState.currentIndex = -1; chainToolState.currentListIDs = []; chainToolState.verifiedFactionId = null; chainToolState.verifiedFactionName = null; chainToolState.enrichedMemberDataCache = {}; if (Object.keys(chainLists).length === 0 && !chainLists[DEFAULT_CHAIN_LIST_NAME]) { chainLists[DEFAULT_CHAIN_LIST_NAME] = []; saveChainLists(); chainToolState.currentListName = DEFAULT_CHAIN_LIST_NAME; } else if (Object.keys(chainLists).length > 0 && (!chainToolState.currentListName || !chainLists[chainToolState.currentListName])) { chainToolState.currentListName = Object.keys(chainLists)[0]; chainToolState.currentListIDs = [...chainLists[chainToolState.currentListName]]; } else if (!chainToolState.currentListName && chainLists[DEFAULT_CHAIN_LIST_NAME]) { chainToolState.currentListName = DEFAULT_CHAIN_LIST_NAME; chainToolState.currentListIDs = [...chainLists[DEFAULT_CHAIN_LIST_NAME]]; } saveChainToolState(); }

    // --- Color Scheme Function ---
    function applyColorScheme() { const schemeName = settings.colorScheme || 'dark_orange'; const scheme = COLOR_SCHEMES[schemeName] || COLOR_SCHEMES['dark_orange']; if (!settingsMenu) { return; } try { if (settingsMenu.style.setProperty) { for (const [variable, color] of Object.entries(scheme)) { settingsMenu.style.setProperty(variable, color); } } else { console.error("[TCAT] setProperty function not available on settingsMenu style."); } } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] Error applying color scheme:`, error); } }

    // --- API Status Function ---
    function testAndSetApiStatus(forceTest = false) { if (!settings.enableApiIntegration) { apiConnectionStatus = 'DISABLED'; updateStatusIndicator(); autofillWebhookUserDetails(); return; } if (!settings.apiKey) { apiConnectionStatus = 'NO_KEY'; updateStatusIndicator(); autofillWebhookUserDetails(); return; } if (apiConnectionStatus === 'CONNECTED' && !forceTest) { updateStatusIndicator(); autofillWebhookUserDetails(); return; } if (apiRequestOngoing && forceTest) { /* allow */ } else if (apiRequestOngoing || isSortingInProgress) { if (apiConnectionStatus !== 'WAITING') updateStatusIndicator(); return; } apiConnectionStatus = 'WAITING'; updateStatusIndicator(); apiRequestOngoing = true; makeManagedApiRequest({ method: "GET", url: `https://api.torn.com/user/?key=${settings.apiKey}&selections=basic&comment=TCATApiTest`, timeout: 7000, isCritical: true, onload: function(response) { try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.error) { console.warn(`[TCAT API Test] Error: ${data.error.error} (Code: ${data.error.code})`); apiConnectionStatus = 'FAILED'; } else if (data.player_id || data.userID || data.name || typeof data.level !== 'undefined') { apiConnectionStatus = 'CONNECTED'; if (settings.enableApiIntegration && settings.apiKey && apiConnectionStatus === 'CONNECTED' && (!settings.userTornName || !settings.userTornId)) { if (data.name && data.player_id) { const changedName = settings.userTornName !== data.name; const changedId = settings.userTornId !== data.player_id.toString(); if (changedName) saveSetting('userTornName', data.name); if (changedId) saveSetting('userTornId', data.player_id.toString()); if ((changedName || changedId) && settingsMenu && settingsMenu.style.display !== 'none') { const webhookTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`); if(webhookTab && webhookTab.classList.contains('active')) { const nameInput = document.getElementById(`${MENU_CSS_PREFIX}userTornName`); const idInput = document.getElementById(`${MENU_CSS_PREFIX}userTornId`); if(nameInput) nameInput.value = settings.userTornName; if(idInput) idInput.value = settings.userTornId; } showUserMessage("User Info Autofilled", "Your Torn Name & ID have been autofilled in Webhook settings.", "info"); } } } } else { apiConnectionStatus = 'FAILED'; } } else { apiConnectionStatus = 'FAILED'; console.warn(`[TCAT API Test] HTTP Status: ${response.status} - ${response.statusText}`); } } catch (e) { apiConnectionStatus = 'FAILED'; console.error("[TCAT API Test] Error processing response:", e, response.responseText); } }, onerror: function(responseDetails) { apiConnectionStatus = 'FAILED'; console.error("[TCAT API Test] Network error.", responseDetails); }, ontimeout: function() { apiConnectionStatus = 'FAILED'; console.error("[TCAT API Test] Request timeout."); }, onFinally: () => { apiRequestOngoing = false; updateStatusIndicator(); autofillWebhookUserDetails(); if (settingsMenu && settingsMenu.style.display !== 'none') { const apiTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-api`); if (apiTab && apiTab.classList.contains('active')) populateApiSettingsTab(apiTab); updateChainToolUI(); } } }, "API Connection Test"); }
    function autofillWebhookUserDetails() { if (settings.enableApiIntegration && settings.apiKey && apiConnectionStatus === 'CONNECTED' && (!settings.userTornName || !settings.userTornId)) { if (!apiRequestOngoing && !isSortingInProgress){ console.log("[TCAT] Attempting webhook user info autofill (if needed)."); testAndSetApiStatus(true); } } if (settingsMenu && settingsMenu.style.display !== 'none') { const webhookTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`); if (webhookTab && webhookTab.classList.contains('active')) { const nameInput = document.getElementById(`${MENU_CSS_PREFIX}userTornName`); const idInput = document.getElementById(`${MENU_CSS_PREFIX}userTornId`); if (nameInput && settings.userTornName) nameInput.value = settings.userTornName; if (idInput && settings.userTornId) idInput.value = settings.userTornId; } } }

    // --- Settings Logic ---
    function initSettings() { let loadedSettings = {}; try { const savedJSON = GM_getValue('tcatSettings', null); if (savedJSON) { try { const parsed = JSON.parse(savedJSON); if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) { loadedSettings = parsed; } else { console.warn("[TCAT] Invalid settings format found, deleting."); try { GM_deleteValue('tcatSettings'); } catch(e){} } } catch (e) { console.error("[TCAT] Error parsing settings:", e); try { GM_deleteValue('tcatSettings'); } catch(e){} } } settings = { ...DEFAULT_SETTINGS, ...loadedSettings }; ['4min', '3min', '2min', '1min', '30sec'].forEach(prefix => { if (typeof settings[`alert${prefix}CustomUrl`] === 'undefined') { settings[`alert${prefix}CustomUrl`] = DEFAULT_SETTINGS[`alert${prefix}CustomUrl`]; } }); settings.webhookUrl = settings.webhookUrl || ''; settings.apiKey = settings.apiKey || ''; if (!savedJSON) { saveAllSettings(); } loadChainLists(); testAndSetApiStatus(); } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] CRITICAL Error initializing settings:`, e); settings = { ...DEFAULT_SETTINGS }; showUserMessage("TCAT Error", "Failed to initialize settings. Using defaults.", "error"); } applyUISettings(); }
    function saveSetting(key, value) { try { const validKeys = Object.keys(DEFAULT_SETTINGS); let isKnownKey = validKeys.includes(key); if (!isKnownKey) { ['4min', '3min', '2min', '1min', '30sec'].forEach(prefix => { if (key === `alert${prefix}CustomUrl`) isKnownKey = true; }); } if (!isKnownKey) { console.warn(`[TCAT] Attempting to save unknown or removed setting: ${key}`); return; } settings[key] = value; let needsIntervalRestart = false; let needsAlertReset = false; if (key === 'checkIntervalChain') { needsIntervalRestart = true; } else if (key.startsWith('alert') && (key.endsWith('Enable') || key.endsWith('ActionsEnable') || key.endsWith('CustomUrl'))) { needsAlertReset = true; } else if (key === 'enableWebhook' || key === 'webhookUrl' || key === 'webhookMessageFormat' || key === 'userTornName' || key === 'userTornId' || key === 'webhookUseUserInfo' || key === 'enableApiIntegration' || key === 'apiKey' || key === 'factionImportConfirmSize' || key === 'enableRetaliationWebhook' || key.startsWith('retaliationShow') || key === 'retaliationEmbedColor' || key === 'webhookRetaliationMessage' || key === 'menuWidth' || key === 'menuHeight') { if (key === 'enableApiIntegration' || key === 'apiKey') { if(key === 'apiKey'){ chainToolState.verifiedFactionId = null; chainToolState.verifiedFactionName = null; saveChainToolState(); } testAndSetApiStatus(true); } if (settingsMenu && settingsMenu.style.display !== 'none') { const currentActiveTab = settingsMenu.querySelector(`.${MENU_CSS_PREFIX}tab-button.active`)?.dataset.tabId; if (key.startsWith('webhook') || key.startsWith('retaliation') || key === 'enableWebhook') { if (currentActiveTab === 'webhook') populateWebhookSettingsTab(settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`)); } else if (key === 'enableApiIntegration' || key === 'apiKey') { if (currentActiveTab === 'api') populateApiSettingsTab(settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-api`)); updateChainToolUI(); } } } else if (key === 'alert4minBorder' || key === 'alert3minBorder' || key === 'alert2minBorder' || key === 'alert1minBorder' || key === 'alert30secBorder' || key.includes('Color') || key.includes('Emoji') || key === 'flashTitle' || key === 'solidTabAlert') { needsAlertReset = true; } saveAllSettings(); applyUISettings(); if (key === 'colorScheme' || key === 'menuWidth' || key === 'menuHeight') { applyColorScheme(); applyUISettings(); /* For menu size */ } if(needsAlertReset) { alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; resetSolidTitle(); setBodyBorder(null); } if (needsIntervalRestart) { restartIntervals(); } } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error saving setting ${key}:`, e); } }
    function saveAllSettings() { try { GM_setValue('tcatSettings', JSON.stringify(settings)); } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error saving all settings:`, e); } }
    function resetSettings() { try { if (confirm("Reset ALL TCAT settings (Alerts, General, Webhook, API, About/Appearance - NOT Chain Lists) to default values?")) { try { GM_deleteValue('tcatSettings'); } catch(e){} settings = { ...DEFAULT_SETTINGS }; alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; resetSolidTitle(); setBodyBorder(null); testAndSetApiStatus(true); showUserMessage("Settings Reset", "General/Alert/Webhook/API/Appearance settings reset. Chain Lists remain.", "success"); if(settingsMenu) populateMenuTabs(); applyUISettings(); applyColorScheme(); restartIntervals(); } } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error resetting settings:`, e); showUserMessage("Reset Error", "Failed to reset settings.", "error"); } }
    function applyUISettings() { if (settingsMenu && settingsMenu.style) { if (settings.fontSize) settingsMenu.style.fontSize = settings.fontSize; if (settings.menuWidth) settingsMenu.style.width = settings.menuWidth; if (settings.menuHeight) settingsMenu.style.height = settings.menuHeight; } updateStatusIndicator(); }
    function updateStatusIndicator() { if (!settingsMenu) return; const indicator = settingsMenu.querySelector(`#${STATUS_INDICATOR_ID}`); const apiCallDisplay = settingsMenu.querySelector(`#${API_CALL_COUNT_DISPLAY_ID}`); if (indicator) { let chainStatusText = "Chain: "; let chainStatusColor = 'var(--tcat-text-light-color)'; let chainDetailsArray = []; if (chainIntervalId) { if (isChainCurrentlyActive) { chainStatusText += `Active`; chainStatusColor = '#2ECC71'; } else { chainStatusText += `No Chain`; chainStatusColor = '#3498DB'; } chainDetailsArray.push(`(Scan ${settings.checkIntervalChain / 1000}s)`); } else { chainStatusText += 'Scan OFF'; chainStatusColor = '#F39C12'; } const chainFullText = `${chainStatusText} ${chainDetailsArray.join(' ')}`; const chainTooltip = `Chain Scan Interval: ${settings.checkIntervalChain / 1000}s`; let apiStatusTextPart = "API: "; let apiStatusColor = 'var(--tcat-text-light-color)'; let apiTooltip = `API Status: ${apiConnectionStatus}`; switch (apiConnectionStatus) { case 'NO_KEY': apiStatusTextPart += "No Key!"; apiStatusColor = 'var(--tcat-color-orange, orange)'; break; case 'DISABLED': apiStatusTextPart += "Disabled"; apiStatusColor = 'var(--tcat-text-light-color)'; break; case 'WAITING': apiStatusTextPart += "Waiting..."; apiStatusColor = '#3498DB'; break; case 'CONNECTED': apiStatusTextPart += "Connected"; apiStatusColor = '#2ECC71'; break; case 'FAILED': apiStatusTextPart += "Failed"; apiStatusColor = 'var(--tcat-danger-color)'; break; case 'UNKNOWN': default: apiStatusTextPart += "Unknown"; apiStatusColor = 'var(--tcat-text-light-color)'; break; } indicator.innerHTML = `<span style="color: ${chainStatusColor};" title="${chainTooltip}">${chainFullText}</span> <span style="font-weight:normal; color: var(--tcat-text-light-color);">|</span> <span style="color: ${apiStatusColor};" title="${apiTooltip}">${apiStatusTextPart}</span>`; } tcatUpdateApiCallDisplay(); }

    // --- UI Element Creation Functions ---
    function createSection(titleText, options = {}) { try { const section = document.createElement('div'); section.className = `${MENU_CSS_PREFIX}section`; if(options.id) section.id = options.id; const title = document.createElement('h3'); title.textContent = titleText || 'Section'; title.className = `${MENU_CSS_PREFIX}section-title ${options.titleClass || ''}`; section.appendChild(title); if (options.description) { const desc = document.createElement('p'); desc.className = `${MENU_CSS_PREFIX}section-description`; desc.innerHTML = options.description; section.appendChild(desc); } return section; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating section:`, e); return document.createElement('div'); } }
    function createCheckbox(settingKey, labelText, parentElement, tooltip = '') { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container ${MENU_CSS_PREFIX}checkbox-container`; if (tooltip) container.title = tooltip; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = `${MENU_CSS_PREFIX}${settingKey}`; checkbox.checked = settings[settingKey] === true; checkbox.onchange = (event) => { saveSetting(settingKey, event.target.checked); }; const label = document.createElement('label'); label.textContent = ` ${labelText}`; label.htmlFor = checkbox.id; container.appendChild(checkbox); container.appendChild(label); parentElement.appendChild(container); return container; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating checkbox for ${settingKey}:`, e); return null; } }
    function createTextInput(settingKeyOrId, labelText, parentElement, options = {}) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container ${options.containerClass || ''}`; const finalId = (options.isSetting === false) ? settingKeyOrId : `${MENU_CSS_PREFIX}${settingKeyOrId}`; const label = document.createElement('label'); label.textContent = `${labelText}: `; label.htmlFor = finalId; const input = document.createElement('input'); input.type = options.type || 'text'; input.id = finalId; if (options.isSetting !== false) { input.value = settings[settingKeyOrId] !== undefined ? settings[settingKeyOrId] : (options.defaultValue || ''); } else { input.value = options.value !== undefined ? options.value : (options.defaultValue || ''); } input.placeholder = options.placeholder || ''; input.className = `${MENU_CSS_PREFIX}input`; if(options.type === 'number') { input.min = options.min; input.max = options.max; input.step = options.step || 1; } if(options.inputMode) input.inputMode = options.inputMode; if(options.pattern) input.pattern = options.pattern; if (options.width) input.style.width = options.width; input.autocomplete = options.autocomplete || 'off'; input.disabled = options.disabled ?? false; if (options.isSetting !== false) { input.onchange = (event) => { let valueToSave = event.target.value; if (options.type === 'number') { valueToSave = parseInt(valueToSave, 10); if (isNaN(valueToSave)) valueToSave = options.defaultValue ?? ''; event.target.value = valueToSave; } else if (options.trim === true) { valueToSave = valueToSave.trim(); } if (options.validator && !options.validator(valueToSave)) { showUserMessage("Invalid Input", options.validationMsg || "Invalid format.", "warning"); event.target.value = (settings[settingKeyOrId] !== undefined ? settings[settingKeyOrId] : (options.defaultValue || '')); return; } if (valueToSave === '' && options.defaultValue !== undefined) { valueToSave = options.defaultValue; } saveSetting(settingKeyOrId, valueToSave); }; } else if (options.onchange) { input.onchange = options.onchange; } if (options.onblur) { input.onblur = options.onblur; } container.appendChild(label); container.appendChild(input); if (options.unit) { const unitSpan = document.createElement('span'); unitSpan.textContent = ` ${options.unit}`; unitSpan.className = `${MENU_CSS_PREFIX}input-unit`; container.appendChild(unitSpan); } parentElement.appendChild(container); return container; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating text input for '${settingKeyOrId}':`, e); return null; } }
    function createPasswordInput(settingKey, labelText, parentElement, options = {}) { return createTextInput(settingKey, labelText, parentElement, { ...options, type: 'password', autocomplete: 'new-password' }); }
    function createIntervalInput(settingKey, labelText, parentElement, options = {}) { return createTextInput(settingKey, labelText, parentElement, { ...options, type: 'number', width: options.width || '80px', unit: 'ms', containerClass: `${MENU_CSS_PREFIX}interval-input-container` }); }
    function createWebhookUrlInput(settingKey, labelText, parentElement, options = {}) { return createPasswordInput(settingKey, labelText, parentElement, { ...options, placeholder: options.placeholder || 'Enter Discord Webhook URL', width: '95%' }); }
    function createTextArea(settingKey, labelText, parentElement, options = {}) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container`; container.style.flexDirection = 'column'; container.style.alignItems = 'stretch'; const label = document.createElement('label'); label.textContent = `${labelText}:`; label.htmlFor = `${MENU_CSS_PREFIX}${settingKey}`; label.style.marginBottom = '3px'; const textarea = document.createElement('textarea'); textarea.id = `${MENU_CSS_PREFIX}${settingKey}`; textarea.value = settings[settingKey] || options.defaultValue || ''; textarea.rows = options.rows || 3; textarea.placeholder = options.placeholder || ''; textarea.className = `${MENU_CSS_PREFIX}textarea`; textarea.onchange = (event) => { saveSetting(settingKey, event.target.value); }; container.appendChild(label); container.appendChild(textarea); parentElement.appendChild(container); return container; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating textarea for ${settingKey}:`, e); return null; } }
    function createColorPicker(settingKey, labelText, parentElement, options = {}) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container`; container.style.justifyContent = 'space-between'; container.style.width = '150px'; const label = document.createElement('label'); label.textContent = `${labelText}: `; label.htmlFor = `${MENU_CSS_PREFIX}${settingKey}`; const input = document.createElement('input'); input.type = 'color'; input.id = `${MENU_CSS_PREFIX}${settingKey}`; input.value = settings[settingKey] || options.defaultValue || '#000000'; input.className = `${MENU_CSS_PREFIX}input ${MENU_CSS_PREFIX}input-color`; input.onchange = (event) => { saveSetting(settingKey, event.target.value); }; container.appendChild(label); container.appendChild(input); parentElement.appendChild(container); return container; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating color picker for ${settingKey}:`, e); return null; } }
    function createEmojiInput(settingKey, labelText, parentElement, options = {}) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container ${MENU_CSS_PREFIX}emoji-input-container`; const label = document.createElement('label'); label.textContent = `${labelText}: `; label.htmlFor = `${MENU_CSS_PREFIX}${settingKey}`; const input = document.createElement('input'); input.type = 'text'; input.id = `${MENU_CSS_PREFIX}${settingKey}`; input.value = settings[settingKey] || options.defaultValue || ''; input.maxLength = 2; input.className = `${MENU_CSS_PREFIX}input ${MENU_CSS_PREFIX}input-emoji`; input.style.width = options.width || '40px'; input.style.textAlign = 'center'; input.onchange = (event) => { let value = event.target.value; if ([...value].length > 2) { value = [...value].slice(0, 2).join(''); event.target.value = value; } saveSetting(settingKey, value); }; container.appendChild(label); container.appendChild(input); parentElement.appendChild(container); return container; } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating emoji input for ${settingKey}:`, e); return null; } }
    function createProfileInput(parentElement) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container`; container.style.alignItems = 'flex-start'; container.style.flexDirection = 'column'; const label = document.createElement('label'); label.textContent = 'Profile IDs to Open:'; label.style.width = '100%'; label.style.marginBottom = '3px'; container.appendChild(label); const inputWrapper = document.createElement('div'); inputWrapper.style.display = 'flex'; inputWrapper.style.gap = '5px'; for (let i = 0; i < 3; i++) { const input = document.createElement('input'); input.type = 'text'; input.inputMode = 'numeric'; input.pattern = '[0-9]*'; input.id = `${MENU_CSS_PREFIX}emergencyProfiles${i}`; input.placeholder = `ID ${i + 1}`; input.value = (Array.isArray(settings.emergencyProfiles) && settings.emergencyProfiles[i]) ? settings.emergencyProfiles[i] : ''; input.className = `${MENU_CSS_PREFIX}input`; input.style.width="80px"; input.autocomplete = 'off'; input.onchange = (event) => { const value = event.target.value.trim(); if (value === '' || /^\d+$/.test(value)) { if (!Array.isArray(settings.emergencyProfiles)) settings.emergencyProfiles = ['', '', '']; settings.emergencyProfiles[i] = value; saveSetting('emergencyProfiles', [...settings.emergencyProfiles]); } else { event.target.value = (Array.isArray(settings.emergencyProfiles) && settings.emergencyProfiles[i]) ? settings.emergencyProfiles[i] : ''; showUserMessage('Invalid Input', 'Profile ID must be numbers only.', 'warning'); } }; inputWrapper.appendChild(input); } container.appendChild(inputWrapper); parentElement.appendChild(container); return container; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating profile input:`, e); return null; } }
    function createUrlInput(settingKey, labelText, parentElement, options = {}) { return createTextInput(settingKey, labelText, parentElement, { ...options, type: 'url', placeholder: options.placeholder || 'https://...' }); }
    function createEnableToggle(levelPrefix, labelText, parentElement) { try { const sectionContainer = document.createElement('div'); sectionContainer.className = `${MENU_CSS_PREFIX}alert-action-group`; createCheckbox(`alert${levelPrefix}Enable`, labelText, sectionContainer, `Enable alerts for ${levelPrefix}`); if (parentElement instanceof Node) { parentElement.appendChild(sectionContainer); } else { console.error(`[TCAT] createEnableToggle: Invalid parentElement provided for level ${levelPrefix}`); return null; } return sectionContainer; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating enable toggle for level ${levelPrefix}:`, e); return null; } }
    function createTabPreview(parentElement) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}tab-preview-container`; const label = document.createElement('label'); label.textContent = "Tab Title Preview:"; label.style.display = 'block'; label.style.marginBottom = '3px'; const previewArea = document.createElement('div'); previewArea.id = TAB_PREVIEW_ID; previewArea.className = `${MENU_CSS_PREFIX}tab-preview-area`; previewArea.textContent = `Original: ${originalTitle.substring(0,30)}`; const button = document.createElement('button'); button.textContent = "Preview Title Alert"; button.className = `${MENU_CSS_PREFIX}button`; button.style.marginTop = '5px'; button.onclick = () => runTabPreview(false); container.appendChild(label); container.appendChild(previewArea); container.appendChild(button); parentElement.appendChild(container); return container; } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating tab preview:`, e); return null; } }
    function runTabPreview(silent = false) { try { if (tabPreviewInterval) { clearInterval(tabPreviewInterval); tabPreviewInterval = null; document.title = originalTitle; if (!silent) { const previewArea = document.getElementById(TAB_PREVIEW_ID); if (previewArea) previewArea.textContent = `Preview finished. Original: ${originalTitle.substring(0,30)}`; } return; } if (!silent) { const previewArea = document.getElementById(TAB_PREVIEW_ID); if (previewArea) previewArea.textContent = "Running preview..."; } const useSolid = settings.solidTabAlert; const useFlash = settings.flashTitle; const emojis = [settings.preWarningEmoji4min || '?', settings.preWarningEmoji3min || '?', settings.preWarningEmoji2min || '?', settings.preWarningEmoji1min || '?', settings.emergencyEmoji || '?']; const levels = ['4', '3', '2', '1', '0.5']; let currentLevelIndex = 0; let flashState = false; const flashIntervalMs = 500; const levelChangeMs = 1000; tabPreviewInterval = setInterval(() => { try { const emoji = emojis[currentLevelIndex]; const levelText = levels[currentLevelIndex]; if (useSolid) { document.title = `${emoji} Alert ${levelText}m ${emoji}`; } else if (useFlash) { flashState = !flashState; document.title = flashState ? `${emoji} CHAIN ALERT ${emoji}` : originalTitle; } else { document.title = originalTitle; } if (!useSolid || (useSolid && (Date.now() % levelChangeMs < flashIntervalMs))) { currentLevelIndex = (currentLevelIndex + 1) % emojis.length; } } catch(intervalError) { console.error("Error inside runTabPreview interval:", intervalError); try{clearInterval(tabPreviewInterval); tabPreviewInterval = null; document.title = originalTitle;}catch{} } }, flashIntervalMs); setTimeout(() => { try { if (tabPreviewInterval) { clearInterval(tabPreviewInterval); tabPreviewInterval = null; document.title = originalTitle; if (!silent) { const previewArea = document.getElementById(TAB_PREVIEW_ID); if (previewArea) previewArea.textContent = `Preview finished. Original: ${originalTitle.substring(0,30)}`; } } } catch(timeoutError){ console.error("Error inside runTabPreview timeout:", timeoutError); try{document.title = originalTitle;}catch{} } }, 5000); } catch(e) { console.error("Error starting runTabPreview:", e); } }
    function createDropdownInput(settingKeyOrId, labelText, parentElement, optionsArray, options = {}) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container`; const label = document.createElement('label'); label.textContent = `${labelText}: `; label.htmlFor = (options.isSetting === false ? settingKeyOrId : `${MENU_CSS_PREFIX}${settingKeyOrId}`); const select = document.createElement('select'); select.id = (options.isSetting === false ? settingKeyOrId : `${MENU_CSS_PREFIX}${settingKeyOrId}`); select.className = `${MENU_CSS_PREFIX}select`; if (options.width) select.style.width = options.width; optionsArray.forEach(opt => { const option = document.createElement('option'); option.value = opt.value; option.textContent = opt.text || opt.value; option.disabled = opt.disabled ?? false; if(options.isSetting !== false) { if (settings[settingKeyOrId] === opt.value) { option.selected = true; } } else if (options.selectedValue === opt.value) { option.selected = true; } select.appendChild(option); }); if(options.isSetting !== false) { select.onchange = (event) => { saveSetting(settingKeyOrId, event.target.value); if (settingKeyOrId === 'fontSize' || settingKeyOrId === 'menuWidth' || settingKeyOrId === 'menuHeight') { applyUISettings(); /* Apply immediately */ } }; } else if (options.onchange) { select.onchange = options.onchange; } container.appendChild(label); container.appendChild(select); parentElement.appendChild(container); return select; } catch (e) { console.error(`[TCAT] Error creating dropdown for ${settingKeyOrId}:`, e); return null; } }
    function createColorSchemeSelector(parentElement) { try { const container = document.createElement('div'); container.className = `${MENU_CSS_PREFIX}setting-container`; const label = document.createElement('label'); label.textContent = 'Color Scheme: '; label.htmlFor = `${MENU_CSS_PREFIX}colorScheme`; const select = document.createElement('select'); select.id = `${MENU_CSS_PREFIX}colorScheme`; select.className = `${MENU_CSS_PREFIX}select`; Object.keys(COLOR_SCHEMES).forEach(schemeName => { const option = document.createElement('option'); option.value = schemeName; option.textContent = schemeName.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); if (settings.colorScheme === schemeName) option.selected = true; select.appendChild(option); }); select.onchange = (event) => { saveSetting('colorScheme', event.target.value); applyColorScheme(); }; container.appendChild(label); container.appendChild(select); parentElement.appendChild(container); return container; } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating color scheme selector:`, e); return null; } }
    function createButton(text, parentElement, onClick, options = {}) { try { const button = document.createElement('button'); button.textContent = text; button.className = `${MENU_CSS_PREFIX}button ${options.extraClass || ''}`; button.disabled = options.disabled ?? false; if (options.id) button.id = options.id; button.onclick = onClick; if (options.style) { Object.assign(button.style, options.style); } parentElement.appendChild(button); return button; } catch (e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error creating button:`, e); return null; } }

    // --- New UI Helper: Styled Enable/Disable Button ---
    function createStyledEnableDisableButton(settingKey, baseLabel, parentElement, initialState, onClickCallback, options = {}) {
        try {
            const button = document.createElement('button');
            button.id = `${MENU_CSS_PREFIX}${settingKey}-toggle-btn`;
            button.className = `${MENU_CSS_PREFIX}button ${MENU_CSS_PREFIX}button-enable-disable`;

            function updateButtonAppearance(isEnabled) {
                if (isEnabled) {
                    button.textContent = `${baseLabel}: Enabled`;
                    button.classList.remove(`${MENU_CSS_PREFIX}button-disabled`);
                    button.classList.add(`${MENU_CSS_PREFIX}button-enabled`);
                } else {
                    button.textContent = `${baseLabel}: Disabled`;
                    button.classList.remove(`${MENU_CSS_PREFIX}button-enabled`);
                    button.classList.add(`${MENU_CSS_PREFIX}button-disabled`);
                }
                button.disabled = options.disabledCallback ? options.disabledCallback() : false;
            }

            updateButtonAppearance(initialState);

            button.onclick = () => {
                const newState = !settings[settingKey];
                saveSetting(settingKey, newState); // saveSetting will trigger UI update via populateWebhookSettingsTab
                // Update appearance immediately for responsiveness before full tab repaint if needed
                updateButtonAppearance(newState);
                 // Manually trigger dependant UI updates for the specific tab
                if (settingsMenu && settingsMenu.style.display !== 'none') {
                    const webhookTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`);
                    if(webhookTab && webhookTab.classList.contains('active')) {
                        populateWebhookSettingsTab(webhookTab); // Re-populate to show/hide sections
                    }
                }
            };

            if (options.tooltip) button.title = options.tooltip;
            if (options.style) Object.assign(button.style, options.style);

            parentElement.appendChild(button);
            return button;
        } catch (e) {
            console.error(`[TCAT] Error creating styled enable/disable button for ${settingKey}:`, e);
            return null;
        }
    }


    // --- Alert Logic Functions ---
    function setBodyBorder(level) { try { if (!borderOverlay || !document.body.contains(borderOverlay)) { borderOverlay = document.getElementById(BORDER_OVERLAY_ID); if (!borderOverlay) { borderOverlay = document.createElement('div'); borderOverlay.id = BORDER_OVERLAY_ID; document.body.appendChild(borderOverlay); } } if (!borderOverlay) { console.error("[TCAT] Failed find/create border overlay."); return; } let borderColor = 'transparent'; let newBorderLevel = null; const levelMap = { 'emerg': settings.emergencyColor, 'warn': settings.preWarningColor1min, 'pre2': settings.preWarningColor2min, 'pre': settings.preWarningColor3min, 'pre4': settings.preWarningColor4min }; const enabledMap = { 'emerg': settings.alert30secBorder, 'warn': settings.alert1minBorder, 'pre2': settings.alert2minBorder, 'pre': settings.alert3minBorder, 'pre4': settings.alert4minBorder }; if (level && levelMap[level] && enabledMap[level]) { borderColor = levelMap[level]; newBorderLevel = level; } if (borderColor === 'transparent' && isTestingAlert) { return; } if (borderColor !== 'transparent') { borderOverlay.style.borderColor = borderColor; borderOverlay.style.display = 'block'; } else { if (currentBorderLevel !== null) { borderOverlay.style.display = 'none'; borderOverlay.style.borderColor = 'transparent'; } } currentBorderLevel = newBorderLevel; } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error in setBodyBorder:`, e); if (borderOverlay && borderOverlay.style) { try { borderOverlay.style.display = 'none'; } catch(e2){} } currentBorderLevel = null; } }
    function flashTitle(level) { try { if (!settings.tabTitleAlert) return; const enabledMap = { 'emerg': settings.alert30secTab, 'warn': settings.alert1minTab, 'pre2': settings.alert2minTab, 'pre': settings.alert3minTab, 'pre4': settings.alert4minTab }; if (!level || !enabledMap[level]) return; if (settings.solidTabAlert) { setSolidTitle(level); return; } if (isFlashing) return; solidTitleActive = false; isFlashing = true; let count = 0; const maxFlashes = 10; const interval = 500; const emojiMap = { 'emerg': settings.emergencyEmoji, 'warn': settings.preWarningEmoji1min, 'pre2': settings.preWarningEmoji2min, 'pre': settings.preWarningEmoji3min, 'pre4': settings.preWarningEmoji4min }; const emoji = emojiMap[level] || '❗'; const flashText = `${emoji} CHAIN ALERT ${emoji}`; const flashInterval = setInterval(() => { try { document.title = (document.title === originalTitle) ? flashText : originalTitle; if (++count >= maxFlashes) { clearInterval(flashInterval); document.title = originalTitle; isFlashing = false; } } catch(e) { console.error("Error in flashTitle interval:", e); try { clearInterval(flashInterval); document.title = originalTitle; } catch(e2){} isFlashing = false; } }, interval); window.addEventListener('beforeunload', () => { if (isFlashing) { try { clearInterval(flashInterval); document.title = originalTitle; } catch(e){} isFlashing=false; } }, { once: true }); } catch(e) { console.error("Error starting flashTitle:", e); isFlashing = false; } }
    function setSolidTitle(level) { try { if (!settings.tabTitleAlert || !settings.solidTabAlert) return; const enabledMap = { 'emerg': settings.alert30secTab, 'warn': settings.alert1minTab, 'pre2': settings.alert2minTab, 'pre': settings.alert3minTab, 'pre4': settings.alert4minTab }; if (!level || !enabledMap[level]) return; const emojiMap = { 'emerg': settings.emergencyEmoji, 'warn': settings.preWarningEmoji1min, 'pre2': settings.preWarningEmoji2min, 'pre': settings.preWarningEmoji3min, 'pre4': settings.preWarningEmoji4min }; const levelTextMap = { 'emerg': '0.5', 'warn': '1', 'pre2': '2', 'pre': '3', 'pre4': '4' }; const emoji = emojiMap[level] || '❗'; const levelText = levelTextMap[level] || '?'; document.title = `${emoji} Alert ${levelText}m ${emoji}`; solidTitleActive = true; isFlashing = false; } catch(e) { console.error("Error in setSolidTitle:", e); } }
    function resetSolidTitle() { try { if (isTestingAlert) return; if (solidTitleActive) { document.title = originalTitle; solidTitleActive = false; } } catch(e) { console.error("Error in resetSolidTitle:", e); } }

    // --- Emergency Actions Helper ---
    function triggerEmergencyActions(levelPrefixKey) { openProfiles(settings.emergencyProfiles); const customUrlSettingKey = `alert${levelPrefixKey}CustomUrl`; const customUrl = settings[customUrlSettingKey]; if (customUrl && String(customUrl).trim() !== '') { openUrl(customUrl); } else if (settings.emergencyUrl && String(settings.emergencyUrl).trim() !== '') { openUrl(settings.emergencyUrl); } }

    // --- Core Logic: Chain Timer Check ---
    function checkChainTimer() { try { const timerElement = document.querySelector(CHAIN_TIMER_SELECTOR); if (!timerElement) { if (isChainCurrentlyActive) { isChainCurrentlyActive = false; if (!isTestingAlert) { setBodyBorder(null); resetSolidTitle(); } alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; } return; } const chainLinkElement = timerElement.closest('a.chain-bar___vjdPL'); const isChainCoolingDown = chainLinkElement?.classList.contains('bar-negative___F7k99') ?? true; const timeLeftText = timerElement.textContent?.trim(); if (!timeLeftText || isChainCoolingDown) { if (isChainCurrentlyActive) { isChainCurrentlyActive = false; if (!isTestingAlert) { setBodyBorder(null); resetSolidTitle(); } alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; } return; } if (!isChainCurrentlyActive) { isChainCurrentlyActive = true; alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; } const secondsLeft = parseTimeString(timeLeftText); let activeLevel = null; if (secondsLeft <= 0) { if (isChainCurrentlyActive) { isChainCurrentlyActive = false; if (!isTestingAlert) { setBodyBorder(null); resetSolidTitle(); } alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; } return; } if (secondsLeft <= 30 && settings.alert30secEnable) { activeLevel = 'emerg'; if (!alerted30sec) { flashTitle('emerg'); if (settings.alert30secActionsEnable) triggerEmergencyActions('30sec'); alerted30sec = true; alerted1min = true; alerted2min = true; alerted3min = true; alerted4min = true; } } else if (secondsLeft <= 60 && settings.alert1minEnable) { activeLevel = 'warn'; if (!alerted1min) { flashTitle('warn'); if (settings.alert1minActionsEnable) triggerEmergencyActions('1min'); alerted1min = true; alerted2min = true; alerted3min = true; alerted4min = true; } } else if (secondsLeft <= 120 && settings.alert2minEnable) { activeLevel = 'pre2'; if (!alerted2min) { flashTitle('pre2'); if (settings.alert2minActionsEnable) triggerEmergencyActions('2min'); alerted2min = true; alerted3min = true; alerted4min = true; } } else if (secondsLeft <= 180 && settings.alert3minEnable) { activeLevel = 'pre'; if (!alerted3min) { flashTitle('pre'); if (settings.alert3minActionsEnable) triggerEmergencyActions('3min'); alerted3min = true; alerted4min = true; } } else if (secondsLeft <= 240 && settings.alert4minEnable) { activeLevel = 'pre4'; if (!alerted4min) { flashTitle('pre4'); if (settings.alert4minActionsEnable) triggerEmergencyActions('4min'); alerted4min = true; } } setBodyBorder(activeLevel); } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] Error in checkChainTimer:`, error); if (isChainCurrentlyActive) { isChainCurrentlyActive = false; if (!isTestingAlert) { setBodyBorder(null); resetSolidTitle(); } alerted4min = alerted3min = alerted2min = alerted1min = alerted30sec = false; } } }

    // --- Interval Management ---
    function restartIntervals() { try { if (chainIntervalId) clearInterval(chainIntervalId); chainIntervalId = null; if (statusIntervalId) clearInterval(statusIntervalId); statusIntervalId = null; if(apiCallDisplayIntervalId) clearInterval(apiCallDisplayIntervalId); apiCallDisplayIntervalId = null; const chainCheckMs = Math.max(500, settings.checkIntervalChain || 3000); chainIntervalId = setInterval(checkChainTimer, chainCheckMs); statusIntervalId = setInterval(updateStatusIndicator, 15000); apiCallDisplayIntervalId = setInterval(tcatUpdateApiCallDisplay, 1000); checkChainTimer(); updateStatusIndicator(); tcatUpdateApiCallDisplay(); } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] Error restarting intervals:`, error); } }

    // --- Webhook Logic (Modified for Embeds) ---
    function sendWebhookNotification(eventData, alertType = 'generic') {
        if (!settings.enableWebhook || !settings.webhookUrl || !eventData) {
            return;
        }

        let payload = {};
        const yourName = settings.webhookUseUserInfo && settings.userTornName ? settings.userTornName : 'You';
        const yourId = settings.webhookUseUserInfo && settings.userTornId ? settings.userTornId : 'YourID'; // Use placeholder if not set
        const attackerName = eventData.attackerName || 'Unknown Attacker';
        const attackerId = eventData.attackerId || '0';
        const action = eventData.action || 'interacted with you';
        const profileUrl = `https://www.torn.com/profiles.php?XID=${attackerId}`;
        const timeString = eventData.timestampText || formatTimestamp(eventData.eventTimestamp) || new Date().toLocaleString();

        // Simplified: if retaliation webhook is enabled, always try to send an embed.
        if (alertType === 'retaliation' && settings.enableRetaliationWebhook) {
            const embed = {
                title: `⚔️ Retaliation Alert: ${yourName} ${action}!`,
                description: settings.webhookUseUserInfo ?
                    `**${yourName} [${yourId}]** was ${action} by **[${attackerName}](${profileUrl}) [${attackerId}]**.` :
                    `You were ${action} by **[${attackerName}](${profileUrl}) [${attackerId}]**.`,
                color: parseInt(settings.retaliationEmbedColor.replace("#", ""), 16),
                fields: [
                    { name: "Attacker", value: `[${attackerName}](${profileUrl}) [${attackerId}]`, inline: true },
                    { name: "Time", value: timeString, inline: true }
                ],
                timestamp: new Date().toISOString(),
                footer: {
                    text: `TCAT v${SCRIPT_VERSION}`,
                    icon_url: NEW_LOGO_URL // Using constant directly
                }
            };

            if (eventData.description) {
                 embed.fields.push({ name: "Details", value: eventData.description.replace(/<a .*?>|<\/a>/gi, '').replace(/<br\/?>/gi, ' ').replace(/ +/g, ' ').trim(), inline: false });
            }
            // Add more fields here based on future settings (e.g., respect, faction)

            payload.embeds = [embed];
        } else if (alertType === 'generic' || !settings.enableRetaliationWebhook) { // Fallback for generic or if retaliation is off
            let message = settings.webhookMessageFormat || DEFAULT_SETTINGS.webhookMessageFormat;
            const placeholders = {
                '{attackerName}': attackerName, '{attackerId}': attackerId, '{action}': action,
                '{timeString}': timeString, '{profileUrl}': profileUrl,
                '{description}': eventData.description ? eventData.description.replace(/<a .*?>|<\/a>/gi, '').replace(/<br\/?>/gi, ' ').replace(/ +/g, ' ').trim() : 'N/A',
                '{yourName}': yourName, '{yourId}': yourId,
                '{yourProfileUrl}': yourId && yourId !== 'YourID' ? `https://www.torn.com/profiles.php?XID=${yourId}` : ''
            };
            for (const [key, value] of Object.entries(placeholders)) {
                message = message.replace(new RegExp(key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), String(value));
            }
            payload.content = message;
        } else {
            return; // Don't send if specific alert type is disabled
        }

        if (Object.keys(payload).length > 0) {
            makeManagedApiRequest({
                method: "POST", url: settings.webhookUrl, headers: { "Content-Type": "application/json" },
                data: JSON.stringify(payload), timeout: 10000, isCritical: true,
                onload: function(response) { if (!(response.status >= 200 && response.status < 300)) { console.error(`[TCAT Webhook] Error sending notification: Status ${response.status}`, response.responseText); showUserMessage("Webhook Error", `Failed to send: Status ${response.status}. Check console/URL.`, "error"); } else { console.log("[TCAT Webhook] Notification sent successfully."); showUserMessage("Webhook Sent", `Test ${alertType} webhook sent!`, "success");} },
                onerror: function(response) { console.error("[TCAT Webhook] Request Error:", response); showUserMessage("Webhook Error", "Network error sending webhook. Check console.", "error"); },
                ontimeout: function() { console.error("[TCAT Webhook] Request Timeout"); showUserMessage("Webhook Error", "Webhook request timed out.", "warning"); }
            }, "Webhook Send");
        }
    }

    // --- Test Functions ---
    function testChainAlert(level) { try { const levelSettingMap = { 'emerg': '30sec', 'warn': '1min', 'pre2': '2min', 'pre': '3min', 'pre4': '4min' }; const settingPrefix = levelSettingMap[level]; if (!settingPrefix) { console.error("[TCAT TEST] Invalid level for testChainAlert:", level); return; } const actionsEnabledKey = `alert${settingPrefix}ActionsEnable`; isTestingAlert = true; setBodyBorder(level); flashTitle(level); if (settings[actionsEnabledKey] === true) { triggerEmergencyActions(settingPrefix); showUserMessage("Test Alert", `Emergency actions triggered for ${settingPrefix}.`, "info"); } else { showUserMessage("Test Alert", `Visuals triggered for ${settingPrefix}. Actions disabled for this level.`, "info"); } setTimeout(() => { isTestingAlert = false; setBodyBorder(null); resetSolidTitle(); }, 7000); } catch(e){ console.error("[TCAT TEST] Error in testChainAlert:", e); showUserMessage("Test Error", `Failed test: ${level}`, "error"); isTestingAlert = false; } }
    function testRetaliationAlert() {
        try {
            if (!settings.enableWebhook || !settings.webhookUrl) {
                showUserMessage("Webhook Test", "Master Webhook is disabled or Global URL is not set in settings.", "info");
                return;
            }
            if (!settings.enableRetaliationWebhook) {
                showUserMessage("Webhook Test", "Retaliation Alert Webhooks are currently disabled in settings. Test will use generic format if enabled, or nothing.", "info");
                 // Optionally send generic test if master is on but retaliation is off
                if (settings.enableWebhook) {
                     const testEventGeneric = { eventTimestamp: Math.floor(Date.now() / 1000), attackerId: '12345', attackerName: 'TestAttackerGeneric', action: 'Interacted (Generic Test)', description: 'This is a generic test message.' };
                     sendWebhookNotification(testEventGeneric, 'generic');
                }
                return;
            }
            const testEvent = {
                eventTimestamp: Math.floor(Date.now() / 1000),
                attackerId: '12345',
                attackerName: 'EmbedTester',
                action: 'Hospitalized (Embed Test)',
                description: 'You were hospitalized in a test retaliation!',
            };
            sendWebhookNotification(testEvent, 'retaliation');
        } catch(e) {
            console.error("[TCAT TEST] Error in testRetaliationAlert:", e);
            showUserMessage("Test Error", "Failed to initiate webhook test.", "error");
        }
    }

    // --- Menu Structure and Control ---
    function buildMenuHTML() { return ` <div class="${MENU_CSS_PREFIX}menu-header"> <div class="${MENU_CSS_PREFIX}title-wrapper"> <h2 class="${MENU_CSS_PREFIX}title"> <img src="${NEW_LOGO_URL}" alt="TCAT Logo" class="${MENU_CSS_PREFIX}title-icon"> TCAT Settings </h2> </div> <button class="${MENU_CSS_PREFIX}button-close" title="Close">×</button> </div> <div class="${MENU_CSS_PREFIX}tab-container"> <button class="${MENU_CSS_PREFIX}tab-button active" data-tab-id="general">General</button> <button class="${MENU_CSS_PREFIX}tab-button" data-tab-id="alerts">Alerts</button> <button class="${MENU_CSS_PREFIX}tab-button" data-tab-id="chain-tool">Chain Tool</button> <button class="${MENU_CSS_PREFIX}tab-button" data-tab-id="webhook">Webhook</button> <button class="${MENU_CSS_PREFIX}tab-button" data-tab-id="api">API Settings</button> <button class="${MENU_CSS_PREFIX}tab-button" data-tab-id="about">About</button> </div> <div class="${MENU_CSS_PREFIX}content-container"> <div class="${MENU_CSS_PREFIX}tab-content active" id="${MENU_CSS_PREFIX}tab-general"></div> <div class="${MENU_CSS_PREFIX}tab-content" id="${MENU_CSS_PREFIX}tab-alerts"></div> <div class="${MENU_CSS_PREFIX}tab-content" id="${MENU_CSS_PREFIX}tab-chain-tool"></div> <div class="${MENU_CSS_PREFIX}tab-content" id="${MENU_CSS_PREFIX}tab-webhook"></div> <div class="${MENU_CSS_PREFIX}tab-content" id="${MENU_CSS_PREFIX}tab-api"></div> <div class="${MENU_CSS_PREFIX}tab-content" id="${MENU_CSS_PREFIX}tab-about"></div> </div> <div class="${MENU_CSS_PREFIX}menu-footer"> <div id="${STATUS_INDICATOR_ID}" class="${MENU_CSS_PREFIX}status-indicator">Status: Initializing...</div> <div id="${API_CALL_COUNT_DISPLAY_ID}" class="${MENU_CSS_PREFIX}api-call-count">API Calls: 0/100</div> <div class="${MENU_CSS_PREFIX}forum-link-container"> <a href="${FORUM_THREAD_URL}" target="_blank" rel="noopener noreferrer" class="${MENU_CSS_PREFIX}forum-link"> Like the script? Have ideas? Let me know! </a> </div> <span class="${MENU_CSS_PREFIX}version-info">v${SCRIPT_VERSION}</span> </div> `; }

    // --- API & Chain Tool Interaction Functions ---
    function handleVerifyFaction() { const factionIdInput = document.getElementById(CHAIN_TOOL_FACTION_ID_INPUT_ID); const rawInputValue = factionIdInput ? factionIdInput.value : 'INPUT_ELEMENT_NULL'; const factionId = factionIdInput ? String(factionIdInput.value).trim() : ''; console.log(`[TCAT] Verifying Faction ID: '${factionId}' (raw input: '${rawInputValue}')`); if (!settings.enableApiIntegration || !settings.apiKey) { showUserMessage("API Error", "API Integration is disabled or API Key is not set. Please configure in API Settings.", "error"); return; } if (apiConnectionStatus === 'WAITING' && !apiRequestOngoing) { /* allow */ } else if (apiConnectionStatus === 'WAITING') { showUserMessage("API Busy", "API connection test is in progress. Please wait.", "info"); return; } const verifiedDisplay = document.getElementById(CHAIN_TOOL_VERIFIED_FACTION_DISPLAY_ID); const importButton = document.getElementById(`${MENU_CSS_PREFIX}import-faction-btn`); const verifyButton = document.getElementById(`${MENU_CSS_PREFIX}verify-faction-btn`); if (verifiedDisplay) verifiedDisplay.textContent = 'Verifying...'; chainToolState.verifiedFactionId = null; chainToolState.verifiedFactionName = null; if(importButton) importButton.disabled = true; if(verifyButton) verifyButton.disabled = true; if (!factionId || !isValidFactionID(factionId)) { if (verifiedDisplay) verifiedDisplay.textContent = `Invalid Faction ID: '${factionId}'. Must be 1-5 digits.`; showUserMessage("Verification Error", `Input '${factionId}' is not a valid Faction ID (must be 1-5 digits).`, "warning"); saveChainToolState(); updateChainToolUI(); if(verifyButton) verifyButton.disabled = false; return; } const apiUrl = `https://api.torn.com/faction/${factionId}?key=${settings.apiKey}&selections=basic&comment=TCATFactionVerify`; console.log(`[TCAT] Faction Verify API URL: ${apiUrl.replace(settings.apiKey, "USER_API_KEY")}`); makeManagedApiRequest({ method: "GET", url: apiUrl, timeout: 10000, onload: function(response) { console.log(`[TCAT] Faction Verify Response Status: ${response.status}`); try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.error) { if (verifiedDisplay) verifiedDisplay.textContent = `API Error: ${data.error.error} (Code: ${data.error.code})`; showUserMessage("API Error", `Torn API Error: ${data.error.error} (Code: ${data.error.code})`, "error"); chainToolState.verifiedFactionId = null; chainToolState.verifiedFactionName = null; if ([2,3,4,5,6,7,8,10,13].includes(data.error.code)) { testAndSetApiStatus(true); } } else if (data.ID && data.name && data.tag) { chainToolState.verifiedFactionId = data.ID.toString(); chainToolState.verifiedFactionName = `${data.name} [${data.tag}]`; if (verifiedDisplay) verifiedDisplay.textContent = `Verified: ${chainToolState.verifiedFactionName} (ID: ${data.ID})`; if (importButton) importButton.disabled = tcatIsApiPaused; showUserMessage("Verification Success", `Faction "${chainToolState.verifiedFactionName}" verified.`, "success"); } else { if (verifiedDisplay) verifiedDisplay.textContent = 'Faction not found or API returned unexpected data.'; showUserMessage("Verification Error", "Faction not found or API returned unexpected data. Check console for response.", "error"); console.log("[TCAT] Unexpected faction data from API:", data); } } else { if (verifiedDisplay) verifiedDisplay.textContent = `Error: ${response.statusText} (HTTP ${response.status})`; showUserMessage("API Request Error", `Failed to verify faction. Status: ${response.status} ${response.statusText}`, "error"); } } catch (e) { if (verifiedDisplay) verifiedDisplay.textContent = 'Error processing response.'; console.error("[TCAT Verify Faction] Error processing API response:", e, response.responseText); showUserMessage("Verification Error", "Error processing data from Torn API. Check console.", "error"); } }, onerror: function(details) { if (verifiedDisplay) verifiedDisplay.textContent = 'Network error during verification.'; showUserMessage("API Network Error", "Failed to connect to Torn API for verification.", "error"); console.error("[TCAT Verify Faction] Network/Request error:", details); }, ontimeout: function() { if (verifiedDisplay) verifiedDisplay.textContent = 'Verification timed out.'; showUserMessage("API Timeout", "Torn API request for verification timed out.", "error"); }, onFinally: () => { saveChainToolState(); updateChainToolUI(); }, onRateLimited: () => { if (verifiedDisplay) verifiedDisplay.textContent = 'Verification paused by API rate limiter.'; } }, "Faction Verification"); }

    // --- OPTIMIZED handleImportFaction FUNCTION ---
    async function handleImportFaction() {
        if (!settings.enableApiIntegration || !settings.apiKey) { showUserMessage("API Error", "API Integration is disabled or API Key is not set.", "error"); return; }
        if (apiConnectionStatus === 'WAITING') { showUserMessage("API Busy", "API connection test in progress. Please wait.", "info"); return; }
        if (isSortingInProgress) { showUserMessage("Import Blocked", "Another list operation is in progress.", "info"); return; }

        const factionIdInput = document.getElementById(CHAIN_TOOL_FACTION_ID_INPUT_ID);
        const currentInputFactionId = factionIdInput ? factionIdInput.value.trim() : '';
        if (!chainToolState.verifiedFactionId || currentInputFactionId !== chainToolState.verifiedFactionId) { showUserMessage("Import Error", "Please verify Faction ID first.", "warning"); return; }

        const factionIdToImport = chainToolState.verifiedFactionId;
        const verifiedFactionDisplayName = chainToolState.verifiedFactionName || `Faction ${factionIdToImport}`;
        const importModeSelect = document.getElementById(CHAIN_TOOL_IMPORT_MODE_SELECT_ID);
        const importCountInput = document.getElementById(CHAIN_TOOL_IMPORT_COUNT_INPUT_ID);
        const importMode = importModeSelect ? importModeSelect.value : 'all';
        let importCount = importCountInput ? parseInt(importCountInput.value, 10) : 0;
        if (isNaN(importCount) || importCount <= 0) importCount = (importMode === 'topX' || importMode === 'bottomX') ? 10 : 0;

        const importButton = document.getElementById(`${MENU_CSS_PREFIX}import-faction-btn`);
        const verifyButton = document.getElementById(`${MENU_CSS_PREFIX}verify-faction-btn`);
        if (importButton) importButton.disabled = true;
        if (verifyButton) verifyButton.disabled = true;

        isSortingInProgress = true; updateChainToolUI();
        showUserMessage("API Request", `Workspaceing members for ${verifiedFactionDisplayName}...`, "info");
        const apiUrl = `https://api.torn.com/v2/faction/${factionIdToImport}?key=${settings.apiKey}&selections=members&comment=TCATFactionImportV2`;
        console.log(`[TCAT] Faction Import V2 API URL: ${apiUrl.replace(settings.apiKey, "KEY")}`);

        makeManagedApiRequest({
            method: "GET", url: apiUrl, timeout: 20000, isCritical: true,
            onFinally: () => {
                isSortingInProgress = false;
                if (importButton && chainToolState.verifiedFactionId === currentInputFactionId && chainToolState.verifiedFactionId) { importButton.disabled = tcatIsApiPaused; }
                if (verifyButton) { verifyButton.disabled = tcatIsApiPaused; }
                updateChainToolUI();
            },
            onload: function(response) {
                try {
                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        if (data.error) { showUserMessage("API Error", `Import Error: ${data.error.error} (Code: ${data.error.code})`, "error"); if ([2, 3, 4, 5, 6, 7, 8, 10, 13].includes(data.error.code)) testAndSetApiStatus(true); return; }
                        const memberArray = data.members;
                        if (!Array.isArray(memberArray)) { showUserMessage("Import Error", `No member array for "${verifiedFactionDisplayName}".`, "error"); console.log("[TCAT] Bad member data:", data); return; }

                        chainToolState.enrichedMemberDataCache = {};
                        let processedMembers = memberArray.map(member => {
                            if (member && typeof member.id !== 'undefined') {
                                const memberDetails = { id: member.id.toString(), name: member.name || `User ${member.id}`, level: parseInt(member.level, 10) || 0, status: (member.status && member.status.description) ? member.status.description : "Unknown" };
                                chainToolState.enrichedMemberDataCache[memberDetails.id] = memberDetails; return memberDetails;
                            } return null;
                        }).filter(member => member !== null);

                        const totalFetchedMembers = processedMembers.length;
                        if (totalFetchedMembers === 0) { showUserMessage("Import Info", `No valid members for "${verifiedFactionDisplayName}".`, "info"); }
                        if (importMode === 'all' && totalFetchedMembers > DEFAULT_SETTINGS.factionImportConfirmSize) { if (!confirm(`Faction "${verifiedFactionDisplayName}" has ${totalFetchedMembers} members (confirm size: ${DEFAULT_SETTINGS.factionImportConfirmSize}). Import all?`)) { showUserMessage("Import Cancelled", "Import cancelled by user.", "info"); return; } }

                        let finalMemberIdsToImport;
                        const sortStatusDisplay = document.getElementById(`${MENU_CSS_PREFIX}sort-status-display`);
                        if ((importMode === 'topX' || importMode === 'bottomX') && importCount > 0 && totalFetchedMembers > 0) {
                            if (sortStatusDisplay) sortStatusDisplay.textContent = `Sorting ${totalFetchedMembers} members...`;
                            if (importMode === 'topX') { processedMembers.sort((a, b) => (b.level - a.level) || (a.name.toLowerCase().localeCompare(b.name.toLowerCase())) || (Number(a.id) - Number(b.id))); }
                            else { processedMembers.sort((a, b) => (a.level - b.level) || (a.name.toLowerCase().localeCompare(b.name.toLowerCase())) || (Number(a.id) - Number(b.id))); }
                            finalMemberIdsToImport = processedMembers.slice(0, importCount).map(m => m.id);
                            if (sortStatusDisplay) sortStatusDisplay.textContent = `Selected ${finalMemberIdsToImport.length} members.`;
                        } else { finalMemberIdsToImport = processedMembers.map(m => m.id); }

                        const newListName = `${verifiedFactionDisplayName.replace(/ \[.*?\]$/, "")} [${factionIdToImport}] Roster${(importMode !== 'all' && importCount > 0 && finalMemberIdsToImport.length > 0) ? ` (${importMode === 'topX' ? 'Top' : 'Bottom'} ${finalMemberIdsToImport.length} by Lvl)` : ''}`;
                        if (chainLists[newListName] && (importMode === 'all' || (importMode !== 'all' && finalMemberIdsToImport.length > 0 ))) { if (!confirm(`List "${newListName}" exists. Overwrite?`)) { showUserMessage("Import Cancelled", "Overwrite cancelled.", "info"); return; } }

                        chainLists[newListName] = finalMemberIdsToImport.sort((a, b) => Number(a) - Number(b));
                        saveChainLists(); chainToolState.currentListName = newListName; chainToolState.currentListIDs = [...chainLists[newListName]]; chainToolState.currentIndex = -1;
                        saveChainToolState(); showUserMessage("Import Successful", `Imported ${finalMemberIdsToImport.length} to: "${newListName}".`, "success");
                    } else { showUserMessage("API Error", `Import failed. Status: ${response.status} ${response.statusText}`, "error"); }
                } catch (e) { console.error("[TCAT Import V2] Error processing:", e, response.responseText); showUserMessage("Import Error", "Processing error. Check console.", "error"); }
            },
            onerror: (d) => { showUserMessage("API Network Error", "Import connection failed.", "error"); console.error("[TCAT Import V2] Network error:", d); },
            ontimeout: () => { showUserMessage("API Timeout", "Import request timed out.", "error"); },
            onRateLimited: () => { showUserMessage("Import Halted", "Import paused by rate limits.", "warning"); }
        }, "Faction Member Import (V2)");
    }

    // --- Chain Tool UI Population & Logic ---
    function populateChainToolTab(tabElement) {
        tabElement.innerHTML = '';
        const mainContainer = document.createElement('div');
        mainContainer.style.display = 'flex'; mainContainer.style.flexDirection = 'column'; mainContainer.style.gap = '10px';

        const listMgmtSection = createSection('Chain Lists', {description: 'Load, save, or delete target lists.'});
        const listSelectContainer = document.createElement('div'); listSelectContainer.className = `${MENU_CSS_PREFIX}setting-container`; listSelectContainer.style.alignItems = 'center';
        const listSelectLabel = document.createElement('label'); listSelectLabel.textContent = 'Select List:'; listSelectLabel.htmlFor = CHAIN_TOOL_LIST_SELECT_ID; listSelectLabel.style.minWidth = '75px';
        const listSelect = document.createElement('select'); listSelect.id = CHAIN_TOOL_LIST_SELECT_ID; listSelect.className = `${MENU_CSS_PREFIX}select`; listSelect.style.flexGrow = '1'; listSelect.style.marginRight = '10px';
        listSelectContainer.appendChild(listSelectLabel); listSelectContainer.appendChild(listSelect);
        listMgmtSection.appendChild(listSelectContainer);
        const listNameContainer = document.createElement('div'); listNameContainer.className = `${MENU_CSS_PREFIX}setting-container`;
        createTextInput(CHAIN_TOOL_LIST_NAME_INPUT_ID, 'List Name', listNameContainer, { isSetting: false, width: '100%', placeholder: 'Enter name for new/saved list', value: chainToolState.currentListName || '', containerClass: `${MENU_CSS_PREFIX}full-width-container` });
        listNameContainer.querySelector('label').style.minWidth = '75px';
        listMgmtSection.appendChild(listNameContainer);
        const listButtonsContainer = document.createElement('div'); listButtonsContainer.style.display = 'flex'; listButtonsContainer.style.flexWrap = 'wrap'; listButtonsContainer.style.gap = '5px'; listButtonsContainer.style.marginTop = '10px';
        createButton('Load', listButtonsContainer, handleLoadList);
        createButton('Save', listButtonsContainer, function(){handleSaveList(false)});
        createButton('Save As New', listButtonsContainer, handleSaveAsNewList);
        createButton('Delete', listButtonsContainer, handleDeleteList, {extraClass: `${MENU_CSS_PREFIX}button-danger`});
        listMgmtSection.appendChild(listButtonsContainer);
        mainContainer.appendChild(listMgmtSection);

        const factionImportSection = createSection('Import Faction Roster', {description: 'Create a new target list from a faction\'s member roster using the Torn API.'});
        const factionIdInputRow = document.createElement('div'); factionIdInputRow.style.display = 'flex'; factionIdInputRow.style.alignItems = 'center'; factionIdInputRow.style.gap = '10px'; factionIdInputRow.style.marginBottom = '5px';
        createTextInput(CHAIN_TOOL_FACTION_ID_INPUT_ID, 'Faction ID', factionIdInputRow, { isSetting: false, type: 'text', inputMode: 'numeric', pattern: '[0-9]*', placeholder: 'Enter Faction ID', width: '100px', onblur: (event) => { if(settings.enableApiIntegration && settings.apiKey && event.target.value.trim()){ handleVerifyFaction(); } } }).style.flexGrow = '0';
        factionIdInputRow.querySelector(`label[for="${CHAIN_TOOL_FACTION_ID_INPUT_ID}"]`).style.marginRight = '2px';
        createButton('Verify ID', factionIdInputRow, handleVerifyFaction, {id: `${MENU_CSS_PREFIX}verify-faction-btn`, style: {padding: '5px 8px', marginLeft: '0px'}});
        factionImportSection.appendChild(factionIdInputRow);
        const verifiedDisplay = document.createElement('div'); verifiedDisplay.id = CHAIN_TOOL_VERIFIED_FACTION_DISPLAY_ID; verifiedDisplay.className = `${MENU_CSS_PREFIX}tip-text`; verifiedDisplay.style.minHeight = '1.2em'; verifiedDisplay.style.marginTop = '0px'; verifiedDisplay.style.fontStyle = 'italic';
        factionImportSection.appendChild(verifiedDisplay);
        const importOptionsRow = document.createElement('div'); importOptionsRow.style.display = 'flex'; importOptionsRow.style.alignItems = 'center'; importOptionsRow.style.gap = '10px'; importOptionsRow.style.marginTop = '5px';
        createDropdownInput(CHAIN_TOOL_IMPORT_MODE_SELECT_ID, 'Import', importOptionsRow, [ {value: 'all', text: 'All Members'}, {value: 'topX', text: 'Top X by Level'}, {value: 'bottomX', text: 'Bottom X by Level'} ], { isSetting: false, width: '150px', onchange: updateChainToolUI } );
        const importCountContainer = createTextInput(CHAIN_TOOL_IMPORT_COUNT_INPUT_ID, 'Count (X)', importOptionsRow, { isSetting: false, type: 'number', min: 1, defaultValue: 10, width: '60px', containerClass: `${MENU_CSS_PREFIX}compact-input-row`} );
        importCountContainer.style.display = 'none'; // Initially hidden by updateChainToolUI if mode is 'all'
        factionImportSection.appendChild(importOptionsRow);
        // Removed factionImportConfirmSize input
        const importButtonRow = document.createElement('div'); importButtonRow.style.marginTop = '5px';
        createButton('Import Members', importButtonRow, handleImportFaction, {id: `${MENU_CSS_PREFIX}import-faction-btn`});
        factionImportSection.appendChild(importButtonRow);
        mainContainer.appendChild(factionImportSection);

        const idMgmtSection = createSection('Manage & Sort IDs in Current List', {description: 'Add, remove, copy, or sort IDs in the currently selected list.'});
        const idInputContainer = document.createElement('div'); idInputContainer.className = `${MENU_CSS_PREFIX}setting-container`; idInputContainer.style.justifyContent = 'space-between';
        createTextInput(CHAIN_TOOL_ID_INPUT_ID, 'Player ID', idInputContainer, { isSetting: false, type: 'text', inputMode: 'numeric', pattern: '[0-9]*', placeholder: 'Enter ID', width: '120px' });
        createButton('Add ID', idInputContainer, handleAddId);
        idMgmtSection.appendChild(idInputContainer);
        const sortButtonsContainer = document.createElement('div'); sortButtonsContainer.style.display = 'flex'; sortButtonsContainer.style.gap = '10px'; sortButtonsContainer.style.flexWrap = 'wrap'; sortButtonsContainer.style.marginTop = '10px';
        createButton('Sort by Lowest Level', sortButtonsContainer, () => handleSortList('level'), {id: `${MENU_CSS_PREFIX}sort-level-btn`});
        createButton('Sort Alphabetically (Name)', sortButtonsContainer, () => handleSortList('name'), {id: `${MENU_CSS_PREFIX}sort-name-btn`});
        createButton('Sort by ID (Default)', sortButtonsContainer, () => handleSortList('id'), {id: `${MENU_CSS_PREFIX}sort-id-btn`});
        idMgmtSection.appendChild(sortButtonsContainer);
        const sortStatusDisplay = document.createElement('div'); sortStatusDisplay.id = `${MENU_CSS_PREFIX}sort-status-display`; sortStatusDisplay.className = `${MENU_CSS_PREFIX}tip-text`; sortStatusDisplay.style.minHeight = '1.2em'; sortStatusDisplay.style.marginTop = '5px';
        idMgmtSection.appendChild(sortStatusDisplay);
        const idListLabel = document.createElement('label'); idListLabel.textContent = 'Members in Current List:'; idListLabel.style.display = 'block'; idListLabel.style.marginTop = '10px'; idListLabel.style.marginBottom = '5px';
        idMgmtSection.appendChild(idListLabel);
        const idListDisplay = document.createElement('select'); idListDisplay.id = CHAIN_TOOL_ID_LIST_DISPLAY_ID; idListDisplay.multiple = true; idListDisplay.className = `${MENU_CSS_PREFIX}select`; idListDisplay.style.width = '100%'; idListDisplay.style.minHeight = '100px'; idListDisplay.style.height = 'auto';
        idMgmtSection.appendChild(idListDisplay);
        const idListActionButtons = document.createElement('div'); idListActionButtons.style.marginTop = '5px'; idListActionButtons.style.display = 'flex'; idListActionButtons.style.gap = '10px'; idListActionButtons.style.flexWrap = 'wrap';
        createButton('Remove Selected ID(s)', idListActionButtons, handleRemoveId);
        createButton('Copy List Details', idListActionButtons, handleCopyListIds); // Updated button text
        createButton('Refresh Selected', idListActionButtons, () => handleRefreshMemberData(true), { id: `${MENU_CSS_PREFIX}refresh-selected-btn`, tooltip: 'Refresh data for selected members.'});
        createButton('Refresh All', idListActionButtons, () => handleRefreshMemberData(false), { id: `${MENU_CSS_PREFIX}refresh-all-btn`, tooltip: 'Refresh data for all members in the list.'});
        idMgmtSection.appendChild(idListActionButtons);
        mainContainer.appendChild(idMgmtSection);

        const navSection = createSection('Navigation', {description: 'Cycle through targets in the loaded list.'});
        const targetDisplayContainer = document.createElement('div'); targetDisplayContainer.style.marginBottom = '10px'; targetDisplayContainer.style.padding = '10px'; targetDisplayContainer.style.border = `1px solid var(--tcat-border-color)`; targetDisplayContainer.style.backgroundColor = `var(--tcat-input-bg-color)`; targetDisplayContainer.style.textAlign = 'center'; targetDisplayContainer.style.minHeight = '2.5em'; targetDisplayContainer.style.borderRadius = '3px';
        const targetLabel = document.createElement('label'); targetLabel.textContent = 'Current Target:'; targetLabel.style.fontWeight = 'bold'; targetLabel.style.color = `var(--tcat-accent-color)`; targetLabel.style.marginBottom = '5px';
        const currentTargetDisplay = document.createElement('div'); currentTargetDisplay.id = CHAIN_TOOL_CURRENT_TARGET_DISPLAY_ID; currentTargetDisplay.style.fontSize = '1.1em';
        targetDisplayContainer.appendChild(targetLabel); targetDisplayContainer.appendChild(currentTargetDisplay);
        navSection.appendChild(targetDisplayContainer);
        const navButtonsContainer = document.createElement('div'); navButtonsContainer.style.display = 'flex'; navButtonsContainer.style.justifyContent = 'space-around'; navButtonsContainer.style.gap = '10px';
        createButton('<< Previous Target', navButtonsContainer, handlePreviousTarget, {style: {flex: 1}});
        createButton('Next Target >>', navButtonsContainer, handleNextTarget, {style: {flex: 1}});
        navSection.appendChild(navButtonsContainer);
        mainContainer.appendChild(navSection);
        tabElement.appendChild(mainContainer);
        updateChainToolUI();
    }
    function populateListSelectDropdown(selectElement) { selectElement.innerHTML = ''; const createOption = (value, text, disabled = false) => { const option = document.createElement('option'); option.value = value; option.textContent = text; option.disabled = disabled; if (chainToolState.currentListName === value) { option.selected = true; } selectElement.appendChild(option); }; createOption("__NEW__", "--- Create New List ---"); createOption("", "--- Select Saved List ---", true); const listNames = Object.keys(chainLists).sort(); if (listNames.length > 0) { listNames.forEach(name => createOption(name, name)); } else { if (chainLists[DEFAULT_CHAIN_LIST_NAME]) { createOption(DEFAULT_CHAIN_LIST_NAME, DEFAULT_CHAIN_LIST_NAME); } else { createOption("", "No lists saved yet", true); } } if(chainToolState.currentListName && !listNames.includes(chainToolState.currentListName) && chainToolState.currentListName === DEFAULT_CHAIN_LIST_NAME && chainLists[DEFAULT_CHAIN_LIST_NAME]) { createOption(DEFAULT_CHAIN_LIST_NAME, DEFAULT_CHAIN_LIST_NAME); selectElement.value = DEFAULT_CHAIN_LIST_NAME; } else if (chainToolState.currentListName && listNames.includes(chainToolState.currentListName)) { selectElement.value = chainToolState.currentListName; } else if (listNames.length > 0) { selectElement.value = listNames[0]; } else { selectElement.value = "__NEW__"; } }
    function populateIdListDisplay(displayElement) { displayElement.innerHTML = ''; chainToolState.currentListIDs.forEach((id, index) => { const option = document.createElement('option'); option.value = id; const memberInfo = chainToolState.enrichedMemberDataCache[id]; if (memberInfo && memberInfo.name && typeof memberInfo.level !== 'undefined') { option.textContent = `${memberInfo.name} [${id}] (Lvl: ${memberInfo.level}) - Status: ${memberInfo.status || 'Unknown'}`; } else { option.textContent = id; } if (index === chainToolState.currentIndex) { option.style.backgroundColor = 'var(--tcat-accent-color)'; option.style.color = 'var(--tcat-bg-color)'; option.selected = true; } displayElement.appendChild(option); }); if (chainToolState.currentIndex !== -1 && displayElement.selectedIndex !== chainToolState.currentIndex && chainToolState.currentIndex < displayElement.options.length) { displayElement.selectedIndex = chainToolState.currentIndex; } if (displayElement.selectedIndex !== -1 && displayElement.options[displayElement.selectedIndex]) { displayElement.options[displayElement.selectedIndex].scrollIntoView({ block: 'nearest' }); } }
    function getCurrentTargetDisplayValue() { if (chainToolState.currentListName && chainToolState.currentIndex >= 0 && chainToolState.currentIndex < chainToolState.currentListIDs.length) { const targetId = chainToolState.currentListIDs[chainToolState.currentIndex]; const memberInfo = chainToolState.enrichedMemberDataCache[targetId]; if (memberInfo && memberInfo.name && typeof memberInfo.level !== 'undefined') { return `${memberInfo.name} [${targetId}] (Lvl: ${memberInfo.level}) - Status: ${memberInfo.status || 'Unknown'}`; } return targetId; } else if (chainToolState.currentListName && chainToolState.currentListIDs.length > 0) { return "(Press Next/Prev)"; } else if (chainToolState.currentListName) { return "(List Empty)"; } else { return "(No List Loaded)"; } }
    function updateChainToolSortButtonsState() { const sortLevelBtn = document.getElementById(`${MENU_CSS_PREFIX}sort-level-btn`); const sortNameBtn = document.getElementById(`${MENU_CSS_PREFIX}sort-name-btn`); const sortIdBtn = document.getElementById(`${MENU_CSS_PREFIX}sort-id-btn`); const sortStatusDisplay = document.getElementById(`${MENU_CSS_PREFIX}sort-status-display`); const apiIsFullyConnected = settings.enableApiIntegration && settings.apiKey && apiConnectionStatus === 'CONNECTED'; const listLoadedWithMembers = chainToolState.currentListName && chainToolState.currentListIDs && chainToolState.currentListIDs.length > 0; const canSortWithApiIfNeeded = listLoadedWithMembers && !isSortingInProgress && !tcatIsApiPaused && apiIsFullyConnected; const canSortById = listLoadedWithMembers && !isSortingInProgress; if (sortLevelBtn) sortLevelBtn.disabled = !canSortWithApiIfNeeded; if (sortNameBtn) sortNameBtn.disabled = !canSortWithApiIfNeeded; if (sortIdBtn) sortIdBtn.disabled = !canSortById; if (sortStatusDisplay) { if (isSortingInProgress) { /* message set by caller */ } else if (!listLoadedWithMembers) { sortStatusDisplay.textContent = 'Load a list with members to enable sorting.'; } else if (!apiIsFullyConnected && listLoadedWithMembers) { sortStatusDisplay.textContent = 'API connection required for initial Level/Name enrichment or if data is missing. Check API Settings.'; } else if (tcatIsApiPaused && listLoadedWithMembers) { sortStatusDisplay.textContent = 'API calls paused; Level/Name sorting may be unavailable or use cached data.'; } else { sortStatusDisplay.textContent = ''; } } }
    function updateChainToolUI() { const listSelect = document.getElementById(CHAIN_TOOL_LIST_SELECT_ID); const listNameInput = document.getElementById(CHAIN_TOOL_LIST_NAME_INPUT_ID); const idListDisplay = document.getElementById(CHAIN_TOOL_ID_LIST_DISPLAY_ID); const currentTargetDisplay = document.getElementById(CHAIN_TOOL_CURRENT_TARGET_DISPLAY_ID); const factionImportBtn = document.getElementById(`${MENU_CSS_PREFIX}import-faction-btn`); const verifyFactionBtn = document.getElementById(`${MENU_CSS_PREFIX}verify-faction-btn`); const verifiedDisplay = document.getElementById(CHAIN_TOOL_VERIFIED_FACTION_DISPLAY_ID); const importModeSelect = document.getElementById(CHAIN_TOOL_IMPORT_MODE_SELECT_ID); const importCountInputContainer = document.getElementById(CHAIN_TOOL_IMPORT_COUNT_INPUT_ID)?.parentElement; const refreshSelectedBtn = document.getElementById(`${MENU_CSS_PREFIX}refresh-selected-btn`); const refreshAllBtn = document.getElementById(`${MENU_CSS_PREFIX}refresh-all-btn`); if (listSelect) populateListSelectDropdown(listSelect); if (listNameInput) listNameInput.value = chainToolState.currentListName || ''; if (idListDisplay) populateIdListDisplay(idListDisplay); if (currentTargetDisplay) currentTargetDisplay.textContent = getCurrentTargetDisplayValue(); const factionIdInputElem = document.getElementById(CHAIN_TOOL_FACTION_ID_INPUT_ID); const currentFactionIdInputValue = factionIdInputElem ? factionIdInputElem.value.trim() : ''; if (verifiedDisplay) { if (chainToolState.verifiedFactionId && chainToolState.verifiedFactionName && chainToolState.verifiedFactionId === currentFactionIdInputValue) { verifiedDisplay.textContent = `Verified: ${chainToolState.verifiedFactionName} (ID: ${chainToolState.verifiedFactionId})`; verifiedDisplay.style.color = 'var(--tcat-accent-color)'; } else if (currentFactionIdInputValue && chainToolState.verifiedFactionId && chainToolState.verifiedFactionId !== currentFactionIdInputValue) { verifiedDisplay.textContent = `Previously verified: ${chainToolState.verifiedFactionName}. Input ID changed. Click "Verify ID".`; verifiedDisplay.style.color = 'var(--tcat-text-light-color)'; } else if (currentFactionIdInputValue) { verifiedDisplay.textContent = 'Not verified. Click "Verify ID".'; verifiedDisplay.style.color = 'var(--tcat-text-light-color)'; } else { verifiedDisplay.textContent = 'Enter a Faction ID and click "Verify ID".'; verifiedDisplay.style.color = 'var(--tcat-text-light-color)'; } } const apiIsReadyForTool = settings.enableApiIntegration && settings.apiKey && (apiConnectionStatus === 'CONNECTED'); if (verifyFactionBtn) verifyFactionBtn.disabled = !apiIsReadyForTool || isSortingInProgress || tcatIsApiPaused; if (factionImportBtn) factionImportBtn.disabled = !apiIsReadyForTool || !chainToolState.verifiedFactionId || chainToolState.verifiedFactionId !== currentFactionIdInputValue || isSortingInProgress || tcatIsApiPaused; if (importModeSelect && importCountInputContainer) { importCountInputContainer.style.display = (importModeSelect.value === 'topX' || importModeSelect.value === 'bottomX') ? 'flex' : 'none'; } const factionImportSection = document.getElementById(CHAIN_TOOL_FACTION_ID_INPUT_ID)?.closest(`.${MENU_CSS_PREFIX}section`); if (factionImportSection) { let apiDisabledMsgElement = factionImportSection.querySelector(`p.${MENU_CSS_PREFIX}tip-text[style*="color: var(--tcat-danger-color)"]`); if (!settings.enableApiIntegration || !settings.apiKey || apiConnectionStatus === 'NO_KEY' || apiConnectionStatus === 'DISABLED' || apiConnectionStatus === 'FAILED') { if (!apiDisabledMsgElement) { apiDisabledMsgElement = document.createElement('p'); apiDisabledMsgElement.className = `${MENU_CSS_PREFIX}tip-text`; apiDisabledMsgElement.style.color = 'var(--tcat-danger-color)'; apiDisabledMsgElement.style.marginTop = '10px'; apiDisabledMsgElement.style.marginBottom = '5px'; factionImportSection.appendChild(apiDisabledMsgElement); } let msgText = '<i>API Integration is disabled or API Key is not set/valid. Go to "API Settings" tab to enable/configure.</i>'; if (apiConnectionStatus === 'FAILED') msgText = '<i>API connection failed. Please check your key and network, then try again.</i>'; else if (apiConnectionStatus === 'NO_KEY') msgText = '<i>API key is missing. Please add it in "API Settings".</i>'; else if (apiConnectionStatus === 'DISABLED') msgText = '<i>API Integration is disabled. Enable it in "API Settings".</i>'; apiDisabledMsgElement.innerHTML = msgText; apiDisabledMsgElement.style.display = 'block'; } else if (apiDisabledMsgElement) { apiDisabledMsgElement.style.display = 'none'; } } updateChainToolSortButtonsState(); if (refreshSelectedBtn) refreshSelectedBtn.disabled = !apiIsReadyForTool || isSortingInProgress || tcatIsApiPaused || !chainToolState.currentListIDs || chainToolState.currentListIDs.length === 0; if (refreshAllBtn) refreshAllBtn.disabled = !apiIsReadyForTool || isSortingInProgress || tcatIsApiPaused || !chainToolState.currentListIDs || chainToolState.currentListIDs.length === 0; }
    function handleLoadList() { const listSelect = document.getElementById(CHAIN_TOOL_LIST_SELECT_ID); const selectedName = listSelect?.value; if (selectedName && selectedName !== "__NEW__" && chainLists[selectedName]) { chainToolState.currentListName = selectedName; chainToolState.currentListIDs = [...chainLists[selectedName]]; chainToolState.currentIndex = -1; chainToolState.enrichedMemberDataCache = {}; saveChainToolState(); updateChainToolUI(); showUserMessage("List Loaded", `Loaded list: ${selectedName}`, "success"); } else if (selectedName === "__NEW__") { chainToolState.currentListName = ''; chainToolState.currentListIDs = []; chainToolState.currentIndex = -1;  chainToolState.enrichedMemberDataCache = {}; saveChainToolState(); updateChainToolUI(); const listNameInput = document.getElementById(CHAIN_TOOL_LIST_NAME_INPUT_ID); if(listNameInput) { listNameInput.value = ''; listNameInput.focus(); } showUserMessage("New List", "Enter a name and add IDs, or import a faction.", "info"); } else { showUserMessage("Load Error", "Please select a valid saved list to load.", "warning"); } }
    function handleSaveList(isSaveAs = false) { const listNameInput = document.getElementById(CHAIN_TOOL_LIST_NAME_INPUT_ID); let listName = listNameInput?.value.trim(); if (!listName) { showUserMessage("Save Error", "Please enter a name for the list.", "warning"); return; } const savingCurrentList = (!isSaveAs && listName === chainToolState.currentListName); const nameExists = chainLists[listName] !== undefined; if (!savingCurrentList && nameExists) { if (!confirm(`A list named "${listName}" already exists. Overwrite it?`)) { return; } } chainLists[listName] = [...chainToolState.currentListIDs]; chainToolState.currentListName = listName; saveChainLists(); saveChainToolState(); updateChainToolUI(); showUserMessage("List Saved", `List "${listName}" saved successfully.`, "success"); }
    function handleSaveAsNewList() { handleSaveList(true); }
    function handleDeleteList() { const listSelect = document.getElementById(CHAIN_TOOL_LIST_SELECT_ID); const selectedName = listSelect?.value; if (!selectedName || selectedName === "__NEW__" || !chainLists[selectedName]) { showUserMessage("Delete Error", "Please select a valid saved list to delete.", "warning"); return; } if (confirm(`Are you sure you want to permanently delete the list "${selectedName}"?`)) { delete chainLists[selectedName]; saveChainLists(); if (chainToolState.currentListName === selectedName) { resetChainToolState(); } else { populateListSelectDropdown(listSelect); } updateChainToolUI(); showUserMessage("List Deleted", `List "${selectedName}" deleted.`, "success"); } }
    function handleAddId() { const idInput = document.getElementById(CHAIN_TOOL_ID_INPUT_ID); const idValue = idInput?.value.trim(); if (!chainToolState.currentListName && chainToolState.currentListName !== "") { showUserMessage("Add ID Error", "Please load or provide a name for the current list first (even if new).", "warning"); return; } if (idValue && isValidPlayerID(idValue)) { if (!chainToolState.currentListIDs.includes(idValue)) { chainToolState.currentListIDs.push(idValue); chainToolState.currentListIDs.sort((a,b)=>Number(a)-Number(b)); delete chainToolState.enrichedMemberDataCache[idValue]; saveChainToolState(); updateChainToolUI(); if (idInput) idInput.value = ''; } else { showUserMessage("Add ID Info", "That ID is already in the list.", "info"); } } else { showUserMessage("Add ID Error", "Please enter a valid numeric player ID (1-8 digits).", "warning"); } }
    function handleRemoveId() { const idListDisplay = document.getElementById(CHAIN_TOOL_ID_LIST_DISPLAY_ID); if (!idListDisplay || idListDisplay.selectedOptions.length === 0) { showUserMessage("Remove ID Error", "Please select one or more IDs from the list to remove.", "warning"); return; } if (!chainToolState.currentListName && chainToolState.currentListName !== "") { showUserMessage("Remove ID Error", "No list loaded or current list has no name.", "warning"); return; } const idsToRemove = Array.from(idListDisplay.selectedOptions).map(opt => opt.value); const initialLength = chainToolState.currentListIDs.length; chainToolState.currentListIDs = chainToolState.currentListIDs.filter(id => !idsToRemove.includes(id)); idsToRemove.forEach(id => delete chainToolState.enrichedMemberDataCache[id]); if (chainToolState.currentListIDs.length < initialLength) { if (chainToolState.currentIndex >= chainToolState.currentListIDs.length) { chainToolState.currentIndex = chainToolState.currentListIDs.length - 1; } saveChainToolState(); } updateChainToolUI(); }
    function handleCopyListIds() {
        if (!chainToolState.currentListName || chainToolState.currentListIDs.length === 0) {
            showUserMessage("Copy Error", "No list loaded or list is empty.", "warning");
            return;
        }
        const listDetails = chainToolState.currentListIDs.map(id => {
            const memberInfo = chainToolState.enrichedMemberDataCache[id];
            if (memberInfo && memberInfo.name && typeof memberInfo.level !== 'undefined') {
                return `${memberInfo.name} [${id}] (Lvl: ${memberInfo.level}) - Status: ${memberInfo.status || 'Unknown'}`;
            }
            return `${id}`; // Fallback if no enriched data
        }).join('\n');

        navigator.clipboard.writeText(listDetails).then(() => {
            showUserMessage("Copied!", `${chainToolState.currentListIDs.length} member details copied to clipboard.`, "success");
        }).catch(err => {
            console.error("[TCAT] Failed to copy list details:", err);
            showUserMessage("Copy Failed", "Could not copy details to clipboard. Check console.", "error");
        });
    }
    async function handleRefreshMemberData(selectedOnly = false) { if (isSortingInProgress) { showUserMessage("Refresh Busy", "Another Chain Tool operation is in progress.", "info"); return; } if (!settings.enableApiIntegration || !settings.apiKey || apiConnectionStatus !== 'CONNECTED') { showUserMessage("API Error", "API is not enabled, key is not set, or not connected.", "error"); return; } if (!chainToolState.currentListName || chainToolState.currentListIDs.length === 0) { showUserMessage("Refresh Error", "No list loaded or list is empty.", "warning"); return; } const idListDisplay = document.getElementById(CHAIN_TOOL_ID_LIST_DISPLAY_ID); let idsToRefresh; if (selectedOnly) { if (!idListDisplay || idListDisplay.selectedOptions.length === 0) { showUserMessage("Refresh Error", "No members selected to refresh.", "info"); return; } idsToRefresh = Array.from(idListDisplay.selectedOptions).map(opt => opt.value); } else { idsToRefresh = [...chainToolState.currentListIDs]; } if (idsToRefresh.length === 0) { showUserMessage("Refresh Info", "No members to refresh.", "info"); return; } isSortingInProgress = true; updateChainToolUI(); const sortStatusDisplay = document.getElementById(`${MENU_CSS_PREFIX}sort-status-display`); let fetchedCount = 0; const totalToFetch = idsToRefresh.length; if (sortStatusDisplay) sortStatusDisplay.textContent = `Refreshing ${totalToFetch} member(s)... (0%)`; const groupSize = settings.factionImportGroupSize || 10; for (let i = 0; i < totalToFetch; i += groupSize) { if (tcatIsApiPaused) { showUserMessage("Refresh Paused", "API calls paused. Refresh operation may take longer or be incomplete.", "warning"); await new Promise(resolve => setTimeout(resolve, 5000)); i -= groupSize; continue; } const batchIds = idsToRefresh.slice(i, i + groupSize); const batchPromises = batchIds.map(id => new Promise(resolveDetail => { makeManagedApiRequest({ method: "GET", url: `https://api.torn.com/user/${id}?key=${settings.apiKey}&selections=profile,basic&comment=TCATRefreshMemberData`, timeout: 8000, onload: function(response) { fetchedCount++; if (sortStatusDisplay) sortStatusDisplay.textContent = `Refreshing... (${fetchedCount}/${totalToFetch})`; try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.error) { console.warn(`[TCAT Refresh] API Error for ID ${id}: ${data.error.error}`); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `Error (${id})`, level: 0, status: "Error" }; } else { chainToolState.enrichedMemberDataCache[id] = { name: data.name || `Unknown (${id})`, level: data.level || 0, status: (data.status && data.status.description) ? data.status.description : "Unknown" }; } } else { console.warn(`[TCAT Refresh] HTTP Error for ID ${id}: ${response.status}`); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `HTTP Err (${id})`, level: 0, status: "Error" }; } } catch (e) { console.error(`[TCAT Refresh] Parse Error for ID ${id}:`, e); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `Parse Err (${id})`, level: 0, status: "Error" }; } resolveDetail(); }, onerror: () => { fetchedCount++; resolveDetail(); console.warn(`[TCAT Refresh] Network Error for ID ${id}`); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `Net Err (${id})`, level: 0, status: "Error" }; }, ontimeout: () => { fetchedCount++; resolveDetail(); console.warn(`[TCAT Refresh] Timeout for ID ${id}`); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `Timeout (${id})`, level: 0, status: "Error" };}, onRateLimited: () => { fetchedCount++; resolveDetail(); console.warn(`[TCAT Refresh] Rate Limited, skipping ID ${id}`); chainToolState.enrichedMemberDataCache[id] = chainToolState.enrichedMemberDataCache[id] || { name: `Rate Limit Skip (${id})`, level: 0, status: "Skipped" };} }, "Refresh Member Data"); })); await Promise.all(batchPromises); } saveChainToolState(); isSortingInProgress = false; updateChainToolUI(); if (sortStatusDisplay) sortStatusDisplay.textContent = `Refreshed ${fetchedCount} member(s).`; showUserMessage("Refresh Complete", `Data for ${fetchedCount} member(s) refreshed.`, "success"); }
    function handleNextTarget() { if (!chainToolState.currentListName || chainToolState.currentListIDs.length === 0) { showUserMessage("Navigation Error", "No list loaded or list is empty.", "warning"); return; } chainToolState.currentIndex++; if (chainToolState.currentIndex >= chainToolState.currentListIDs.length) { chainToolState.currentIndex = 0; } saveChainToolState(); updateChainToolUI(); const targetId = chainToolState.currentListIDs[chainToolState.currentIndex]; const attackUrl = `https://www.torn.com/loader.php?sid=attack&user2ID=${targetId}`; try { GM_openInTab(attackUrl, { active: true, setParent: true }); } catch(e){ console.error("Error opening attack loader tab:", targetId, e); showUserMessage("Navigation Error", "Failed to open attack tab.", "error"); } }
    function handlePreviousTarget() { if (!chainToolState.currentListName || chainToolState.currentListIDs.length === 0) { showUserMessage("Navigation Error", "No list loaded or list is empty.", "warning"); return; } chainToolState.currentIndex--; if (chainToolState.currentIndex < 0) { chainToolState.currentIndex = chainToolState.currentListIDs.length - 1; } saveChainToolState(); updateChainToolUI(); const targetId = chainToolState.currentListIDs[chainToolState.currentIndex]; const attackUrl = `https://www.torn.com/loader.php?sid=attack&user2ID=${targetId}`; try { GM_openInTab(attackUrl, { active: true, setParent: true }); } catch(e){ console.error("Error opening attack loader tab:", targetId, e); showUserMessage("Navigation Error", "Failed to open attack tab.", "error"); } }
    async function handleSortList(sortType) { if (isSortingInProgress) { showUserMessage("Sort Busy", "Sorting is already in progress.", "info"); return; } if (!chainToolState.currentListName || chainToolState.currentListIDs.length === 0) { showUserMessage("Sort Error", "No list loaded or list is empty.", "warning"); return; } if (apiConnectionStatus === 'WAITING') { showUserMessage("API Busy", "API connection test is in progress. Please wait and try sorting again shortly.", "info"); return; } if (sortType === 'id') { chainToolState.currentListIDs.sort((a, b) => Number(a) - Number(b)); chainToolState.currentIndex = -1; saveChainToolState(); updateChainToolUI(); showUserMessage("Sort Complete", "List sorted by ID.", "success"); const sortStatusDisplay = document.getElementById(`${MENU_CSS_PREFIX}sort-status-display`); if(sortStatusDisplay) sortStatusDisplay.textContent = 'List sorted by ID. Consider saving.'; return; } if (!settings.enableApiIntegration || !settings.apiKey || apiConnectionStatus !== 'CONNECTED') { showUserMessage("API Error", "API is not enabled, key is not set, or not connected. Required for Level/Name sort.", "error"); return; } isSortingInProgress = true; updateChainToolUI(); const sortStatusDisplay = document.getElementById(`${MENU_CSS_PREFIX}sort-status-display`); if (sortStatusDisplay) sortStatusDisplay.textContent = `Workspaceing member data for sorting ${chainToolState.currentListIDs.length} members... (0%)`; const memberDataToFetch = chainToolState.currentListIDs.filter(id => !chainToolState.enrichedMemberDataCache[id] || !chainToolState.enrichedMemberDataCache[id].name || typeof chainToolState.enrichedMemberDataCache[id].level === 'undefined' || typeof chainToolState.enrichedMemberDataCache[id].status === 'undefined'); let fetchedCount = 0; const totalToFetch = memberDataToFetch.length; const groupSize = settings.factionImportGroupSize || 10; if (totalToFetch > 0) { for (let i = 0; i < totalToFetch; i += groupSize) { if (tcatIsApiPaused) { showUserMessage("Sort Paused", "API calls paused during detail fetching. Sorting may take longer or be based on incomplete data.", "warning"); await new Promise(resolve => setTimeout(resolve, 5000)); i -= groupSize; continue; } const batchIds = memberDataToFetch.slice(i, i + groupSize); const batchPromises = batchIds.map(id => new Promise(resolveDetail => { makeManagedApiRequest({ method: "GET", url: `https://api.torn.com/user/${id}?key=${settings.apiKey}&selections=profile,basic&comment=TCATListSortDetails`, timeout: 8000, onload: function(response) { fetchedCount++; if (sortStatusDisplay) sortStatusDisplay.textContent = `Workspaceing member data... (${fetchedCount}/${totalToFetch})`; try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.error) { console.warn(`[TCAT Sort Detail] API Error for ID ${id}: ${data.error.error}`); resolveDetail({ id, name: `Error (${id})`, level: Infinity, status: "Error", error: true }); } else { chainToolState.enrichedMemberDataCache[id] = { name: data.name || `Unknown (${id})`, level: data.level || 0, status: (data.status && data.status.description) ? data.status.description : "Unknown" }; resolveDetail({ id, ...chainToolState.enrichedMemberDataCache[id] }); } } else { resolveDetail({ id, name: `User ${id} (HTTP Err)`, level: Infinity, status: "Error", error: true }); } } catch (e) { resolveDetail({ id, name: `User ${id} (Parse Err)`, level: Infinity, status: "Error", error: true }); } }, onerror: () => { fetchedCount++; resolveDetail({ id, name: `User ${id} (Net Err)`, level: Infinity, status: "Error", error: true }); }, ontimeout: () => { fetchedCount++; resolveDetail({ id, name: `User ${id} (Timeout)`, level: Infinity, status: "Error", error: true }); }, onRateLimited: () => { fetchedCount++; resolveDetail({ id, name: `User ${id} (Rate Limit Skip)`, level: Infinity, status: "Error", error: true });} }, "List Sort Member Detail"); })); await Promise.all(batchPromises); } } if (sortStatusDisplay) sortStatusDisplay.textContent = "Sorting data..."; chainToolState.currentListIDs.sort((a, b) => { const dataA = chainToolState.enrichedMemberDataCache[a] || { id: a, name: `ID ${a}`, level: Infinity, status: "Error", error: true }; const dataB = chainToolState.enrichedMemberDataCache[b] || { id: b, name: `ID ${b}`, level: Infinity, status: "Error", error: true }; if (dataA.error && !dataB.error) return 1; if (!dataA.error && dataB.error) return -1; if (sortType === 'level') { const levelDiff = (dataA.level || 0) - (dataB.level || 0); if (levelDiff !== 0) return levelDiff; } else if (sortType === 'name') { const nameA = (dataA.name || '').toLowerCase(); const nameB = (dataB.name || '').toLowerCase(); if (nameA < nameB) return -1; if (nameA > nameB) return 1; } return Number(a) - Number(b); }); chainToolState.currentIndex = -1; saveChainToolState(); isSortingInProgress = false; updateChainToolUI(); showUserMessage("Sort Complete", `List sorted by ${sortType}. Consider saving.`, "success"); if (sortStatusDisplay) sortStatusDisplay.textContent = `Sort by ${sortType} complete. Consider saving the list.`; }

    // --- Menu Population ---
    function populateApiSettingsTab(tabElement) { tabElement.innerHTML = ''; const settingsSection = createSection('API Configuration'); tabElement.appendChild(settingsSection); const enableApiCheckboxContainer = createCheckbox('enableApiIntegration', 'Enable Torn API Integration', settingsSection, 'Allows TCAT to use the Torn API for features like Faction Roster Import and list sorting.'); const apiKeyContainer = document.createElement('div'); apiKeyContainer.id = `${MENU_CSS_PREFIX}api-key-container`; apiKeyContainer.style.marginTop = '10px'; apiKeyContainer.style.display = settings.enableApiIntegration ? 'flex' : 'none'; apiKeyContainer.style.flexDirection = 'column'; apiKeyContainer.style.gap = '5px'; createPasswordInput('apiKey', 'Your Torn API Key', apiKeyContainer, { placeholder: 'Enter your Torn API Key here', width: '100%' }); settingsSection.appendChild(apiKeyContainer); const apiTestButtonContainer = document.createElement('div'); apiTestButtonContainer.style.marginTop = '5px'; apiTestButtonContainer.style.display = settings.enableApiIntegration ? 'block' : 'none'; const testApiBtn = createButton('Test API Connection', apiTestButtonContainer, () => testAndSetApiStatus(true), { id: `${MENU_CSS_PREFIX}api-test-btn`, disabled: !settings.apiKey || apiRequestOngoing || isSortingInProgress || apiConnectionStatus === 'WAITING' || !settings.enableApiIntegration, tooltip: "Tests if the provided API key can connect to Torn." }); apiKeyContainer.appendChild(apiTestButtonContainer); const disclaimerSection = createSection('About Torn API Usage with TCAT'); const disclaimerText = ` <p style="font-size: 0.9em; line-height: 1.4;"> <strong>Purpose of Use:</strong><br> TCAT uses your Torn API key solely to enhance its features by fetching specific information directly from the Torn API <em>on your behalf</em>. This includes: <ul> <li>Fetching faction member lists (names and IDs) to populate target lists in the Chain Tool.</li> <li>Fetching individual user profile data (name, level) for sorting target lists.</li> <li>Verifying API key validity and autofilling your Torn Name/ID in Webhook settings.</li> </ul> TCAT will only use the API key for functionalities you explicitly try to use or for basic connection tests. <br><br> <strong>Data Storage & Access:</strong><br> <ul> <li>Your API key, if you provide it, is stored <strong>locally in your browser's userscript storage</strong> by your userscript manager (e.g., Tampermonkey).</li> <li>It is <strong>not transmitted to or stored on any external server or database by TCAT itself.</strong></li> <li>Only you and the TCAT script running in <em>your</em> browser have access to the key you've saved in the script's settings.</li> </ul> <strong>Data Sharing:</strong><br> <ul> <li>The TCAT script uses your API key to make direct requests from <em>your browser</em> to the official Torn API.</li> <li>The data retrieved (e.g., faction member lists, user profiles) is used locally by the script. This data is <strong>not shared with any third-party services or individuals by TCAT.</strong></li> </ul> <strong>API Key Type Required:</strong><br> TCAT requires a Torn API Key that allows access to 'Public User Data' (such as profile, basic info) and 'Public Faction Information' (such as faction name, tag, and member lists). A "Limited Access" key is typically sufficient. Check the <a href="https://www.torn.com/preferences.php#tab=api" target="_blank" rel="noopener noreferrer" style="color:var(--tcat-accent-color);">Torn API Preferences page</a>. <br><br> <strong>API Rate Limits:</strong><br> Be aware of Torn's API rate limits (typically 100 requests per minute per key). TCAT aims to use the API efficiently and includes a basic rate monitor and pause function. Sorting large lists or importing large factions will make multiple requests. </p>`; const disclaimerDiv = document.createElement('div'); disclaimerDiv.innerHTML = disclaimerText; disclaimerSection.appendChild(disclaimerDiv); tabElement.appendChild(disclaimerSection); if (enableApiCheckboxContainer) { const checkbox = enableApiCheckboxContainer.querySelector('input[type="checkbox"]'); checkbox.addEventListener('change', (event) => { const showApiFields = event.target.checked; apiKeyContainer.style.display = showApiFields ? 'flex' : 'none'; apiTestButtonContainer.style.display = showApiFields ? 'block' : 'none'; if (testApiBtn) { testApiBtn.disabled = !settings.apiKey || apiRequestOngoing || isSortingInProgress || apiConnectionStatus === 'WAITING' || !showApiFields; } if(!showApiFields) { apiConnectionStatus = 'DISABLED'; updateStatusIndicator(); } else if (settings.apiKey) { testAndSetApiStatus(true); } updateChainToolUI(); }); } }

    // --- Webhook Settings Tab Population ---
    function populateWebhookSettingsTab(tabElement) {
        tabElement.innerHTML = '';
        const mainWebhookContainer = document.createElement('div');
        mainWebhookContainer.className = `${MENU_CSS_PREFIX}webhook-main-container`;

        const masterEnableSection = createSection('Webhook Master Control');
        const masterEnableCheckboxContainer = createCheckbox('enableWebhook', 'Enable Discord Webhook Notifications (Master Switch)', masterEnableSection, 'Globally enables or disables all TCAT webhook functionalities.');
        mainWebhookContainer.appendChild(masterEnableSection);

        const webhookSettingsContent = document.createElement('div');
        webhookSettingsContent.id = `${MENU_CSS_PREFIX}webhook-settings-content`;
        webhookSettingsContent.className = `${MENU_CSS_PREFIX}webhook-main-content`;
        webhookSettingsContent.style.display = settings.enableWebhook ? 'block' : 'none';

        const globalConfigSection = createSection('Global Webhook Configuration', { description: 'Basic settings applicable to all webhook messages.'});
        webhookSettingsContent.appendChild(globalConfigSection);
        createWebhookUrlInput('webhookUrl', 'Global Webhook URL', globalConfigSection, { placeholder: 'Enter your primary Discord Webhook URL' });
        const userInfoContainer = document.createElement('div'); userInfoContainer.className = `${MENU_CSS_PREFIX}setting-container`; userInfoContainer.style.display = 'flex'; userInfoContainer.style.alignItems = 'baseline'; userInfoContainer.style.gap = '5px';
        const nameInputContainer = createTextInput('userTornName', 'Your Torn Info', userInfoContainer, { placeholder: 'Name (Autofills with API)', width: '180px', containerClass: `${MENU_CSS_PREFIX}inline-input-group` });
        const nameLabel = nameInputContainer.querySelector('label'); const nameInput = nameInputContainer.querySelector('input'); userInfoContainer.innerHTML = ''; if (nameLabel) userInfoContainer.appendChild(nameLabel); if (nameInput) userInfoContainer.appendChild(nameInput);
        const hashSpan = document.createElement('span'); hashSpan.textContent = '#'; hashSpan.style.margin = '0 3px'; userInfoContainer.appendChild(hashSpan);
        const idInput = document.createElement('input'); idInput.type = 'text'; idInput.inputMode = 'numeric'; idInput.pattern = '[0-9]*'; idInput.id = `${MENU_CSS_PREFIX}userTornId`; idInput.value = settings.userTornId || ''; idInput.placeholder = 'ID (Autofills)'; idInput.className = `${MENU_CSS_PREFIX}input`; idInput.style.width = '100px'; idInput.onchange = (event) => { saveSetting('userTornId', event.target.value.trim()); }; userInfoContainer.appendChild(idInput);
        globalConfigSection.appendChild(userInfoContainer);
        createCheckbox('webhookUseUserInfo', "Use 'Your Torn Info' in Placeholders", globalConfigSection, "If checked, placeholders like {yourName} will use the info above.");
        // Removed TCAT Logo URL input - NEW_LOGO_URL will be used directly.

        const retaliationSection = createSection('Retaliation Alerts', { description: 'Configure webhooks specifically for when you are attacked.' });
        retaliationSection.classList.add(`${MENU_CSS_PREFIX}webhook-subsection`);
        webhookSettingsContent.appendChild(retaliationSection);
        const retContainer = document.createElement('div');
        const enableRetaliationBtn = createStyledEnableDisableButton('enableRetaliationWebhook', 'Retaliation Alerts', retContainer, settings.enableRetaliationWebhook,
            (newState) => { saveSetting('enableRetaliationWebhook', newState); }, // saveSetting now triggers UI update
            { tooltip: "Enable/Disable webhooks for when you are attacked."}
        );
        retaliationSection.appendChild(retContainer);

        const retaliationOptionsContent = document.createElement('div');
        retaliationOptionsContent.id = `${MENU_CSS_PREFIX}retaliation-options-content`;
        retaliationOptionsContent.className = `${MENU_CSS_PREFIX}webhook-subsection-content`;
        retaliationOptionsContent.style.display = settings.enableRetaliationWebhook ? 'block' : 'none';
        retContainer.appendChild(retaliationOptionsContent);
        const embedConfigPlaceholder = document.createElement('p');
        embedConfigPlaceholder.className = `${MENU_CSS_PREFIX}tip-text`;
        embedConfigPlaceholder.innerHTML = "<i>Retaliation alerts are sent as embeds with TCAT branding. Advanced field customization will be added later.</i>";
        embedConfigPlaceholder.style.fontStyle = 'italic';
        retaliationOptionsContent.appendChild(embedConfigPlaceholder);
        // Add color picker for retaliation embed color
        createColorPicker('retaliationEmbedColor', 'Embed Color', retaliationOptionsContent, { defaultValue: DEFAULT_SETTINGS.retaliationEmbedColor });


        const previewSection = createSection('Webhook Preview (Retaliation Example)', { description: 'A live preview of your configured Discord webhook message.' });
        webhookSettingsContent.appendChild(previewSection);
        const previewArea = document.createElement('div'); previewArea.id = `${MENU_CSS_PREFIX}webhook-preview-area`; previewArea.className = `${MENU_CSS_PREFIX}webhook-preview-area`;
        previewSection.appendChild(previewArea);
        createButton('Refresh Preview', previewSection, () => {
            const webhookTabElement = document.getElementById(`${MENU_CSS_PREFIX}tab-webhook`);
            if (webhookTabElement) populateWebhookSettingsTab(webhookTabElement);
            showUserMessage("Preview Updated", "Webhook preview has been refreshed.", "info");
        }, { style: { marginTop: '10px' } });

        const testWebhookBtnContainer = document.createElement('div'); testWebhookBtnContainer.style.marginTop = '15px'; testWebhookBtnContainer.style.paddingTop = '10px'; testWebhookBtnContainer.style.borderTop = `1px solid var(--tcat-border-light-color)`;
        const testBtn = createButton('Send Test Retaliation Webhook', testWebhookBtnContainer, testRetaliationAlert, {
            disabled: !settings.enableWebhook || !settings.webhookUrl || !settings.enableRetaliationWebhook,
            tooltip: "Sends a test retaliation embed using the global webhook URL."
        });
        webhookSettingsContent.appendChild(testWebhookBtnContainer);

        mainWebhookContainer.appendChild(webhookSettingsContent);
        tabElement.appendChild(mainWebhookContainer);
        updateWebhookPreview(previewArea); // Initial preview render

        if (masterEnableCheckboxContainer) {
            const masterCheckbox = masterEnableCheckboxContainer.querySelector('input[type="checkbox"]');
            masterCheckbox.addEventListener('change', (event) => {
                webhookSettingsContent.style.display = event.target.checked ? 'block' : 'none';
                if (testBtn) testBtn.disabled = !event.target.checked || !settings.webhookUrl || !settings.enableRetaliationWebhook;
                const retOpts = document.getElementById(`${MENU_CSS_PREFIX}retaliation-options-content`);
                if (retOpts) retOpts.style.display = event.target.checked && settings.enableRetaliationWebhook ? 'block' : 'none';
                if (retaliationToggleButton) retaliationToggleButton.disabled = !event.target.checked;
                updateWebhookPreview(previewArea);
            });
        }
        const retaliationToggleButton = document.getElementById(`${MENU_CSS_PREFIX}enableRetaliationWebhook-toggle-btn`);
        if(retaliationToggleButton) {
            retaliationToggleButton.disabled = !settings.enableWebhook;
            if (testBtn) testBtn.disabled = !settings.enableWebhook || !settings.webhookUrl || !settings.enableRetaliationWebhook;
        }
    }

    function updateWebhookPreview(previewAreaElement) {
        if (!previewAreaElement || !settings.enableWebhook) { // Don't show preview if master is off
            if (previewAreaElement) previewAreaElement.style.display = 'none';
            return;
        }
        previewAreaElement.style.display = 'flex';

        const yourName = settings.webhookUseUserInfo && settings.userTornName ? settings.userTornName : 'YourName';
        const yourId = settings.webhookUseUserInfo && settings.userTornId ? settings.userTornId : 'YourID';
        const attackerName = 'TestAttacker';
        const attackerId = '12345';
        const action = 'Hospitalized (Example)';
        const profileUrl = `https://www.torn.com/profiles.php?XID=${attackerId}`;
        const timeString = new Date().toLocaleString();
        const tcatLogo = NEW_LOGO_URL; // Directly use the constant

        let embedDescription = settings.webhookRetaliationMessage
            .replace('{action}', action)
            .replace('{attackerName}', attackerName)
            .replace('{attackerId}', attackerId)
            .replace('{profileUrl}', profileUrl)
            .replace('{yourName}', yourName)
            .replace('{yourId}', yourId);

        // Basic HTML for preview
        previewAreaElement.innerHTML = `
            <div class="${MENU_CSS_PREFIX}webhook-preview-embed" style="border-left-color: ${settings.retaliationEmbedColor || '#F44336'};">
                <div class="${MENU_CSS_PREFIX}webhook-preview-embed-title">⚔️ Retaliation Alert!</div>
                <div class="${MENU_CSS_PREFIX}webhook-preview-embed-description">${embedDescription}</div>
                <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field">
                    <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field-name">Attacker:</div>
                    <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field-value"><a href="${profileUrl}" target="_blank" style="color: #00b0f4; text-decoration: none;">${attackerName}</a> [${attackerId}]</div>
                </div>
                <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field">
                    <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field-name">Time:</div>
                    <div class="${MENU_CSS_PREFIX}webhook-preview-embed-field-value">${timeString}</div>
                </div>
                <div class="${MENU_CSS_PREFIX}webhook-preview-embed-footer">
                    <img src="${tcatLogo}" alt="TCAT" class="${MENU_CSS_PREFIX}webhook-preview-embed-footer-icon">
                    TCAT v${SCRIPT_VERSION}
                </div>
            </div>
        `;
    }


    // --- Menu Population ---
    function populateMenuTabs() { if (!settingsMenu) { console.error("Cannot populate tabs."); return; } try { const generalTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-general`); if (generalTab) { generalTab.innerHTML = ''; const topSection = createSection('Core Settings'); createIntervalInput('checkIntervalChain', 'Chain Check Int.', topSection, { defaultValue: DEFAULT_SETTINGS.checkIntervalChain, min: 500, max: 60000 }); createCheckbox('tabTitleAlert', 'Enable Tab Title Alerts', topSection); generalTab.appendChild(topSection); const emojiPreviewSection = createSection('Alert Emojis & Tab Preview', { description: 'Customize emojis and test tab title alerts.' }); const emojiPreviewFlexContainer = document.createElement('div'); emojiPreviewFlexContainer.style.display = 'flex'; emojiPreviewFlexContainer.style.gap = '30px'; emojiPreviewFlexContainer.style.alignItems = 'flex-start'; emojiPreviewFlexContainer.style.marginBottom = '15px'; const emojiGridContainer = document.createElement('div'); emojiGridContainer.style.flex = '1'; const emojiGrid = document.createElement('div'); emojiGrid.className = `${MENU_CSS_PREFIX}emoji-grid`; createEmojiInput('preWarningEmoji4min', '4min Emoji', emojiGrid); createEmojiInput('preWarningEmoji3min', '3min Emoji', emojiGrid); createEmojiInput('preWarningEmoji2min', '2min Emoji', emojiGrid); createEmojiInput('preWarningEmoji1min', '1min Emoji', emojiGrid); createEmojiInput('emergencyEmoji', '30s Emoji', emojiGrid); emojiGridContainer.appendChild(emojiGrid); const tipText = document.createElement('p'); tipText.className = `${MENU_CSS_PREFIX}tip-text`; tipText.innerHTML = `Tip: Copy emojis from <a href="https://getemoji.com/" target="_blank" rel="noopener noreferrer" style="color:var(--tcat-accent-color);">GetEmoji.com</a>`; emojiGridContainer.appendChild(tipText); emojiPreviewFlexContainer.appendChild(emojiGridContainer); const previewElementsContainer = document.createElement('div'); previewElementsContainer.style.flex = '1'; createTabPreview(previewElementsContainer); emojiPreviewFlexContainer.appendChild(previewElementsContainer); emojiPreviewSection.appendChild(emojiPreviewFlexContainer); createCheckbox('flashTitle', 'Use Flashing Title Alert', emojiPreviewSection); createCheckbox('solidTabAlert', 'Use Solid Title Alert (Overrides Flashing)', emojiPreviewSection); generalTab.appendChild(emojiPreviewSection); const borderSection = createSection('Border Colors', {description: 'Colors the browser window border overlay will flash.'}); const borderGrid = document.createElement('div'); borderGrid.className = `${MENU_CSS_PREFIX}color-picker-grid`; createColorPicker('preWarningColor4min', '4min Border', borderGrid); createColorPicker('preWarningColor3min', '3min Border', borderGrid); createColorPicker('preWarningColor2min', '2min Border', borderGrid); createColorPicker('preWarningColor1min', '1min Border', borderGrid); createColorPicker('emergencyColor', '30s Border', borderGrid); borderSection.appendChild(borderGrid); generalTab.appendChild(borderSection); } else { console.error("General tab not found!"); } const alertsTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-alerts`); if (alertsTab) { alertsTab.innerHTML = ''; const levelsSection = createSection('Alert Levels & Actions', { description: 'Configure which alerts are active and how they behave.'}); alertsTab.appendChild(levelsSection); const alertLevels = [ { prefix: '4min', label: '4 Min Warning' }, { prefix: '3min', label: '3 Min Warning' }, { prefix: '2min', label: '2 Min Warning' }, { prefix: '1min', label: '1 Min Warning' }, { prefix: '30sec', label: '30 Sec Warning' } ]; alertLevels.forEach(level => { try { const levelContainer = document.createElement('div'); levelContainer.className = `${MENU_CSS_PREFIX}level-block`; const enableContainer = createEnableToggle(level.prefix, level.label, levelContainer); const togglesContainer = document.createElement('div'); togglesContainer.className = `${MENU_CSS_PREFIX}alert-action-toggles`; togglesContainer.style.marginLeft="25px"; togglesContainer.style.display = 'flex'; togglesContainer.style.flexDirection = 'column'; togglesContainer.style.gap = '3px'; createCheckbox(`alert${level.prefix}Border`, 'Border Alert', togglesContainer, `Enable Border at ${level.label}`); createCheckbox(`alert${level.prefix}Tab`, 'Tab Title Alert', togglesContainer, `Enable Tab Title alert at ${level.label}`); const actionsCheckboxElementContainer = createCheckbox(`alert${level.prefix}ActionsEnable`, 'Emergency Actions', togglesContainer, `Trigger profile/URL opening actions defined below at ${level.label}`); const customUrlInputContainer = document.createElement('div'); customUrlInputContainer.className = `${MENU_CSS_PREFIX}custom-url-input-container`; customUrlInputContainer.style.marginLeft = '20px'; customUrlInputContainer.style.marginTop = '4px'; customUrlInputContainer.style.display = settings[`alert${level.prefix}ActionsEnable`] ? 'flex' : 'none'; customUrlInputContainer.style.alignItems = 'center'; createTextInput(`alert${level.prefix}CustomUrl`, 'Custom URL', customUrlInputContainer, { placeholder: 'Optional: Overrides global Emergency URL', width: 'calc(100% - 95px)', containerClass: `${MENU_CSS_PREFIX}compact-input-row`, isSetting: true }); const customUrlLabel = customUrlInputContainer.querySelector('label'); if(customUrlLabel) { customUrlLabel.style.minWidth = '85px'; customUrlLabel.style.marginRight = '5px'; } togglesContainer.appendChild(customUrlInputContainer); if (actionsCheckboxElementContainer) { const checkboxInput = actionsCheckboxElementContainer.querySelector('input[type="checkbox"]'); if (checkboxInput) { checkboxInput.addEventListener('change', (event) => { customUrlInputContainer.style.display = event.target.checked ? 'flex' : 'none'; }); } } if (enableContainer && enableContainer.appendChild) { enableContainer.appendChild(togglesContainer); } else { levelContainer.appendChild(togglesContainer); } levelsSection.appendChild(levelContainer); } catch(e) { console.error(`[TCAT] Error creating UI block for level ${level.prefix}:`, e); const errorP = document.createElement('p'); errorP.style.color = 'red'; errorP.textContent = `Error loading UI for ${level.label}. Check console.`; levelsSection.appendChild(errorP); } }); const emergencyActionsSection = createSection('Global Emergency Actions', { titleClass: `${MENU_CSS_PREFIX}section-title-large`, description: 'Profiles and a global URL to open if a specific alert level has "Emergency Actions" checked AND no "Custom URL" is set for that level.' }); createProfileInput(emergencyActionsSection); createUrlInput('emergencyUrl', 'Global Emergency URL', emergencyActionsSection, {width: '95%'}); alertsTab.appendChild(emergencyActionsSection); const testSection = createSection('Test Chain Alerts', { titleClass: `${MENU_CSS_PREFIX}section-title-large`, description: 'Manually trigger alert visuals (border/tab) and actions (profiles/URL) if enabled for the level. Tests last ~7 seconds.' }); const testButtonContainer = document.createElement('div'); testButtonContainer.className = `${MENU_CSS_PREFIX}test-button-container`; const createTestButton = (text, level) => { createButton(text, testButtonContainer, function() { testChainAlert(level); }); }; createTestButton('Test 4m Alert', 'pre4'); createTestButton('Test 3m Alert', 'pre'); createTestButton('Test 2m Alert', 'pre2'); createTestButton('Test 1m Alert', 'warn'); createTestButton('Test 30s Alert', 'emerg'); testSection.appendChild(testButtonContainer); alertsTab.appendChild(testSection); } else { console.error("Alerts tab not found!"); } const chainToolTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-chain-tool`); if (chainToolTab) { populateChainToolTab(chainToolTab); } else { console.error("Chain Tool tab not found!"); } const webhookTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`); if (webhookTab) { populateWebhookSettingsTab(webhookTab); } else { console.error("Webhook tab not found!"); } const apiTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-api`); if (apiTab) { populateApiSettingsTab(apiTab); } else { console.error("API Settings tab not found!"); } const aboutTab = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-about`); if (aboutTab) { aboutTab.innerHTML = ''; const aboutSection = document.createElement('div'); aboutSection.style.textAlign = 'center'; aboutSection.style.marginBottom = '15px'; const title = document.createElement('h2'); title.textContent = "About TCAT"; title.className = `${MENU_CSS_PREFIX}section-title-large`; aboutSection.appendChild(title); const imgContainer = document.createElement('div'); imgContainer.className = `${MENU_CSS_PREFIX}about-profile-pic-container`; const img = document.createElement('img'); img.src = 'https://profileimages.torn.com/7f15b5d8-872c-44c4-8e27-612f90525fd8-3626448.png'; img.alt = 'Profile Image'; img.className = `${MENU_CSS_PREFIX}about-profile-pic`; imgContainer.appendChild(img); aboutSection.appendChild(imgContainer); const userLink = document.createElement('p'); userLink.className = `${MENU_CSS_PREFIX}about-centered-text`; userLink.innerHTML = `<a href="https://www.torn.com/profiles.php?XID=3626448" target="_blank" rel="noopener noreferrer" style="color:var(--tcat-accent-color);">HeyItzWerty [3626448]</a>`; aboutSection.appendChild(userLink); const scriptInfo = document.createElement('p'); scriptInfo.className = `${MENU_CSS_PREFIX}about-centered-text ${MENU_CSS_PREFIX}about-script-info`; scriptInfo.innerHTML = `Torn Chain Alert & Tools v${SCRIPT_VERSION}<br>Provides chain alerts & Chain Tool for list management.`; aboutSection.appendChild(scriptInfo); const devInfo = document.createElement('p'); devInfo.className = `${MENU_CSS_PREFIX}about-centered-text ${MENU_CSS_PREFIX}about-dev-info`; devInfo.textContent = `Thought of, created and tested during a 2k+ chain over a single night of chain watching.`; aboutSection.appendChild(devInfo); aboutTab.appendChild(aboutSection); const appearanceSection = createSection('Menu Appearance', { titleClass: `${MENU_CSS_PREFIX}section-title-large`, description: 'Adjust visual elements of the TCAT menu.'}); createDropdownInput('fontSize', 'Font Size', appearanceSection, FONT_SIZE_OPTIONS, {width: '150px'}); createDropdownInput('menuWidth', 'Menu Width', appearanceSection, MENU_SIZE_OPTIONS.width, {width: '180px'}); createDropdownInput('menuHeight', 'Menu Height', appearanceSection, MENU_SIZE_OPTIONS.height, {width: '180px'}); createColorSchemeSelector(appearanceSection); aboutTab.appendChild(appearanceSection); const resetSection = createSection('Reset General Settings', { description: 'Reset alert/general/webhook/API/appearance settings to defaults. Chain Lists will NOT be affected.' }); const resetBtnContainer = document.createElement('div'); resetBtnContainer.style.textAlign = 'center'; resetBtnContainer.style.marginTop = '10px'; createButton("Reset TCAT Options", resetBtnContainer, resetSettings, {extraClass: `${MENU_CSS_PREFIX}button-danger`}); resetSection.appendChild(resetBtnContainer); aboutTab.appendChild(resetSection); } else { console.error(`[TCAT v${SCRIPT_VERSION}] About tab not found!`); } } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] Error populating tabs:`, e); const contentArea = settingsMenu.querySelector(`.${MENU_CSS_PREFIX}content-container`); if(contentArea) contentArea.innerHTML = "<p style='color: red;'>Error loading UI.</p>"; } }
    function createMenuIfNotExists() { if (settingsMenu && document.body.contains(settingsMenu)) { return settingsMenu; } try { settingsMenu = document.createElement('div'); settingsMenu.id = SETTINGS_MENU_ID; settingsMenu.style.display = 'none'; if (settings.fontSize) settingsMenu.style.fontSize = settings.fontSize; if (settings.menuWidth) settingsMenu.style.width = settings.menuWidth; if (settings.menuHeight) settingsMenu.style.height = settings.menuHeight; settingsMenu.innerHTML = buildMenuHTML(); document.body.appendChild(settingsMenu); populateMenuTabs(); applyColorScheme(); const closeButton = settingsMenu.querySelector(`.${MENU_CSS_PREFIX}button-close`); if (closeButton) { closeButton.onclick = toggleMenu; } const tabButtons = settingsMenu.querySelectorAll(`.${MENU_CSS_PREFIX}tab-button`); tabButtons.forEach(button => { button.onclick = (e) => { const tabId = e.target.dataset.tabId; if (!tabId) return; settingsMenu.querySelectorAll(`.${MENU_CSS_PREFIX}tab-button`).forEach(btn => btn.classList.remove('active')); settingsMenu.querySelectorAll(`.${MENU_CSS_PREFIX}tab-content`).forEach(content => content.classList.remove('active')); e.target.classList.add('active'); const targetContent = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-${tabId}`); if (targetContent) targetContent.classList.add('active'); if (tabId === 'chain-tool') updateChainToolUI(); else if (tabId === 'api') { const apiTabContent = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-api`); if (apiTabContent) populateApiSettingsTab(apiTabContent); } else if (tabId === 'webhook') { const webhookTabContent = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`); if(webhookTabContent) populateWebhookSettingsTab(webhookTabContent); autofillWebhookUserDetails(); } }; }); const contentContainer = settingsMenu.querySelector(`.${MENU_CSS_PREFIX}content-container`); if (contentContainer) { contentContainer.addEventListener('wheel', function(event) { const el = event.currentTarget; const isScrollable = el.scrollHeight > el.clientHeight; const isScrollingUp = event.deltaY < 0; const isScrollingDown = event.deltaY > 0; if (isScrollable) { if (isScrollingUp && el.scrollTop === 0) { return; } if (isScrollingDown && el.scrollTop + el.clientHeight >= el.scrollHeight -1 ) { return; } event.stopPropagation(); } }, { passive: false }); } return settingsMenu; } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] CRITICAL error createMenu:`, error); showUserMessage("TCAT Error", "Failed create menu.", "error"); if (settingsMenu && settingsMenu.parentNode) settingsMenu.remove(); settingsMenu = null; return null; } }
    function toggleMenu() { try { if (!createMenuIfNotExists()) { return; } const currentDisplay = settingsMenu.style.display; const isHidden = !currentDisplay || currentDisplay === 'none'; settingsMenu.style.display = isHidden ? 'flex' : 'none'; if (isHidden) { applyUISettings(); applyColorScheme(); testAndSetApiStatus(); const activeTabButton = settingsMenu.querySelector(`.${MENU_CSS_PREFIX}tab-button.active`); if (activeTabButton) { const activeTabId = activeTabButton.dataset.tabId; if (activeTabId === 'chain-tool') updateChainToolUI(); else if (activeTabId === 'api') { const apiTabContent = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-api`); if (apiTabContent) populateApiSettingsTab(apiTabContent); } else if (activeTabId === 'webhook') { const webhookTabContent = settingsMenu.querySelector(`#${MENU_CSS_PREFIX}tab-webhook`); if(webhookTabContent) populateWebhookSettingsTab(webhookTabContent); autofillWebhookUserDetails(); } } } } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] Error toggling menu:`, error); } }

    // --- Menu Styles ---
    function addMenuStyles() { try { const css = `
        /* --- Border Overlay --- */
        #${BORDER_OVERLAY_ID} { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; box-sizing: border-box; border: 5px solid transparent; z-index: 2147483646; pointer-events: none; display: none; }
        /* --- Main Menu Container --- */
        #${SETTINGS_MENU_ID} { /* Dimensions now set by applyUISettings */ display: none; position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; max-width: 95% !important; max-height: 90vh !important; background-color: var(--tcat-bg-color) !important; border: 1px solid var(--tcat-border-color) !important; z-index: 2147483647 !important; box-shadow: 4px 4px 12px rgba(0,0,0,0.3) !important; color: var(--tcat-text-color) !important; font-size: ${settings.fontSize}; flex-direction: column !important; overflow: hidden; border-radius: 5px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}menu-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; border-bottom: 1px solid var(--tcat-border-light-color); background-color: var(--tcat-bg-secondary-color); flex-shrink: 0; border-radius: 5px 5px 0 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}title-wrapper { flex-grow: 1; text-align: center; margin-left: 30px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}title { margin: 0; font-size: 1.3em; font-weight: bold; display: inline-flex; align-items: center; gap: 8px; color: var(--tcat-text-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}title-icon { width: 22px; height: 22px; vertical-align: middle; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-close { background: none; border: none; font-size: 1.8em; font-weight: bold; color: var(--tcat-text-light-color); cursor: pointer; padding: 0 5px; line-height: 1; flex-shrink: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-close:hover { color: var(--tcat-text-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-container { padding: 5px 5px 0px 5px; border-bottom: 1px solid var(--tcat-border-light-color); background-color: var(--tcat-bg-secondary-color); flex-shrink: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-button { padding: 6px 10px; margin: 0 1px; border: 1px solid var(--tcat-border-light-color); border-bottom: none; border-radius: 4px 4px 0 0; cursor: pointer; background-color: var(--tcat-tab-inactive-bg); color: var(--tcat-text-light-color); display: inline-block; position: relative; bottom: -1px; font-size: 0.95em; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-button.active { background-color: var(--tcat-content-bg-color); border-color: var(--tcat-border-light-color); border-bottom-color: var(--tcat-content-bg-color); font-weight: 600; z-index: 1; color: var(--tcat-accent-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-button:hover:not(.active) { background-color: var(--tcat-tab-hover-bg); color: var(--tcat-text-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}content-container { padding: 15px 20px; flex-grow: 1; overflow-y: auto; background-color: var(--tcat-content-bg-color); color: var(--tcat-text-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-content { display: none; animation: fadeIn 0.3s ease-in-out; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-content.active { display: block; }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}menu-footer { padding: 8px 15px; background-color: var(--tcat-bg-secondary-color); font-size: 0.9em; color: var(--tcat-text-light-color); display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; border-top: 1px solid var(--tcat-border-color); border-radius: 0 0 5px 5px;}
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}status-indicator { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: bold; flex-shrink: 1; margin-right: 5px; display: inline-block; max-width: calc(50% - 70px); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}api-call-count { font-weight: bold; flex-shrink: 1; margin-left: 5px; margin-right: 5px; white-space: nowrap; display: inline-block; text-align: left; max-width: calc(50% - 70px); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}version-info { font-style: italic; flex-shrink: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}forum-link-container { flex-grow: 1; text-align: center; margin: 0 5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}forum-link { color: var(--tcat-accent-color); text-decoration: none; font-size: 0.9em; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}forum-link:hover { text-decoration: underline; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}section { margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px dashed var(--tcat-border-light-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}section:last-child { border-bottom: none; margin-bottom: 0;}
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}section-title { margin-top: 0; margin-bottom: 8px; color: var(--tcat-accent-color); font-size: 1.15em; border-bottom: 1px solid var(--tcat-border-color); padding-bottom: 5px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}section-description { font-size: 0.9em; color: var(--tcat-text-light-color); margin-top: -5px; margin-bottom: 10px; padding-left: 5px; border-left: 3px solid var(--tcat-accent-color); opacity: 0.8; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}setting-container { margin-bottom: 8px; padding: 2px 0; display: flex; align-items: center; flex-wrap: wrap; gap: 5px 10px; }
        #${SETTINGS_MENU_ID} label { cursor: pointer; margin-right: 5px; color: var(--tcat-text-color); flex-shrink: 0; }
        #${SETTINGS_MENU_ID} input[type="checkbox"] { accent-color: var(--tcat-accent-color); vertical-align: middle; width: 16px; height: 16px; }
        #${SETTINGS_MENU_ID} input[type="checkbox"] + label { margin-right: 15px; vertical-align: middle; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}input, #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}textarea, #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}select { padding: 5px 8px; border: 1px solid var(--tcat-input-border-color); border-radius: 3px; background-color: var(--tcat-input-bg-color); color: var(--tcat-input-text-color); font-size: 1em; box-sizing: border-box; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}input:focus, #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}textarea:focus, #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}select:focus { border-color: var(--tcat-accent-color); box-shadow: 0 0 3px var(--tcat-accent-color); outline: none; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}textarea { width: 100%; max-width: 100%; resize: vertical; display: block; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}input-color { padding: 1px 2px; height: 28px; width: 45px; vertical-align: middle; border: 1px solid var(--tcat-input-border-color); cursor: pointer; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}input-emoji { width: 40px; text-align: center; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}select { cursor: pointer; background-color: var(--tcat-input-bg-color); color: var(--tcat-input-text-color); border: 1px solid var(--tcat-input-border-color); padding: 4px; border-radius: 3px;}
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button { background-color: var(--tcat-button-bg-color); border: 1px solid var(--tcat-border-light-color); color: var(--tcat-button-text-color); padding: 6px 12px; border-radius: 3px; cursor: pointer; margin: 0 5px 5px 0; transition: background-color 0.2s ease; font-weight: 500; text-shadow: 1px 1px 1px rgba(0,0,0,0.1); box-shadow: inset 0 -2px 1px rgba(0,0,0,0.1); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button:hover { background-color: var(--tcat-button-hover-bg-color); border-color: var(--tcat-border-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button:active { box-shadow: inset 0 1px 1px rgba(0,0,0,0.2); transform: translateY(1px); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-danger { background-color: var(--tcat-danger-color); border-color: var(--tcat-danger-hover-color); color: var(--tcat-danger-text-color); font-weight: bold; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-danger:hover { background-color: var(--tcat-danger-hover-color); border-color: var(--tcat-danger-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button:disabled { cursor: not-allowed; opacity: 0.6; background-color: var(--tcat-button-bg-color) !important; border-color: var(--tcat-border-light-color) !important; color: var(--tcat-text-light-color) !important; box-shadow: none; text-shadow: none; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tab-preview-area { min-height:2em; border:1px dashed var(--tcat-border-color); padding:5px; margin:5px 0; background-color:var(--tcat-input-bg-color); font-style:italic; color:var(--tcat-text-light-color); }
        #${SETTINGS_MENU_ID} code { background-color: var(--tcat-bg-secondary-color); padding: 1px 3px; border-radius: 3px; font-family: monospace; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tip-text { font-size: 0.9em; margin-top: 10px; color: var(--tcat-text-light-color); line-height: 1.3; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}tip-text a { color: var(--tcat-accent-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}interval-input-container { justify-content: flex-start; width: auto; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}interval-input-container input { width: 70px !important; margin-right: 3px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}input-unit { color: var(--tcat-text-light-color); margin-left: 2px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}section-title-large { font-size: 1.3em !important; text-align: center; border-bottom: 1px solid var(--tcat-accent-color) !important; margin-top: 25px !important; margin-bottom: 15px !important; color: var(--tcat-accent-color) !important; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}alert-action-toggles { }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}level-block { margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid var(--tcat-border-light-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}level-block:last-of-type { border-bottom: none; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}checkbox-container { margin-bottom: 2px !important; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}full-width-container input { width: 100% !important; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}color-picker-grid { display: flex; flex-wrap: wrap; gap: 10px 20px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}color-picker-grid .${MENU_CSS_PREFIX}setting-container { margin-bottom: 5px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}emoji-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px 20px; align-items: center; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}emoji-grid .${MENU_CSS_PREFIX}setting-container { margin-bottom: 0px; width: auto; display:flex; justify-content: flex-start; align-items: center; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}color-picker-grid label { min-width: auto; text-align: left; margin-right: 5px; flex-shrink: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}emoji-grid .${MENU_CSS_PREFIX}setting-container label { min-width: 65px; text-align: right; margin-right: 5px; flex-shrink: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}test-button-container { display: flex; flex-wrap: wrap; gap: 10px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}about-profile-pic-container { text-align: center; margin-bottom: 10px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}about-profile-pic { border-radius: 4px; width: 80px; height: 80px; border: 2px solid var(--tcat-border-color); margin: 10px auto 5px auto; display: block; object-fit: cover; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}about-centered-text { text-align: center; margin-bottom: 5px; margin-top: 0; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}about-script-info { font-size: 0.95em; color: var(--tcat-text-light-color); line-height: 1.4; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}about-dev-info { font-size: 0.85em; font-style: italic; color: var(--tcat-text-light-color); margin-top: 10px; }
        #${CHAIN_TOOL_ID_LIST_DISPLAY_ID} { font-family: monospace; font-size: 0.95em; background-color: var(--tcat-input-bg-color); border: 1px solid var(--tcat-input-border-color); color: var(--tcat-input-text-color); border-radius: 3px; }
        #${CHAIN_TOOL_ID_LIST_DISPLAY_ID} option:hover { background-color: var(--tcat-tab-hover-bg) !important; color: var(--tcat-text-color) !important; }
        #${CHAIN_TOOL_ID_LIST_DISPLAY_ID} option:checked { box-shadow: 0 0 0 1px var(--tcat-accent-color) inset; background-color: var(--tcat-bg-secondary-color) !important; color: var(--tcat-text-color) !important; }
        #${CHAIN_TOOL_ID_LIST_DISPLAY_ID} option { padding: 2px 5px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}setting-container.${MENU_CSS_PREFIX}inline-input-group label { flex-basis: 100px; flex-shrink: 0; text-align: right; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}setting-container.${MENU_CSS_PREFIX}inline-setting { margin-bottom: 0px !important; }
        #${SETTINGS_MENU_ID} #${MENU_CSS_PREFIX}tab-api ul { margin-top: 5px; margin-bottom: 10px; padding-left: 20px; }
        #${SETTINGS_MENU_ID} #${MENU_CSS_PREFIX}tab-api li { margin-bottom: 5px; }
        #${CHAIN_TOOL_VERIFIED_FACTION_DISPLAY_ID} { padding: 3px; border-radius: 3px; display: block; text-align: left; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}compact-input-row label {min-width: 85px !important; margin-right: 5px !important; }

        /* --- WEBHOOK TAB STYLES --- */
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-main-content { padding-top: 10px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-subsection { padding: 10px; border: 1px solid var(--tcat-border-light-color); border-radius: 4px; margin-top: 15px; margin-bottom:15px; background-color: var(--tcat-bg-secondary-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-subsection-content { padding-left: 15px; margin-top: 8px; border-left: 2px solid var(--tcat-accent-color); }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-enable-disable { padding: 5px 12px; font-weight: bold; min-width: 180px; text-align: center; transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; border-radius: 3px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-enabled { background-color: #4CAF50; border: 1px solid #388E3C; color: white; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-enabled:hover { background-color: #43A047; border-color: #2E7D32;}
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-disabled { background-color: #f44336; border: 1px solid #d32f2f; color: white; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}button-disabled:hover { background-color: #d32f2f; border-color: #c62828; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-area { border: 1px dashed var(--tcat-border-color); background-color: var(--tcat-input-bg-color); padding: 15px; margin-top: 10px; min-height: 200px; border-radius: 4px; display: flex; font-family: 'Whitney', 'Helvetica Neue', Helvetica, Arial, sans-serif; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed { background-color: #2f3136; border-left: 4px solid var(--tcat-accent-color); border-radius: 4px; padding: 8px 12px; color: #dcddde; width: 100%; box-sizing: border-box; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-title { font-weight: bold; color: #ffffff; margin-bottom: 4px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-description { font-size: 0.9em; line-height: 1.4; margin-bottom: 8px; white-space: pre-wrap; word-break: break-word; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-field { margin-bottom: 8px; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-field-name { font-weight: bold; color: #ffffff; margin-bottom: 2px; font-size: 0.9em; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-field-value { font-size: 0.85em; line-height: 1.3; white-space: pre-wrap; word-break: break-word; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-footer { font-size: 0.75em; color: #72767d; margin-top: 8px; display: flex; align-items: center; }
        #${SETTINGS_MENU_ID} .${MENU_CSS_PREFIX}webhook-preview-embed-footer-icon { width: 20px; height: 20px; margin-right: 6px; border-radius: 50%; }
        `; if (typeof GM_addStyle === 'function') { GM_addStyle(css); } else { console.warn(`[TCAT v${SCRIPT_VERSION}] GM_addStyle not available. Using fallback.`); const S=document.createElement('style'); S.textContent=css; document.head.appendChild(S); } } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] Failed to add styles:`, error); showUserMessage("TCAT Error", "Failed to apply styles.", "error"); } }

    // --- Main Execution ---
    function main() { try { initSettings(); addMenuStyles(); if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand("Toggle TCAT Settings", toggleMenu); } else { console.warn(`[TCAT v${SCRIPT_VERSION}] GM_registerMenuCommand not available.`); } restartIntervals(); } catch (error) { console.error(`[TCAT v${SCRIPT_VERSION}] CRITICAL error in main():`, error); showUserMessage("TCAT Error", `Initialization failed: ${error.message}`, "error"); } }

    // --- Script Entry Point ---
    try { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } } catch(e) { console.error(`[TCAT v${SCRIPT_VERSION}] CRITICAL Error at script entry point:`, e); alert("TCAT Script failed to initialize. Check the browser console (F12) for errors."); }

})(); // End of IIFE wrapper