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.

// ==UserScript==
// @name         TCAT - Torn Chain Alert & Tool
// @namespace    http://torn.com/HeyItzWerty
// @version      1.2.1
// @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/JjTydW6.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