<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Compass — Cartographer</title>
    <style>
        *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

        body {
            font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif;
            background: #f3f4f6;
            color: #1f2937;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }

        /* ===== Top Header ===== */
        .app-header {
            background: #ffffff;
            border-bottom: 1px solid #e5e7eb;
            padding: 0 24px;
            display: flex;
            align-items: center;
            gap: 24px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.06);
        }

        .app-title {
            font-size: 17px;
            font-weight: 700;
            color: #111827;
            padding: 14px 0;
            white-space: nowrap;
        }

        .app-title span {
            color: #2563eb;
        }

        /* ===== Tab Navigation ===== */
        .tab-nav {
            display: flex;
            gap: 0;
            overflow-x: auto;
            flex: 1;
        }

        .tab-btn {
            font-family: inherit;
            font-size: 13px;
            font-weight: 600;
            color: #6b7280;
            background: none;
            border: none;
            border-bottom: 2px solid transparent;
            padding: 14px 18px;
            cursor: pointer;
            white-space: nowrap;
            transition: color 0.15s, border-color 0.15s;
        }

        .tab-btn:hover {
            color: #374151;
        }

        .tab-btn.active {
            color: #2563eb;
            border-bottom-color: #2563eb;
        }

        .refresh-btn {
            font-family: inherit;
            font-size: 12px;
            font-weight: 600;
            color: #6b7280;
            background: #f9fafb;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            padding: 6px 14px;
            cursor: pointer;
            white-space: nowrap;
            transition: all 0.15s;
        }

        .refresh-btn:hover {
            background: #f3f4f6;
            border-color: #9ca3af;
        }

        /* ===== Tab Content ===== */
        .tab-content {
            flex: 1;
            display: none;
            padding: 20px 24px;
            overflow-y: auto;
        }

        .tab-content.active {
            display: block;
        }

        /* ===== Summary Cards ===== */
        .summary-cards {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
            gap: 16px;
            margin-bottom: 24px;
        }

        .summary-card {
            background: #ffffff;
            border: 1px solid #e5e7eb;
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.04);
        }

        .summary-card-label {
            font-size: 12px;
            font-weight: 600;
            color: #6b7280;
            text-transform: uppercase;
            letter-spacing: 0.4px;
            margin-bottom: 8px;
        }

        .summary-card-value {
            font-size: 28px;
            font-weight: 700;
            color: #111827;
            line-height: 1;
            margin-bottom: 8px;
        }

        .summary-card-detail {
            font-size: 12px;
            color: #6b7280;
            display: flex;
            gap: 12px;
        }

        .summary-card-detail .active-count { color: #059669; font-weight: 600; }
        .summary-card-detail .inactive-count { color: #dc2626; font-weight: 600; }
        .summary-card-detail .set-count { color: #059669; font-weight: 600; }
        .summary-card-detail .notset-count { color: #dc2626; font-weight: 600; }

        .summary-card.loading .summary-card-value {
            color: #d1d5db;
        }

        /* ===== Sections / Panels ===== */
        .section {
            background: #ffffff;
            border: 1px solid #e5e7eb;
            border-radius: 10px;
            margin-bottom: 20px;
            overflow: hidden;
            box-shadow: 0 1px 3px rgba(0,0,0,0.04);
        }

        .section-header {
            padding: 14px 20px;
            border-bottom: 1px solid #e5e7eb;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .section-header h3 {
            font-size: 15px;
            font-weight: 600;
            color: #111827;
        }

        .section-body {
            padding: 0;
        }

        /* ===== Data Tables ===== */
        .data-table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
        }

        .data-table thead th {
            background: #f9fafb;
            border-bottom: 2px solid #e5e7eb;
            padding: 10px 14px;
            text-align: left;
            font-size: 11px;
            font-weight: 700;
            color: #6b7280;
            text-transform: uppercase;
            letter-spacing: 0.4px;
            white-space: nowrap;
            user-select: none;
        }

        .data-table tbody tr {
            transition: background 0.1s;
        }

        .data-table tbody tr:hover {
            background: #f9fafb;
        }

        .data-table tbody tr:not(:last-child) td {
            border-bottom: 1px solid #f3f4f6;
        }

        .data-table tbody td {
            padding: 10px 14px;
            font-size: 13px;
            color: #374151;
            vertical-align: middle;
        }

        .col-name {
            font-weight: 600;
            color: #1f2937;
            max-width: 300px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .col-mono {
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 12px;
            color: #6b7280;
        }

        .col-date {
            white-space: nowrap;
            color: #6b7280;
            font-size: 12px;
        }

        /* ===== Badges ===== */
        .badge {
            display: inline-block;
            padding: 2px 9px;
            border-radius: 12px;
            font-size: 11px;
            font-weight: 600;
            white-space: nowrap;
        }

        .badge-on { background: #d1fae5; color: #065f46; }
        .badge-off { background: #fee2e2; color: #991b1b; }
        .badge-enabled { background: #d1fae5; color: #065f46; }
        .badge-disabled { background: #fee2e2; color: #991b1b; }
        .badge-active { background: #d1fae5; color: #065f46; }
        .badge-inactive { background: #fee2e2; color: #991b1b; }
        .badge-sync { background: #dbeafe; color: #1e40af; }
        .badge-async { background: #e0e7ff; color: #3730a3; }
        .badge-set { background: #d1fae5; color: #065f46; }
        .badge-default { background: #fef3c7; color: #92400e; }
        .badge-notset { background: #fee2e2; color: #991b1b; }
        .badge-type { background: #e0e7ff; color: #3730a3; }
        .badge-stage { background: #f3f4f6; color: #374151; }

        /* ===== Buttons ===== */
        .btn {
            font-family: inherit;
            font-size: 12px;
            padding: 5px 12px;
            border-radius: 6px;
            border: 1px solid #d1d5db;
            background: #f9fafb;
            color: #374151;
            cursor: pointer;
            font-weight: 600;
            transition: all 0.15s;
            white-space: nowrap;
        }

        .btn:hover:not(:disabled) { background: #f3f4f6; border-color: #9ca3af; }
        .btn:focus { outline: none; box-shadow: 0 0 0 3px rgba(59,130,246,0.12); }
        .btn:disabled { opacity: 0.5; cursor: not-allowed; }

        .btn-primary { background: #2563eb; color: #fff; border-color: #2563eb; }
        .btn-primary:hover:not(:disabled) { background: #1d4ed8; }

        .btn-danger { background: #fee2e2; color: #991b1b; border-color: #fecaca; }
        .btn-danger:hover:not(:disabled) { background: #fecaca; }

        .btn-success { background: #d1fae5; color: #065f46; border-color: #a7f3d0; }
        .btn-success:hover:not(:disabled) { background: #a7f3d0; }

        .btn-toggle { min-width: 70px; text-align: center; }
        .btn-toggle.is-on { background: #fee2e2; color: #991b1b; border-color: #fecaca; }
        .btn-toggle.is-off { background: #d1fae5; color: #065f46; border-color: #a7f3d0; }

        /* ===== Filter / Search Bar ===== */
        .toolbar {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            padding: 14px 20px;
            align-items: center;
            border-bottom: 1px solid #e5e7eb;
        }

        .toolbar-search {
            font-family: inherit;
            font-size: 13px;
            padding: 7px 12px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            background: #fff;
            color: #1f2937;
            min-width: 240px;
            transition: border-color 0.15s, box-shadow 0.15s;
        }

        .toolbar-search:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
        }

        .toolbar-select {
            font-family: inherit;
            font-size: 13px;
            padding: 7px 12px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            background: #fff;
            color: #1f2937;
            min-width: 140px;
        }

        .toolbar-select:focus {
            outline: none;
            border-color: #3b82f6;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
        }

        .toolbar-count {
            font-size: 12px;
            color: #6b7280;
            margin-left: auto;
        }

        /* ===== Spinner ===== */
        .spinner {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid #e5e7eb;
            border-top-color: #3b82f6;
            border-radius: 50%;
            animation: spin 0.7s linear infinite;
            vertical-align: middle;
        }

        .spinner-sm {
            width: 12px;
            height: 12px;
            border-width: 2px;
            margin-right: 4px;
        }

        @keyframes spin { to { transform: rotate(360deg); } }

        /* ===== Empty / Loading States ===== */
        .empty-state {
            text-align: center;
            padding: 48px 24px;
            color: #9ca3af;
        }

        .empty-state h4 {
            font-size: 15px;
            color: #6b7280;
            font-weight: 600;
            margin-bottom: 6px;
        }

        .empty-state p {
            font-size: 13px;
        }

        .loading-state {
            text-align: center;
            padding: 40px;
            color: #6b7280;
            font-size: 13px;
        }

        /* ===== Toast Notifications ===== */
        .toast-container {
            position: fixed;
            top: 16px;
            right: 16px;
            z-index: 10000;
            display: flex;
            flex-direction: column;
            gap: 8px;
            pointer-events: none;
        }

        .toast {
            background: #ffffff;
            border: 1px solid #e5e7eb;
            border-radius: 8px;
            padding: 12px 18px;
            box-shadow: 0 4px 16px rgba(0,0,0,0.12);
            font-size: 13px;
            color: #374151;
            max-width: 380px;
            pointer-events: auto;
            animation: toastIn 0.25s ease;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .toast.success { border-left: 4px solid #059669; }
        .toast.error { border-left: 4px solid #dc2626; }

        .toast-icon { font-size: 16px; flex-shrink: 0; }
        .toast.success .toast-icon { color: #059669; }
        .toast.error .toast-icon { color: #dc2626; }

        .toast.fade-out { animation: toastOut 0.3s ease forwards; }

        @keyframes toastIn { from { opacity: 0; transform: translateX(60px); } to { opacity: 1; transform: translateX(0); } }
        @keyframes toastOut { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(60px); } }

        /* ===== Confirmation Dialog ===== */
        .confirm-overlay {
            position: fixed;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.4);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 9000;
            animation: fadeIn 0.15s ease;
        }

        .confirm-box {
            background: #ffffff;
            border-radius: 12px;
            padding: 24px;
            max-width: 440px;
            width: 90%;
            box-shadow: 0 20px 60px rgba(0,0,0,0.2);
        }

        .confirm-box h3 {
            font-size: 16px;
            font-weight: 700;
            color: #111827;
            margin-bottom: 8px;
        }

        .confirm-box p {
            font-size: 13px;
            color: #4b5563;
            margin-bottom: 20px;
            line-height: 1.6;
        }

        .confirm-actions {
            display: flex;
            gap: 8px;
            justify-content: flex-end;
        }

        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

        /* ===== Inline Edit ===== */
        .inline-edit-group {
            display: flex;
            gap: 6px;
            align-items: center;
        }

        .inline-edit-input {
            font-family: inherit;
            font-size: 13px;
            padding: 5px 10px;
            border: 1px solid #3b82f6;
            border-radius: 6px;
            background: #fff;
            color: #1f2937;
            min-width: 200px;
            box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
        }

        .inline-edit-input:focus { outline: none; }

        /* ===== Component Breakdown (Dashboard) ===== */
        .breakdown-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
            gap: 8px;
            padding: 16px 20px;
        }

        .breakdown-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 12px;
            background: #f9fafb;
            border-radius: 6px;
            font-size: 13px;
        }

        .breakdown-item .type-name {
            color: #374151;
            font-weight: 500;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            margin-right: 8px;
        }

        .breakdown-item .type-count {
            font-weight: 700;
            color: #2563eb;
            flex-shrink: 0;
        }

        /* ===== Recent Items List ===== */
        .recent-list {
            list-style: none;
        }

        .recent-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 10px 20px;
            border-bottom: 1px solid #f3f4f6;
            font-size: 13px;
        }

        .recent-item:last-child { border-bottom: none; }

        .recent-item .name {
            font-weight: 600;
            color: #1f2937;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            max-width: 300px;
        }

        .recent-item .meta {
            font-size: 12px;
            color: #6b7280;
            display: flex;
            gap: 12px;
            flex-shrink: 0;
        }

        /* ===== Two-Column Layout (Dashboard) ===== */
        .mc-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }

        @media (max-width: 900px) {
            .mc-grid { grid-template-columns: 1fr; }
            .toolbar-search { min-width: 180px; }
            .summary-cards { grid-template-columns: repeat(2, 1fr); }
        }

        @media (max-width: 600px) {
            .app-header { padding: 0 12px; gap: 12px; }
            .tab-btn { padding: 12px 12px; font-size: 12px; }
            .tab-content { padding: 12px; }
            .summary-cards { grid-template-columns: 1fr; }
        }
    </style>
</head>
<body>

    <!-- Header with tabs -->
    <div class="app-header">
        <div class="app-title"><span>Compass</span></div>
        <div class="tab-nav">
            <button class="tab-btn active" data-tab="mission-control">Dashboard</button>
            <button class="tab-btn" data-tab="cloud-flows">Cloud Flows</button>
            <button class="tab-btn" data-tab="plugin-steps">Plugin Steps</button>
            <button class="tab-btn" data-tab="env-variables">Environment Variables</button>
            <button class="tab-btn" data-tab="business-rules">Business Rules</button>
        </div>
        <button class="refresh-btn" id="globalRefresh">Refresh</button>
    </div>

    <!-- Toast container -->
    <div class="toast-container" id="toastContainer"></div>

    <!-- Confirm dialog (hidden) -->
    <div id="confirmOverlay" class="confirm-overlay" style="display:none;">
        <div class="confirm-box">
            <h3 id="confirmTitle">Confirm</h3>
            <p id="confirmText"></p>
            <div class="confirm-actions">
                <button class="btn" id="confirmCancel">Cancel</button>
                <button class="btn btn-danger" id="confirmOk">Confirm</button>
            </div>
        </div>
    </div>

    <!-- ===== Tab: Dashboard ===== -->
    <div class="tab-content active" id="tab-mission-control">
        <div class="summary-cards" id="mcCards">
            <div class="summary-card loading" id="mcCardFlows">
                <div class="summary-card-label">Cloud Flows</div>
                <div class="summary-card-value"><span class="spinner"></span></div>
                <div class="summary-card-detail"></div>
            </div>
            <div class="summary-card loading" id="mcCardBR">
                <div class="summary-card-label">Business Rules</div>
                <div class="summary-card-value"><span class="spinner"></span></div>
                <div class="summary-card-detail"></div>
            </div>
            <div class="summary-card loading" id="mcCardPlugins">
                <div class="summary-card-label">Plugin Steps</div>
                <div class="summary-card-value"><span class="spinner"></span></div>
                <div class="summary-card-detail"></div>
            </div>
            <div class="summary-card loading" id="mcCardEnvVars">
                <div class="summary-card-label">Environment Variables</div>
                <div class="summary-card-value"><span class="spinner"></span></div>
                <div class="summary-card-detail"></div>
            </div>
        </div>

        <div class="mc-grid">
            <div class="section">
                <div class="section-header"><h3>Recently Modified Components</h3></div>
                <div class="section-body" id="mcRecentList">
                    <div class="loading-state"><span class="spinner"></span> Loading...</div>
                </div>
            </div>
            <div class="section">
                <div class="section-header"><h3>Component Breakdown</h3></div>
                <div class="section-body" id="mcBreakdown">
                    <div class="loading-state"><span class="spinner"></span> Loading...</div>
                </div>
            </div>
        </div>
    </div>

    <!-- ===== Tab: Cloud Flows ===== -->
    <div class="tab-content" id="tab-cloud-flows">
        <div class="section">
            <div class="toolbar">
                <input type="text" class="toolbar-search" id="cfSearch" placeholder="Search flows by name..." />
                <span class="toolbar-count" id="cfCount"></span>
            </div>
            <div class="section-body">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Entity</th>
                            <th>Status</th>
                            <th>Modified On</th>
                            <th style="width:90px;">Action</th>
                        </tr>
                    </thead>
                    <tbody id="cfBody">
                        <tr><td colspan="5"><div class="loading-state"><span class="spinner"></span> Loading cloud flows...</div></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- ===== Tab: Plugin Steps ===== -->
    <div class="tab-content" id="tab-plugin-steps">
        <div class="section">
            <div class="toolbar">
                <input type="text" class="toolbar-search" id="psSearch" placeholder="Search plugin steps..." />
                <select class="toolbar-select" id="psStageFilter">
                    <option value="">All Stages</option>
                    <option value="10">Pre-Validation</option>
                    <option value="20">Pre-Operation</option>
                    <option value="40">Post-Operation</option>
                </select>
                <span class="toolbar-count" id="psCount"></span>
            </div>
            <div class="section-body">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Message</th>
                            <th>Stage</th>
                            <th>Mode</th>
                            <th>Status</th>
                            <th>Rank</th>
                            <th style="width:90px;">Action</th>
                        </tr>
                    </thead>
                    <tbody id="psBody">
                        <tr><td colspan="7"><div class="loading-state"><span class="spinner"></span> Loading plugin steps...</div></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- ===== Tab: Environment Variables ===== -->
    <div class="tab-content" id="tab-env-variables">
        <div class="section">
            <div class="toolbar">
                <input type="text" class="toolbar-search" id="evSearch" placeholder="Search variables..." />
                <span class="toolbar-count" id="evCount"></span>
            </div>
            <div class="section-body">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>Display Name</th>
                            <th>Schema Name</th>
                            <th>Type</th>
                            <th>Default Value</th>
                            <th>Current Value</th>
                            <th>Status</th>
                            <th style="width:140px;">Actions</th>
                        </tr>
                    </thead>
                    <tbody id="evBody">
                        <tr><td colspan="7"><div class="loading-state"><span class="spinner"></span> Loading environment variables...</div></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- ===== Tab: Business Rules ===== -->
    <div class="tab-content" id="tab-business-rules">
        <div class="section">
            <div class="toolbar">
                <input type="text" class="toolbar-search" id="brSearch" placeholder="Search business rules..." />
                <span class="toolbar-count" id="brCount"></span>
            </div>
            <div class="section-body">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Entity</th>
                            <th>Status</th>
                            <th>Modified On</th>
                            <th style="width:90px;">Action</th>
                        </tr>
                    </thead>
                    <tbody id="brBody">
                        <tr><td colspan="5"><div class="loading-state"><span class="spinner"></span> Loading business rules...</div></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <!-- Tier Gate Overlay -->
    <div id="tierGate" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:#f3f4f6;z-index:1000;align-items:center;justify-content:center;">
        <div style="text-align:center;max-width:400px;padding:40px;">
            <div style="font-size:48px;margin-bottom:16px;">&#128274;</div>
            <h2 style="font-size:20px;font-weight:700;color:#1f2937;margin-bottom:8px;">Compass requires Professional or Enterprise</h2>
            <p style="font-size:14px;color:#6b7280;margin-bottom:20px;">Upgrade your plan to access the operations dashboard — manage flows, plugins, environment variables, and business rules.</p>
            <a href="https://www.verseblocks.com/pricing" target="_blank" style="display:inline-block;padding:10px 24px;background:#2563eb;color:white;border-radius:8px;text-decoration:none;font-size:14px;font-weight:600;">View Plans &amp; Upgrade</a>
        </div>
    </div>

<script>
// Analytics - fire and forget
function vbAnalytics(eventType, details) {
    try {
        fetch('/api/data/v9.2/vb_configurations?$select=vb_license_key,vb_org_id&$top=1', {
            headers: { 'Accept': 'application/json', 'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
            credentials: 'include'
        }).then(function(r) { return r.json(); }).then(function(d) {
            if (!d.value || !d.value[0]) return;
            var c = d.value[0];
            fetch('https://cartographer-api.azurewebsites.net/api/analytics/event', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    licenseKey: c.vb_license_key,
                    orgId: c.vb_org_id || '',
                    eventType: eventType,
                    details: JSON.stringify(details || {}),
                    timestamp: new Date().toISOString()
                })
            });
        });
    } catch(e) { /* silent */ }
}
vbAnalytics('page_view', {page: 'compass'});

(function () {
    'use strict';

    // =========================================================================
    // Configuration
    // =========================================================================
    var API_BASE = (function () {
        try {
            if (window.parent && window.parent.Xrm && window.parent.Xrm.Utility) {
                return window.parent.Xrm.Utility.getGlobalContext().getClientUrl() + '/api/data/v9.2/';
            }
        } catch (e) { /* ignore */ }
        return window.location.origin + '/api/data/v9.2/';
    })();

    var HEADERS_GET = {
        'Accept': 'application/json',
        'OData-MaxVersion': '4.0',
        'OData-Version': '4.0',
        'Prefer': 'odata.include-annotations="*"'
    };

    var HEADERS_POST = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'OData-MaxVersion': '4.0',
        'OData-Version': '4.0'
    };

    // Component type labels (matching ComponentDashboard)
    var COMPONENT_TYPE_LABELS = {
        10001001: 'Plugin Step', 10001002: 'Cloud Flow (Automated)', 10001003: 'Cloud Flow (Scheduled)',
        10001004: 'Cloud Flow (Instant)', 10001005: 'Business Rule', 10001006: 'Classic Workflow',
        10001007: 'Custom Action', 10001008: 'Custom API', 10001009: 'JS Web Resource',
        10001010: 'HTML Web Resource', 10001011: 'CSS Web Resource', 10001012: 'Form (Main)',
        10001013: 'Form (Quick Create)', 10001014: 'Form (Quick View)', 10001015: 'View (System)',
        10001016: 'Dashboard', 10001017: 'Chart', 10001018: 'Custom Page',
        10001019: 'Canvas App', 10001020: 'Model-Driven App', 10001021: 'Site Map',
        10001022: 'Security Role', 10001023: 'BPF', 10001024: 'Desktop Flow',
        10001025: 'Connection Reference', 10001026: 'Environment Variable', 10001027: 'Custom Connector',
        10001028: 'Table (Entity)', 10001029: 'Column (Field)', 10001030: 'Relationship',
        10001031: 'Option Set (Global)', 10001032: 'Ribbon/Command Bar', 10001033: 'Other'
    };

    var ENV_VAR_TYPE_LABELS = { 10001000: 'String', 10001001: 'Number', 10001002: 'Boolean',
                                10001003: 'JSON', 10001004: 'Data Source', 10001005: 'Secret' };

    // =========================================================================
    // Utility Functions
    // =========================================================================
    function htmlEncode(str) {
        if (str === null || str === undefined) return '';
        var div = document.createElement('div');
        div.textContent = String(str);
        return div.innerHTML;
    }

    function formatDate(dateStr) {
        if (!dateStr) return '--';
        try {
            var d = new Date(dateStr);
            return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' }) +
                ' ' + d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
        } catch (e) { return dateStr; }
    }

    function truncate(str, max) {
        if (!str) return '';
        return str.length > max ? str.substring(0, max) + '...' : str;
    }

    function friendlyPluginName(rawName) {
        if (!rawName) return '--';
        // Strip common namespace patterns (e.g., "MyCompany.Plugins.OnCreate" -> "On Create")
        var parts = rawName.split('.');
        var name = parts[parts.length - 1];
        // Also strip content after ": " pattern (step config descriptions)
        var colonIdx = name.indexOf(': ');
        if (colonIdx > 0) name = name.substring(0, colonIdx);
        // Add spaces before capitals (camelCase -> Camel Case)
        name = name.replace(/([a-z])([A-Z])/g, '$1 $2');
        name = name.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
        return name;
    }

    function stageLabel(stage) {
        if (stage === 10) return 'Pre-Val';
        if (stage === 20) return 'Pre-Op';
        if (stage === 40) return 'Post-Op';
        return stage != null ? 'Stage ' + stage : '--';
    }

    function modeLabel(mode) {
        if (mode === 0) return 'Sync';
        if (mode === 1) return 'Async';
        return mode != null ? 'Mode ' + mode : '--';
    }

    // =========================================================================
    // API Helpers
    // =========================================================================
    function apiGet(url) {
        return fetch(API_BASE + url, { method: 'GET', headers: HEADERS_GET, credentials: 'include' })
            .then(function (r) {
                if (!r.ok) return r.text().then(function (t) { throw new Error('API ' + r.status + ': ' + t); });
                return r.json();
            });
    }

    function apiPost(url, body) {
        return fetch(API_BASE + url, {
            method: 'POST', headers: HEADERS_POST, credentials: 'include',
            body: JSON.stringify(body || {})
        }).then(function (r) {
            if (!r.ok) return r.text().then(function (t) { throw new Error('API ' + r.status + ': ' + t); });
            var ct = r.headers.get('content-type') || '';
            if (ct.indexOf('application/json') >= 0) return r.json();
            return r.text();
        });
    }

    // =========================================================================
    // Toast Notifications
    // =========================================================================
    var toastContainer = document.getElementById('toastContainer');

    function showToast(message, type) {
        var toast = document.createElement('div');
        toast.className = 'toast ' + (type || 'success');
        var icon = type === 'error' ? '\u2716' : '\u2714';
        toast.innerHTML = '<span class="toast-icon">' + icon + '</span><span>' + htmlEncode(message) + '</span>';
        toastContainer.appendChild(toast);

        setTimeout(function () {
            toast.classList.add('fade-out');
            setTimeout(function () { toast.remove(); }, 300);
        }, 4000);
    }

    // =========================================================================
    // Confirmation Dialog
    // =========================================================================
    var confirmOverlay = document.getElementById('confirmOverlay');
    var confirmTitleEl = document.getElementById('confirmTitle');
    var confirmTextEl = document.getElementById('confirmText');
    var confirmOkBtn = document.getElementById('confirmOk');
    var confirmCancelBtn = document.getElementById('confirmCancel');
    var pendingConfirmCallback = null;

    function showConfirm(title, message, onConfirm) {
        confirmTitleEl.textContent = title;
        confirmTextEl.textContent = message;
        confirmOverlay.style.display = 'flex';
        pendingConfirmCallback = onConfirm;
    }

    function hideConfirm() {
        confirmOverlay.style.display = 'none';
        pendingConfirmCallback = null;
    }

    confirmCancelBtn.addEventListener('click', hideConfirm);
    confirmOkBtn.addEventListener('click', function () {
        var cb = pendingConfirmCallback;
        hideConfirm();
        if (cb) cb();
    });

    // Close confirm on Escape
    document.addEventListener('keydown', function (e) {
        if (e.key === 'Escape' && confirmOverlay.style.display === 'flex') {
            hideConfirm();
        }
    });

    // =========================================================================
    // Tab Navigation
    // =========================================================================
    var tabButtons = document.querySelectorAll('.tab-btn');
    var tabContents = document.querySelectorAll('.tab-content');
    var activeTab = 'mission-control';
    var tabLoadedMap = {};

    tabButtons.forEach(function (btn) {
        btn.addEventListener('click', function () {
            var tabId = btn.getAttribute('data-tab');
            switchTab(tabId);
        });
    });

    function switchTab(tabId) {
        activeTab = tabId;

        tabButtons.forEach(function (b) {
            b.classList.toggle('active', b.getAttribute('data-tab') === tabId);
        });

        tabContents.forEach(function (tc) {
            tc.classList.toggle('active', tc.id === 'tab-' + tabId);
        });

        // Lazy-load tab data on first visit
        if (!tabLoadedMap[tabId]) {
            tabLoadedMap[tabId] = true;
            loadTabData(tabId);
        }
    }

    function loadTabData(tabId) {
        switch (tabId) {
            case 'mission-control': loadMissionControl(); break;
            case 'cloud-flows': loadCloudFlows(); break;
            case 'plugin-steps': loadPluginSteps(); break;
            case 'env-variables': loadEnvVariables(); break;
            case 'business-rules': loadBusinessRules(); break;
        }
    }

    // Global refresh
    document.getElementById('globalRefresh').addEventListener('click', function () {
        tabLoadedMap = {};
        tabLoadedMap[activeTab] = true;
        loadTabData(activeTab);
    });

    // =========================================================================
    // MISSION CONTROL
    // =========================================================================
    function loadMissionControl() {
        loadMCCardFlows();
        loadMCCardBR();
        loadMCCardPlugins();
        loadMCCardEnvVars();
        loadMCRecentComponents();
        loadMCBreakdown();
    }

    function renderSummaryCard(cardId, total, details) {
        var card = document.getElementById(cardId);
        card.classList.remove('loading');
        card.querySelector('.summary-card-value').textContent = total;
        card.querySelector('.summary-card-detail').innerHTML = details;
    }

    function renderSummaryCardError(cardId) {
        var card = document.getElementById(cardId);
        card.classList.remove('loading');
        card.querySelector('.summary-card-value').textContent = '--';
        card.querySelector('.summary-card-detail').innerHTML = '<span style="color:#dc2626;">Failed to load</span>';
    }

    // Card: Cloud Flows
    function loadMCCardFlows() {
        var card = document.getElementById('mcCardFlows');
        card.classList.add('loading');
        card.querySelector('.summary-card-value').innerHTML = '<span class="spinner"></span>';

        apiGet("workflows?$filter=category eq 5&$select=statecode")
            .then(function (data) {
                var records = data.value || [];
                var active = 0, inactive = 0;
                records.forEach(function (r) {
                    if (r.statecode === 1) active++;
                    else inactive++;
                });
                renderSummaryCard('mcCardFlows', records.length,
                    '<span class="active-count">' + active + ' Active</span>' +
                    '<span class="inactive-count">' + inactive + ' Inactive</span>');
            })
            .catch(function () { renderSummaryCardError('mcCardFlows'); });
    }

    // Card: Business Rules
    function loadMCCardBR() {
        var card = document.getElementById('mcCardBR');
        card.classList.add('loading');
        card.querySelector('.summary-card-value').innerHTML = '<span class="spinner"></span>';

        apiGet("workflows?$filter=category eq 2&$select=statecode")
            .then(function (data) {
                var records = data.value || [];
                var active = 0, inactive = 0;
                records.forEach(function (r) {
                    if (r.statecode === 1) active++;
                    else inactive++;
                });
                renderSummaryCard('mcCardBR', records.length,
                    '<span class="active-count">' + active + ' Active</span>' +
                    '<span class="inactive-count">' + inactive + ' Inactive</span>');
            })
            .catch(function () { renderSummaryCardError('mcCardBR'); });
    }

    // Card: Plugin Steps
    function loadMCCardPlugins() {
        var card = document.getElementById('mcCardPlugins');
        card.classList.add('loading');
        card.querySelector('.summary-card-value').innerHTML = '<span class="spinner"></span>';

        apiGet("sdkmessageprocessingsteps?$filter=ishidden/Value eq false&$select=statecode")
            .then(function (data) {
                var records = data.value || [];
                var enabled = 0, disabled = 0;
                records.forEach(function (r) {
                    if (r.statecode === 0) enabled++;
                    else disabled++;
                });
                renderSummaryCard('mcCardPlugins', records.length,
                    '<span class="active-count">' + enabled + ' Enabled</span>' +
                    '<span class="inactive-count">' + disabled + ' Disabled</span>');
            })
            .catch(function () { renderSummaryCardError('mcCardPlugins'); });
    }

    // Card: Environment Variables
    function loadMCCardEnvVars() {
        var card = document.getElementById('mcCardEnvVars');
        card.classList.add('loading');
        card.querySelector('.summary-card-value').innerHTML = '<span class="spinner"></span>';

        Promise.all([
            apiGet("environmentvariabledefinitions?$select=environmentvariabledefinitionid,defaultvalue"),
            apiGet("environmentvariablevalues?$select=_environmentvariabledefinitionid_value,value")
        ]).then(function (results) {
            var defs = results[0].value || [];
            var vals = results[1].value || [];

            // Build lookup: defId -> has value
            var valMap = {};
            vals.forEach(function (v) {
                valMap[v._environmentvariabledefinitionid_value] = true;
            });

            var total = defs.length;
            var set = 0, notSet = 0;
            defs.forEach(function (d) {
                if (valMap[d.environmentvariabledefinitionid] || d.defaultvalue) {
                    set++;
                } else {
                    notSet++;
                }
            });

            renderSummaryCard('mcCardEnvVars', total,
                '<span class="set-count">' + set + ' Set</span>' +
                '<span class="notset-count">' + notSet + ' Not Set</span>');
        }).catch(function () { renderSummaryCardError('mcCardEnvVars'); });
    }

    // Recently Modified Components (from crawled data)
    function loadMCRecentComponents() {
        var container = document.getElementById('mcRecentList');
        container.innerHTML = '<div class="loading-state"><span class="spinner"></span> Loading...</div>';

        apiGet("vb_components?$orderby=vb_source_modified_on desc&$top=10&$select=vb_name,vb_component_type,vb_source_modified_on")
            .then(function (data) {
                var records = data.value || [];
                if (records.length === 0) {
                    container.innerHTML = '<div class="empty-state"><h4>No crawled data yet</h4><p>Run a crawl from the Cartographer app to populate component data.</p></div>';
                    return;
                }

                var html = '<ul class="recent-list">';
                records.forEach(function (r) {
                    var typeLabel = COMPONENT_TYPE_LABELS[r.vb_component_type] || 'Unknown';
                    html += '<li class="recent-item">' +
                        '<span class="name" title="' + htmlEncode(r.vb_name) + '">' + htmlEncode(r.vb_name) + '</span>' +
                        '<span class="meta">' +
                        '<span class="badge badge-type">' + htmlEncode(typeLabel) + '</span>' +
                        '<span>' + formatDate(r.vb_source_modified_on) + '</span>' +
                        '</span></li>';
                });
                html += '</ul>';
                container.innerHTML = html;
            })
            .catch(function (err) {
                container.innerHTML = '<div class="empty-state"><h4>Unable to load components</h4><p>' + htmlEncode(err.message) + '</p></div>';
            });
    }

    // Component Breakdown (from crawled data)
    function loadMCBreakdown() {
        var container = document.getElementById('mcBreakdown');
        container.innerHTML = '<div class="loading-state"><span class="spinner"></span> Loading...</div>';

        apiGet("vb_components?$select=vb_component_type")
            .then(function (data) {
                var records = data.value || [];
                if (records.length === 0) {
                    container.innerHTML = '<div class="empty-state"><h4>No crawled data yet</h4><p>Run a crawl to see the component breakdown.</p></div>';
                    return;
                }

                // Count by type
                var counts = {};
                records.forEach(function (r) {
                    var t = r.vb_component_type;
                    counts[t] = (counts[t] || 0) + 1;
                });

                // Sort by count descending
                var sorted = Object.keys(counts).sort(function (a, b) { return counts[b] - counts[a]; });

                var html = '<div class="breakdown-grid">';
                sorted.forEach(function (key) {
                    var label = COMPONENT_TYPE_LABELS[key] || 'Unknown (' + key + ')';
                    html += '<div class="breakdown-item">' +
                        '<span class="type-name" title="' + htmlEncode(label) + '">' + htmlEncode(label) + '</span>' +
                        '<span class="type-count">' + counts[key] + '</span></div>';
                });
                html += '</div>';
                container.innerHTML = html;
            })
            .catch(function (err) {
                container.innerHTML = '<div class="empty-state"><h4>Unable to load breakdown</h4><p>' + htmlEncode(err.message) + '</p></div>';
            });
    }

    // =========================================================================
    // CLOUD FLOW MONITOR
    // =========================================================================
    var allCloudFlows = [];
    var cfSearchInput = document.getElementById('cfSearch');
    var cfSearchTimer = null;

    cfSearchInput.addEventListener('input', function () {
        clearTimeout(cfSearchTimer);
        cfSearchTimer = setTimeout(function () { renderCloudFlows(); }, 300);
    });

    function loadCloudFlows() {
        var body = document.getElementById('cfBody');
        body.innerHTML = '<tr><td colspan="5"><div class="loading-state"><span class="spinner"></span> Loading cloud flows...</div></td></tr>';

        apiGet("workflows?$filter=category eq 5&$select=name,statecode,statuscode,primaryentity,modifiedon,createdon,workflowid&$orderby=name")
            .then(function (data) {
                allCloudFlows = data.value || [];
                renderCloudFlows();
            })
            .catch(function (err) {
                body.innerHTML = '<tr><td colspan="5"><div class="empty-state"><h4>Failed to load cloud flows</h4><p>' + htmlEncode(err.message) + '</p></div></td></tr>';
            });
    }

    function renderCloudFlows() {
        var body = document.getElementById('cfBody');
        var countEl = document.getElementById('cfCount');
        var search = (cfSearchInput.value || '').trim().toLowerCase();

        var filtered = allCloudFlows;
        if (search) {
            filtered = allCloudFlows.filter(function (f) {
                return (f.name || '').toLowerCase().indexOf(search) >= 0;
            });
        }

        countEl.textContent = filtered.length + ' flow' + (filtered.length !== 1 ? 's' : '');

        if (filtered.length === 0) {
            body.innerHTML = '<tr><td colspan="5"><div class="empty-state"><h4>No flows found</h4><p>' +
                (search ? 'Try a different search term.' : 'No cloud flows in this environment.') + '</p></div></td></tr>';
            return;
        }

        var html = '';
        filtered.forEach(function (flow) {
            var isOn = flow.statecode === 1;
            var statusBadge = isOn
                ? '<span class="badge badge-on">On</span>'
                : '<span class="badge badge-off">Off</span>';
            var toggleClass = isOn ? 'btn-toggle is-on' : 'btn-toggle is-off';
            var toggleLabel = isOn ? 'Disable' : 'Enable';

            html += '<tr>' +
                '<td class="col-name" title="' + htmlEncode(flow.name) + '">' + htmlEncode(flow.name) + '</td>' +
                '<td class="col-mono">' + htmlEncode(flow.primaryentity || 'none') + '</td>' +
                '<td>' + statusBadge + '</td>' +
                '<td class="col-date">' + formatDate(flow.modifiedon) + '</td>' +
                '<td><button class="btn ' + toggleClass + '" data-flowid="' + flow.workflowid + '" data-flowname="' + htmlEncode(flow.name) + '" data-ison="' + isOn + '">' + toggleLabel + '</button></td>' +
                '</tr>';
        });

        body.innerHTML = html;

        // Attach toggle handlers
        body.querySelectorAll('.btn-toggle').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var flowId = btn.getAttribute('data-flowid');
                var flowName = btn.getAttribute('data-flowname');
                var isOn = btn.getAttribute('data-ison') === 'true';
                var action = isOn ? 'disable' : 'enable';

                showConfirm(
                    (isOn ? 'Disable' : 'Enable') + ' Cloud Flow',
                    'Are you sure you want to ' + action + ' \'' + flowName + '\'?',
                    function () { toggleCloudFlow(btn, flowId, flowName, !isOn); }
                );
            });
        });
    }

    function toggleCloudFlow(btn, flowId, flowName, enable) {
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner spinner-sm"></span>';

        apiPost('vb_ToggleComponent', {
            ComponentType: 'CloudFlow',
            ComponentId: flowId,
            Enable: enable
        }).then(function () {
            showToast(flowName + ' ' + (enable ? 'enabled' : 'disabled') + ' successfully.', 'success');
            // Update local data
            allCloudFlows.forEach(function (f) {
                if (f.workflowid === flowId) {
                    f.statecode = enable ? 1 : 0;
                }
            });
            renderCloudFlows();
        }).catch(function (err) {
            showToast('Failed to ' + (enable ? 'enable' : 'disable') + ' ' + flowName + ': ' + err.message, 'error');
            btn.disabled = false;
            btn.textContent = enable ? 'Enable' : 'Disable';
        });
    }

    // =========================================================================
    // PLUGIN STEP MANAGER
    // =========================================================================
    var allPluginSteps = [];
    var psSearchInput = document.getElementById('psSearch');
    var psStageFilter = document.getElementById('psStageFilter');
    var psSearchTimer = null;

    psSearchInput.addEventListener('input', function () {
        clearTimeout(psSearchTimer);
        psSearchTimer = setTimeout(function () { renderPluginSteps(); }, 300);
    });

    psStageFilter.addEventListener('change', function () { renderPluginSteps(); });

    function loadPluginSteps() {
        var body = document.getElementById('psBody');
        body.innerHTML = '<tr><td colspan="7"><div class="loading-state"><span class="spinner"></span> Loading plugin steps...</div></td></tr>';

        apiGet("sdkmessageprocessingsteps?$filter=ishidden/Value eq false&$select=name,stage,mode,statecode,rank,sdkmessageprocessingstepid&$expand=sdkmessageid($select=name)&$orderby=name")
            .then(function (data) {
                allPluginSteps = data.value || [];
                renderPluginSteps();
            })
            .catch(function (err) {
                body.innerHTML = '<tr><td colspan="7"><div class="empty-state"><h4>Failed to load plugin steps</h4><p>' + htmlEncode(err.message) + '</p></div></td></tr>';
            });
    }

    function renderPluginSteps() {
        var body = document.getElementById('psBody');
        var countEl = document.getElementById('psCount');
        var search = (psSearchInput.value || '').trim().toLowerCase();
        var stageVal = psStageFilter.value;

        var filtered = allPluginSteps.filter(function (s) {
            if (search && (s.name || '').toLowerCase().indexOf(search) < 0) return false;
            if (stageVal && String(s.stage) !== stageVal) return false;
            return true;
        });

        countEl.textContent = filtered.length + ' step' + (filtered.length !== 1 ? 's' : '');

        if (filtered.length === 0) {
            body.innerHTML = '<tr><td colspan="7"><div class="empty-state"><h4>No plugin steps found</h4><p>Try adjusting your filters.</p></div></td></tr>';
            return;
        }

        var html = '';
        filtered.forEach(function (step) {
            var isEnabled = step.statecode === 0;
            var statusBadge = isEnabled
                ? '<span class="badge badge-enabled">Enabled</span>'
                : '<span class="badge badge-disabled">Disabled</span>';
            var modeBadge = step.mode === 0
                ? '<span class="badge badge-sync">Sync</span>'
                : '<span class="badge badge-async">Async</span>';
            var toggleClass = isEnabled ? 'btn-toggle is-on' : 'btn-toggle is-off';
            var toggleLabel = isEnabled ? 'Disable' : 'Enable';
            var messageName = (step.sdkmessageid && step.sdkmessageid.name) || '--';
            var friendly = friendlyPluginName(step.name);

            html += '<tr>' +
                '<td class="col-name" title="' + htmlEncode(step.name) + '">' + htmlEncode(friendly) + '</td>' +
                '<td>' + htmlEncode(messageName) + '</td>' +
                '<td><span class="badge badge-stage">' + stageLabel(step.stage) + '</span></td>' +
                '<td>' + modeBadge + '</td>' +
                '<td>' + statusBadge + '</td>' +
                '<td style="text-align:center;">' + (step.rank != null ? step.rank : '--') + '</td>' +
                '<td><button class="btn ' + toggleClass + '" data-stepid="' + step.sdkmessageprocessingstepid + '" data-stepname="' + htmlEncode(friendly) + '" data-isenabled="' + isEnabled + '">' + toggleLabel + '</button></td>' +
                '</tr>';
        });

        body.innerHTML = html;

        // Attach toggle handlers
        body.querySelectorAll('.btn-toggle').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var stepId = btn.getAttribute('data-stepid');
                var stepName = btn.getAttribute('data-stepname');
                var isEnabled = btn.getAttribute('data-isenabled') === 'true';
                var action = isEnabled ? 'disable' : 'enable';

                showConfirm(
                    (isEnabled ? 'Disable' : 'Enable') + ' Plugin Step',
                    'Are you sure you want to ' + action + ' \'' + stepName + '\'?',
                    function () { togglePluginStep(btn, stepId, stepName, !isEnabled); }
                );
            });
        });
    }

    function togglePluginStep(btn, stepId, stepName, enable) {
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner spinner-sm"></span>';

        apiPost('vb_ToggleComponent', {
            ComponentType: 'PluginStep',
            ComponentId: stepId,
            Enable: enable
        }).then(function () {
            showToast(stepName + ' ' + (enable ? 'enabled' : 'disabled') + ' successfully.', 'success');
            allPluginSteps.forEach(function (s) {
                if (s.sdkmessageprocessingstepid === stepId) {
                    s.statecode = enable ? 0 : 1;
                }
            });
            renderPluginSteps();
        }).catch(function (err) {
            showToast('Failed to ' + (enable ? 'enable' : 'disable') + ' ' + stepName + ': ' + err.message, 'error');
            btn.disabled = false;
            btn.textContent = enable ? 'Enable' : 'Disable';
        });
    }

    // =========================================================================
    // ENVIRONMENT VARIABLES
    // =========================================================================
    var allEnvVarDefs = [];
    var envVarValueMap = {};   // defId -> { value, valueId }
    var evSearchInput = document.getElementById('evSearch');
    var evSearchTimer = null;
    var editingEnvVarId = null;

    evSearchInput.addEventListener('input', function () {
        clearTimeout(evSearchTimer);
        evSearchTimer = setTimeout(function () { renderEnvVariables(); }, 300);
    });

    function loadEnvVariables() {
        var body = document.getElementById('evBody');
        body.innerHTML = '<tr><td colspan="7"><div class="loading-state"><span class="spinner"></span> Loading environment variables...</div></td></tr>';

        Promise.all([
            apiGet("environmentvariabledefinitions?$select=schemaname,displayname,type,defaultvalue,environmentvariabledefinitionid&$orderby=displayname"),
            apiGet("environmentvariablevalues?$select=_environmentvariabledefinitionid_value,value,environmentvariablevalueid")
        ]).then(function (results) {
            allEnvVarDefs = results[0].value || [];
            var vals = results[1].value || [];

            envVarValueMap = {};
            vals.forEach(function (v) {
                envVarValueMap[v._environmentvariabledefinitionid_value] = {
                    value: v.value,
                    valueId: v.environmentvariablevalueid
                };
            });

            renderEnvVariables();
        }).catch(function (err) {
            body.innerHTML = '<tr><td colspan="7"><div class="empty-state"><h4>Failed to load environment variables</h4><p>' + htmlEncode(err.message) + '</p></div></td></tr>';
        });
    }

    function renderEnvVariables() {
        var body = document.getElementById('evBody');
        var countEl = document.getElementById('evCount');
        var search = (evSearchInput.value || '').trim().toLowerCase();

        var filtered = allEnvVarDefs.filter(function (d) {
            if (!search) return true;
            return (d.displayname || '').toLowerCase().indexOf(search) >= 0 ||
                   (d.schemaname || '').toLowerCase().indexOf(search) >= 0;
        });

        countEl.textContent = filtered.length + ' variable' + (filtered.length !== 1 ? 's' : '');

        if (filtered.length === 0) {
            body.innerHTML = '<tr><td colspan="7"><div class="empty-state"><h4>No variables found</h4><p>' +
                (search ? 'Try a different search term.' : 'No environment variables in this environment.') + '</p></div></td></tr>';
            return;
        }

        var html = '';
        filtered.forEach(function (def) {
            var id = def.environmentvariabledefinitionid;
            var valEntry = envVarValueMap[id];
            var currentValue = valEntry ? valEntry.value : null;
            var defaultValue = def.defaultvalue;
            var typeLabel = getEnvVarTypeLabel(def.type);
            var isSecret = def.type === 10001005;

            // Determine status
            var statusBadge, statusClass;
            if (currentValue !== null && currentValue !== undefined) {
                statusBadge = '<span class="badge badge-set">Set</span>';
                statusClass = 'set';
            } else if (defaultValue !== null && defaultValue !== undefined && defaultValue !== '') {
                statusBadge = '<span class="badge badge-default">Default</span>';
                statusClass = 'default';
            } else {
                statusBadge = '<span class="badge badge-notset">NOT SET</span>';
                statusClass = 'notset';
            }

            // Display values (mask secrets)
            var displayDefault = defaultValue != null ? truncate(String(defaultValue), 50) : '--';
            var displayCurrent;
            if (isSecret && currentValue != null) {
                displayCurrent = '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022';
            } else {
                displayCurrent = currentValue != null ? truncate(String(currentValue), 50) : '--';
            }

            // Editing state
            var isEditing = editingEnvVarId === id;

            var currentValueCell;
            if (isEditing) {
                var editVal = isSecret ? '' : (currentValue || defaultValue || '');
                currentValueCell = '<td>' +
                    '<div class="inline-edit-group">' +
                    '<input type="' + (isSecret ? 'password' : 'text') + '" class="inline-edit-input" id="ev-edit-input-' + id + '" value="' + htmlEncode(editVal) + '" placeholder="Enter value..." />' +
                    '<button class="btn btn-primary" data-action="save" data-defid="' + id + '" style="padding:5px 10px;">Save</button>' +
                    '<button class="btn" data-action="cancel-edit" style="padding:5px 10px;">Cancel</button>' +
                    '</div></td>';
            } else {
                currentValueCell = '<td title="' + htmlEncode(currentValue || '') + '">' + htmlEncode(displayCurrent) + '</td>';
            }

            // Action buttons
            var actionsHtml = '';
            if (!isEditing) {
                actionsHtml = '<button class="btn" data-action="edit" data-defid="' + id + '">Edit</button>';
                if (valEntry) {
                    actionsHtml += ' <button class="btn btn-danger" data-action="reset" data-defid="' + id + '" data-defname="' + htmlEncode(def.displayname || def.schemaname) + '">Reset</button>';
                }
            }

            html += '<tr>' +
                '<td class="col-name" title="' + htmlEncode(def.displayname) + '">' + htmlEncode(def.displayname || '--') + '</td>' +
                '<td class="col-mono">' + htmlEncode(def.schemaname) + '</td>' +
                '<td><span class="badge badge-type">' + htmlEncode(typeLabel) + '</span></td>' +
                '<td title="' + htmlEncode(defaultValue || '') + '">' + htmlEncode(displayDefault) + '</td>' +
                currentValueCell +
                '<td>' + statusBadge + '</td>' +
                '<td>' + actionsHtml + '</td>' +
                '</tr>';
        });

        body.innerHTML = html;

        // Attach event handlers
        body.querySelectorAll('[data-action="edit"]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                editingEnvVarId = btn.getAttribute('data-defid');
                renderEnvVariables();
                // Focus the input
                var input = document.getElementById('ev-edit-input-' + editingEnvVarId);
                if (input) input.focus();
            });
        });

        body.querySelectorAll('[data-action="cancel-edit"]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                editingEnvVarId = null;
                renderEnvVariables();
            });
        });

        body.querySelectorAll('[data-action="save"]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var defId = btn.getAttribute('data-defid');
                var input = document.getElementById('ev-edit-input-' + defId);
                if (input) saveEnvVariable(btn, defId, input.value);
            });
        });

        body.querySelectorAll('[data-action="reset"]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var defId = btn.getAttribute('data-defid');
                var defName = btn.getAttribute('data-defname');
                showConfirm(
                    'Reset to Default',
                    'Are you sure you want to reset \'' + defName + '\' to its default value? The current override will be removed.',
                    function () { resetEnvVariable(btn, defId, defName); }
                );
            });
        });

        // Handle Enter key in edit inputs
        body.querySelectorAll('.inline-edit-input').forEach(function (input) {
            input.addEventListener('keydown', function (e) {
                if (e.key === 'Enter') {
                    var defId = input.id.replace('ev-edit-input-', '');
                    var saveBtn = body.querySelector('[data-action="save"][data-defid="' + defId + '"]');
                    if (saveBtn) saveBtn.click();
                } else if (e.key === 'Escape') {
                    editingEnvVarId = null;
                    renderEnvVariables();
                }
            });
        });
    }

    function getEnvVarTypeLabel(type) {
        return ENV_VAR_TYPE_LABELS[type] || 'Unknown';
    }

    function saveEnvVariable(btn, defId, newValue) {
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner spinner-sm"></span>';

        apiPost('vb_UpdateEnvVariable', {
            DefinitionId: defId,
            NewValue: newValue
        }).then(function () {
            showToast('Environment variable updated successfully.', 'success');
            editingEnvVarId = null;
            // Update local data
            if (!envVarValueMap[defId]) {
                envVarValueMap[defId] = { value: newValue, valueId: null };
            } else {
                envVarValueMap[defId].value = newValue;
            }
            renderEnvVariables();
        }).catch(function (err) {
            showToast('Failed to update variable: ' + err.message, 'error');
            btn.disabled = false;
            btn.textContent = 'Save';
        });
    }

    function resetEnvVariable(btn, defId, defName) {
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner spinner-sm"></span>';

        apiPost('vb_ResetEnvVariable', {
            DefinitionId: defId
        }).then(function () {
            showToast('\'' + defName + '\' reset to default.', 'success');
            delete envVarValueMap[defId];
            editingEnvVarId = null;
            renderEnvVariables();
        }).catch(function (err) {
            showToast('Failed to reset variable: ' + err.message, 'error');
            btn.disabled = false;
            btn.textContent = 'Reset';
        });
    }

    // =========================================================================
    // BUSINESS RULES
    // =========================================================================
    var allBusinessRules = [];
    var brSearchInput = document.getElementById('brSearch');
    var brSearchTimer = null;

    brSearchInput.addEventListener('input', function () {
        clearTimeout(brSearchTimer);
        brSearchTimer = setTimeout(function () { renderBusinessRules(); }, 300);
    });

    function loadBusinessRules() {
        var body = document.getElementById('brBody');
        body.innerHTML = '<tr><td colspan="5"><div class="loading-state"><span class="spinner"></span> Loading business rules...</div></td></tr>';

        apiGet("workflows?$filter=category eq 2&$select=name,primaryentity,statecode,statuscode,modifiedon,workflowid&$orderby=name")
            .then(function (data) {
                allBusinessRules = data.value || [];
                renderBusinessRules();
            })
            .catch(function (err) {
                body.innerHTML = '<tr><td colspan="5"><div class="empty-state"><h4>Failed to load business rules</h4><p>' + htmlEncode(err.message) + '</p></div></td></tr>';
            });
    }

    function renderBusinessRules() {
        var body = document.getElementById('brBody');
        var countEl = document.getElementById('brCount');
        var search = (brSearchInput.value || '').trim().toLowerCase();

        var filtered = allBusinessRules.filter(function (r) {
            if (!search) return true;
            return (r.name || '').toLowerCase().indexOf(search) >= 0 ||
                   (r.primaryentity || '').toLowerCase().indexOf(search) >= 0;
        });

        countEl.textContent = filtered.length + ' rule' + (filtered.length !== 1 ? 's' : '');

        if (filtered.length === 0) {
            body.innerHTML = '<tr><td colspan="5"><div class="empty-state"><h4>No business rules found</h4><p>' +
                (search ? 'Try a different search term.' : 'No business rules in this environment.') + '</p></div></td></tr>';
            return;
        }

        var html = '';
        filtered.forEach(function (rule) {
            var isActive = rule.statecode === 1;
            var statusBadge = isActive
                ? '<span class="badge badge-active">Active</span>'
                : '<span class="badge badge-inactive">Inactive</span>';
            var toggleClass = isActive ? 'btn-toggle is-on' : 'btn-toggle is-off';
            var toggleLabel = isActive ? 'Deactivate' : 'Activate';

            html += '<tr>' +
                '<td class="col-name" title="' + htmlEncode(rule.name) + '">' + htmlEncode(rule.name) + '</td>' +
                '<td class="col-mono">' + htmlEncode(rule.primaryentity || 'none') + '</td>' +
                '<td>' + statusBadge + '</td>' +
                '<td class="col-date">' + formatDate(rule.modifiedon) + '</td>' +
                '<td><button class="btn ' + toggleClass + '" data-ruleid="' + rule.workflowid + '" data-rulename="' + htmlEncode(rule.name) + '" data-isactive="' + isActive + '">' + toggleLabel + '</button></td>' +
                '</tr>';
        });

        body.innerHTML = html;

        // Attach toggle handlers
        body.querySelectorAll('.btn-toggle').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var ruleId = btn.getAttribute('data-ruleid');
                var ruleName = btn.getAttribute('data-rulename');
                var isActive = btn.getAttribute('data-isactive') === 'true';
                var action = isActive ? 'deactivate' : 'activate';

                showConfirm(
                    (isActive ? 'Deactivate' : 'Activate') + ' Business Rule',
                    'Are you sure you want to ' + action + ' \'' + ruleName + '\'?',
                    function () { toggleBusinessRule(btn, ruleId, ruleName, !isActive); }
                );
            });
        });
    }

    function toggleBusinessRule(btn, ruleId, ruleName, activate) {
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner spinner-sm"></span>';

        apiPost('vb_ToggleComponent', {
            ComponentType: 'BusinessRule',
            ComponentId: ruleId,
            Enable: activate
        }).then(function () {
            showToast(ruleName + ' ' + (activate ? 'activated' : 'deactivated') + ' successfully.', 'success');
            allBusinessRules.forEach(function (r) {
                if (r.workflowid === ruleId) {
                    r.statecode = activate ? 1 : 0;
                }
            });
            renderBusinessRules();
        }).catch(function (err) {
            showToast('Failed to ' + (activate ? 'activate' : 'deactivate') + ' ' + ruleName + ': ' + err.message, 'error');
            btn.disabled = false;
            btn.textContent = activate ? 'Activate' : 'Deactivate';
        });
    }

    // =========================================================================
    // Initialize — check tier first
    // =========================================================================
    apiGet('vb_configurations?$select=vb_license_tier&$top=1').then(function (data) {
        var tier = data.value && data.value[0] ? data.value[0].vb_license_tier : null;
        if (tier !== 10001001 && tier !== 10001002) { // Not Professional or Enterprise
            document.getElementById('tierGate').style.display = 'flex';
            vbAnalytics('feature_gate_hit', {feature: 'compass', tier: 'free'});
            return;
        }
        tabLoadedMap['mission-control'] = true;
        loadMissionControl();
    }).catch(function () {
        // If config can't be read, allow access (fail open for dev)
        tabLoadedMap['mission-control'] = true;
        loadMissionControl();
    });

})();
</script>

</body>
</html>
