<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Manage Solutions — 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;
        }
        .header {
            background: #ffffff; border-bottom: 1px solid #e5e7eb;
            padding: 16px 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.06);
        }
        .header h1 { font-size: 18px; font-weight: 600; color: #111827; }
        .header p { font-size: 13px; color: #6b7280; margin-top: 4px; }
        .content { padding: 24px; flex: 1; max-width: 1200px; width: 100%; margin: 0 auto; }
        .section { background: #fff; border-radius: 8px; border: 1px solid #e5e7eb; margin-bottom: 20px; overflow: hidden; }
        .section-header { padding: 14px 20px; border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }
        .section-header h2 { font-size: 15px; font-weight: 600; }
        .section-body { padding: 0; }
        .license-info { padding: 16px 20px; display: flex; gap: 24px; flex-wrap: wrap; font-size: 13px; }
        .license-info .item { display: flex; flex-direction: column; gap: 2px; }
        .license-info .label { color: #6b7280; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
        .license-info .value { font-weight: 600; font-size: 14px; }
        .badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
        .badge-active { background: #d1fae5; color: #065f46; }
        .badge-pending { background: #fef3c7; color: #92400e; }
        .badge-warn { background: #fee2e2; color: #991b1b; }

        /* Solution list */
        .solution-list { list-style: none; }
        .solution-item {
            display: flex; align-items: center; justify-content: space-between;
            padding: 12px 20px; border-bottom: 1px solid #f3f4f6;
            transition: background 0.15s;
        }
        .solution-item:last-child { border-bottom: none; }
        .solution-item:hover { background: #f9fafb; }
        .solution-item .info { flex: 1; }
        .solution-item .name { font-weight: 600; font-size: 14px; }
        .solution-item .meta { font-size: 12px; color: #6b7280; margin-top: 2px; }
        .solution-item .actions { display: flex; gap: 8px; }
        .empty { padding: 40px 20px; text-align: center; color: #9ca3af; font-size: 14px; }

        /* Add solution */
        .add-section { padding: 16px 20px; border-top: 1px solid #e5e7eb; }
        .add-row { display: flex; gap: 12px; align-items: end; flex-wrap: wrap; }
        .add-row .field { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 200px; }
        .add-row .field label { font-size: 12px; font-weight: 600; color: #6b7280; }
        .add-row select, .add-row input {
            font-family: inherit; font-size: 13px; padding: 7px 12px;
            border: 1px solid #d1d5db; border-radius: 6px; background: #fff;
        }
        .btn {
            font-family: inherit; font-size: 13px; padding: 7px 16px;
            border-radius: 6px; border: none; cursor: pointer; font-weight: 600;
            transition: all 0.15s; white-space: nowrap;
        }
        .btn-primary { background: #2563eb; color: #fff; }
        .btn-primary:hover { background: #1d4ed8; }
        .btn-primary:disabled { background: #93c5fd; cursor: not-allowed; }
        .btn-danger { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
        .btn-danger:hover { background: #fecaca; }
        .btn-crawl { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
        .btn-crawl:hover { background: #a7f3d0; }
        .btn-sm { padding: 5px 12px; font-size: 12px; }

        .error { background: #fef2f2; color: #991b1b; padding: 12px 20px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; display: none; }
        .success { background: #f0fdf4; color: #166534; padding: 12px 20px; border-radius: 6px; margin-bottom: 16px; font-size: 13px; display: none; }
        .spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid #d1d5db; border-top-color: #2563eb; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: middle; margin-right: 6px; }
        @keyframes spin { to { transform: rotate(360deg); } }

        .limit-bar { margin-top: 4px; }
        .limit-bar .bar { height: 6px; background: #e5e7eb; border-radius: 3px; overflow: hidden; width: 120px; }
        .limit-bar .fill { height: 100%; background: #2563eb; border-radius: 3px; transition: width 0.3s; }
        .limit-bar .text { font-size: 11px; color: #6b7280; margin-top: 2px; }

        .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: 1000;
        }
        .confirm-box {
            background: #fff; border-radius: 12px; padding: 24px; max-width: 420px; width: 90%;
            box-shadow: 0 20px 60px rgba(0,0,0,0.2);
        }
        .confirm-box h3 { font-size: 16px; margin-bottom: 8px; }
        .confirm-box p { font-size: 13px; color: #4b5563; margin-bottom: 16px; line-height: 1.5; }
        .confirm-box .warn { color: #991b1b; font-weight: 600; }
        .confirm-actions { display: flex; gap: 8px; justify-content: flex-end; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Manage Crawled Solutions</h1>
        <p>Add solutions to crawl, remove them (deletes all crawled data), or start a crawl.</p>
    </div>

    <div class="content">
        <div id="errorMsg" class="error"></div>
        <div id="successMsg" class="success"></div>

        <!-- License Info -->
        <div class="section">
            <div class="section-header"><h2>License</h2></div>
            <div class="license-info" id="licenseInfo">
                <div class="item"><span class="label">Status</span><span class="value" id="licStatus">Loading...</span></div>
                <div class="item"><span class="label">Tier</span><span class="value" id="licTier">--</span></div>
                <div class="item">
                    <span class="label">Component Limit</span>
                    <span class="value" id="licSolutions">--</span>
                    <div class="limit-bar" id="solutionLimitBar" style="display:none;">
                        <div class="bar"><div class="fill" id="solutionFill"></div></div>
                        <div class="text" id="solutionLimitText"></div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Configured Solutions -->
        <div class="section">
            <div class="section-header">
                <h2>Configured Solutions</h2>
                <span id="solutionCount" style="font-size:12px;color:#6b7280;"></span>
            </div>
            <div class="section-body">
                <ul class="solution-list" id="solutionList">
                    <li class="empty">Loading...</li>
                </ul>
            </div>
            <div class="add-section">
                <div class="add-row">
                    <div class="field">
                        <label>Add Solution</label>
                        <select id="solutionPicker"><option value="">Loading solutions...</option></select>
                    </div>
                    <div class="field" style="flex:0;">
                        <label>Frequency</label>
                        <select id="frequencyPicker">
                            <option value="10001000">Manual Only</option>
                            <option value="10001001">Hourly</option>
                            <option value="10001002">Every 6 Hours</option>
                            <option value="10001003" selected>Daily</option>
                            <option value="10001004">Weekly</option>
                        </select>
                    </div>
                    <button class="btn btn-primary" id="addBtn" disabled>Add Solution</button>
                </div>
            </div>
        </div>
    </div>

        <!-- User Guides -->
        <div class="section" id="userGuidesSection">
            <div class="section-header">
                <h2>User Guides</h2>
                <span id="guideCount" style="font-size:12px;color:#6b7280;"></span>
            </div>
            <div class="section-body">
                <ul class="solution-list" id="guideList">
                    <li class="empty">Loading...</li>
                </ul>
            </div>
        </div>
    </div>

    <!-- Guide Viewer (hidden) -->
    <div id="guideOverlay" class="confirm-overlay" style="display:none;">
        <div style="background:#fff;border-radius:12px;max-width:800px;width:95%;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.2);margin:auto;">
            <div style="padding:16px 20px;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center;">
                <h3 id="guideViewerTitle" style="font-size:16px;margin:0;">User Guide</h3>
                <button onclick="closeGuideViewer()" style="background:none;border:none;font-size:22px;cursor:pointer;color:#6b7280;padding:4px 8px;line-height:1;">&times;</button>
            </div>
            <div id="guideViewerContent" style="flex:1;overflow-y:auto;padding:24px;font-size:14px;line-height:1.7;"></div>
            <div style="padding:12px 20px;border-top:1px solid #e5e7eb;display:flex;gap:8px;justify-content:flex-end;">
                <button class="btn btn-primary" onclick="copyGuideContent()" style="font-size:12px;">Copy Markdown</button>
                <button class="btn" onclick="closeGuideViewer()" style="background:#f3f4f6;font-size:12px;">Close</button>
            </div>
        </div>
    </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" style="background:#f3f4f6;">Cancel</button>
                <button class="btn btn-danger" id="confirmOk">Remove</button>
            </div>
        </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: 'solution_manager'});

        var API_BASE = (function () {
            // Detect Dataverse environment URL
            if (window.parent && window.parent.Xrm && window.parent.Xrm.Utility) {
                return window.parent.Xrm.Utility.getGlobalContext().getClientUrl() + "/api/data/v9.2/";
            }
            var url = window.location.origin;
            return url + "/api/data/v9.2/";
        })();

        var FREQ_LABELS = {
            10001000: "Manual Only", 10001001: "Hourly", 10001002: "Every 6 Hours",
            10001003: "Daily", 10001004: "Weekly"
        };
        var TIER_LABELS = { 10001000: "Free", 10001001: "Professional", 10001002: "Enterprise" };
        var STATUS_LABELS = {
            10001000: "Not Activated", 10001001: "Active", 10001002: "Payment Failed",
            10001003: "Suspended", 10001004: "Expired", 10001005: "Invalid"
        };
        var CRAWL_STATUS_LABELS = { 10001000: "Not Run", 10001001: "Running", 10001002: "Completed", 10001003: "Failed" };

        var COMPONENT_TYPE_LABELS = {
            10001000: "Plugin Steps", 10001001: "Cloud Flows (Automated)", 10001002: "Cloud Flows (Instant)",
            10001003: "Cloud Flows (Scheduled)", 10001004: "Business Rules", 10001005: "Classic Workflows",
            10001006: "Custom Actions", 10001007: "Custom APIs", 10001008: "JS Web Resources",
            10001009: "HTML Web Resources", 10001010: "Forms", 10001011: "Views",
            10001012: "Dashboards", 10001013: "Charts", 10001014: "Tables",
            10001015: "Security Roles", 10001016: "Site Maps", 10001017: "Connection Roles",
            10001018: "Environment Variables", 10001019: "Canvas Apps", 10001020: "Model-Driven Apps",
            10001021: "Other"
        };

        // Order for report grouping
        var COMPONENT_TYPE_ORDER = [
            10001000, 10001001, 10001002, 10001003, 10001004, 10001005,
            10001006, 10001007, 10001008, 10001009, 10001010, 10001011,
            10001012, 10001013, 10001014, 10001015, 10001016, 10001017,
            10001018, 10001019, 10001020, 10001021
        ];

        var SEVERITY_LABELS = { 10001000: "Critical", 10001001: "Warning", 10001002: "Info" };

        var maxComponents = -1; // -1 = unlimited
        var currentTier = null;
        var currentConfigs = [];
        var confirmCallback = null;

        function apiGet(url) {
            return fetch(API_BASE + url, {
                headers: { "Accept": "application/json", "OData-MaxVersion": "4.0", "OData-Version": "4.0",
                           "Prefer": 'odata.include-annotations="*"' },
                credentials: "include"
            }).then(function (r) {
                if (!r.ok) return r.text().then(function (t) { throw new Error("API error (" + r.status + "): " + t); });
                return r.json();
            });
        }

        function apiPost(url, body) {
            return fetch(API_BASE + url, {
                method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json",
                    "OData-MaxVersion": "4.0", "OData-Version": "4.0" },
                credentials: "include", body: JSON.stringify(body)
            }).then(function (r) {
                if (!r.ok) return r.text().then(function (t) { throw new Error("API error (" + r.status + "): " + t); });
                return r;
            });
        }

        function apiDelete(url) {
            return fetch(API_BASE + url, {
                method: "DELETE", headers: { "Accept": "application/json" }, credentials: "include"
            }).then(function (r) {
                if (!r.ok) return r.text().then(function (t) { throw new Error("Delete failed (" + r.status + "): " + t); });
                return r;
            });
        }

        function showError(msg) { var el = document.getElementById("errorMsg"); el.textContent = msg; el.style.display = "block"; setTimeout(function () { el.style.display = "none"; }, 8000); }
        function showSuccess(msg) { var el = document.getElementById("successMsg"); el.textContent = msg; el.style.display = "block"; setTimeout(function () { el.style.display = "none"; }, 5000); }

        function showConfirm(title, text, onConfirm) {
            document.getElementById("confirmTitle").textContent = title;
            document.getElementById("confirmText").innerHTML = text;
            document.getElementById("confirmOverlay").style.display = "flex";
            confirmCallback = onConfirm;
        }

        document.getElementById("confirmCancel").onclick = function () { document.getElementById("confirmOverlay").style.display = "none"; confirmCallback = null; };
        document.getElementById("confirmOk").onclick = function () { document.getElementById("confirmOverlay").style.display = "none"; if (confirmCallback) confirmCallback(); confirmCallback = null; };

        // --- Load license info ---
        function loadLicense() {
            return apiGet("vb_configurations?$select=vb_license_status,vb_license_tier,vb_license_expires_on,vb_openai_api_key,vb_ai_mode&$top=1")
            .then(function (data) {
                if (!data.value || data.value.length === 0) {
                    document.getElementById("licStatus").innerHTML = '<span class="badge badge-warn">Not Configured</span>' +
                        '<div style="margin-top:8px;font-size:13px;color:#6b7280;">' +
                        '<a href="/WebResources/vb_/html/LicenseBilling" style="display:inline-block;padding:6px 16px;background:#2563eb;color:white;border-radius:6px;font-size:12px;font-weight:600;text-decoration:none;margin-right:8px;">Set Up License</a>' +
                        '<a href="https://www.verseblocks.com/pricing" target="_blank" style="color:#2563eb;text-decoration:underline;">View plans</a></div>';
                    return;
                }
                var c = data.value[0];
                var status = c.vb_license_status;
                var tier = c.vb_license_tier;
                currentTier = tier;
                var statusLabel = STATUS_LABELS[status] || "Unknown";
                var badgeClass = status === 10001001 ? "badge-active" : (status === 10001000 ? "badge-pending" : "badge-warn");
                document.getElementById("licStatus").innerHTML = '<span class="badge ' + badgeClass + '">' + statusLabel + '</span>';
                document.getElementById("licTier").textContent = TIER_LABELS[tier] || "--";

                // Check if AI key is configured (BYOK mode)
                var aiMode = c.vb_ai_mode;
                var aiKey = c.vb_openai_api_key;
                if (aiMode === 10001001 && !aiKey) { // BYOK but no key
                    document.getElementById("licStatus").innerHTML +=
                        '<div style="margin-top:8px;padding:8px 12px;background:#fef3c7;border:1px solid #fde68a;border-radius:6px;font-size:12px;color:#92400e;">' +
                        '<strong>AI not configured</strong> — Set up your OpenAI API key to enable AI summaries. ' +
                        '<a href="/WebResources/vb_/html/LicenseBilling" style="color:#92400e;text-decoration:underline;font-weight:600;">Configure AI</a>' +
                        '<br><span style="font-size:11px;color:#a16207;margin-top:2px;display:inline-block;">Need a key? Get one at <a href="https://platform.openai.com/api-keys" target="_blank" style="color:#a16207;">platform.openai.com</a> or <a href="https://portal.azure.com/#create/Microsoft.CognitiveServicesOpenAI" target="_blank" style="color:#a16207;">Azure OpenAI</a></span>' +
                        '</div>';
                }

                // Set limits based on tier
                if (tier === 10001000) { maxComponents = 20; } // Standard
                else if (tier === 10001001) { maxComponents = 200; } // Premium = unlimited
                else if (tier === 10001002) { maxComponents = -1; } // Enterprise
                else { maxComponents = 20; }
            }).catch(function () {
                document.getElementById("licStatus").innerHTML = '<span class="badge badge-warn">Error</span>';
            });
        }

        // --- Load configured solutions ---
        function loadConfigs() {
            apiGet("vb_crawlconfigurations?$select=vb_crawlconfigurationid,vb_name,vb_solution_unique_name,vb_crawl_frequency,vb_last_crawl_status,vb_last_crawl_completed_on,vb_component_count,vb_is_active&$orderby=vb_name")
            .then(function (data) {
                currentConfigs = data.value || [];
                renderConfigs();
                updateSolutionLimit();
            }).catch(function (err) { showError("Failed to load configurations: " + err.message); });
        }

        function renderConfigs() {
            var list = document.getElementById("solutionList");
            document.getElementById("solutionCount").textContent = currentConfigs.length + " solution" + (currentConfigs.length !== 1 ? "s" : "");

            if (currentConfigs.length === 0) {
                list.innerHTML = '<li class="empty">No solutions configured. Add one below.</li>';
                return;
            }

            var html = "";
            currentConfigs.forEach(function (c) {
                var freq = FREQ_LABELS[c.vb_crawl_frequency] || "Manual";
                var crawlStatus = CRAWL_STATUS_LABELS[c.vb_last_crawl_status] || "Not Run";
                var lastCrawl = c.vb_last_crawl_completed_on ? new Date(c.vb_last_crawl_completed_on).toLocaleString() : "Never";
                var compCount = c.vb_component_count || 0;
                var id = c.vb_crawlconfigurationid;

                html += '<li class="solution-item" data-id="' + id + '">' +
                    '<div class="info">' +
                    '<div class="name">' + htmlEncode(c.vb_name || c.vb_solution_unique_name) + '</div>' +
                    '<div class="meta">' + htmlEncode(c.vb_solution_unique_name) + ' &middot; ' + freq + ' &middot; ' + compCount + ' components &middot; Last crawl: ' + lastCrawl + ' (' + crawlStatus + ')</div>' +
                    '</div>' +
                    '<div class="actions">' +
                    '<button class="btn btn-crawl btn-sm" onclick="crawlSolution(\'' + id + '\')">Crawl Now</button>' +
                    '<button class="btn btn-sm" style="background:#e0e7ff;color:#3730a3;border:1px solid #c7d2fe;" onclick="generateAllSummaries(\'' + id + '\', this)">AI Summarize All</button>' +
                    (currentTier && currentTier !== 10001000 ? '<button class="btn btn-sm" style="background:#fef3c7;color:#92400e;border:1px solid #fde68a;" onclick="generateReport(\'' + id + '\', \'' + htmlEncode(c.vb_name || c.vb_solution_unique_name).replace(/'/g, "\\'") + '\')">Report</button>' : '') +
                    '<button class="btn btn-danger btn-sm" onclick="removeSolution(\'' + id + '\', \'' + htmlEncode(c.vb_name || c.vb_solution_unique_name) + '\', ' + compCount + ')">Remove</button>' +
                    '</div></li>';
            });
            list.innerHTML = html;
        }

        function updateSolutionLimit() {
            var el = document.getElementById("licSolutions");
            var bar = document.getElementById("solutionLimitBar");

            // Count actual components, not solutions
            fetch(API_BASE + "vb_components/$count", {
                headers: { "Accept": "text/plain", "OData-MaxVersion": "4.0", "OData-Version": "4.0" },
                credentials: "include"
            }).then(function (r) { return r.text(); }).then(function (count) {
                count = parseInt(count) || 0;
                if (maxComponents === -1) {
                    el.textContent = count + " (Unlimited)";
                    bar.style.display = "none";
                } else {
                    el.textContent = count + " / " + maxComponents;
                    bar.style.display = "block";
                    var pct = Math.min(100, (count / maxComponents) * 100);
                    document.getElementById("solutionFill").style.width = pct + "%";
                    document.getElementById("solutionFill").style.background = pct >= 100 ? "#dc2626" : "#2563eb";
                    document.getElementById("solutionLimitText").textContent = (maxComponents - count) + " remaining";
                }
                refreshAddButton();
            }).catch(function () {
                el.textContent = "-- / " + maxComponents;
            });
        }

        // --- Load available solutions for picker ---
        function loadAvailableSolutions() {
            apiGet("solutions?$select=uniquename,friendlyname,version,ismanaged&$filter=isvisible eq true&$orderby=friendlyname")
            .then(function (data) {
                var picker = document.getElementById("solutionPicker");
                var configuredNames = {};
                currentConfigs.forEach(function (c) { configuredNames[c.vb_solution_unique_name.toLowerCase()] = true; });

                // Filter out: Default, Active, system solutions, and already-configured
                var EXCLUDED = ["default", "active", "msdynce_", "msdyn_", "mspp_"];
                var solutions = (data.value || []).filter(function (s) {
                    var lower = s.uniquename.toLowerCase();
                    if (configuredNames[lower]) return false;
                    if (lower === "default" || lower === "active") return false;
                    return true;
                });

                picker.innerHTML = '<option value="">-- Select a solution --</option>';
                solutions.forEach(function (s) {
                    var opt = document.createElement("option");
                    opt.value = s.uniquename;
                    opt.textContent = s.friendlyname + " (" + s.uniquename + ") v" + s.version + (s.ismanaged ? " [Managed]" : "");
                    picker.appendChild(opt);
                });

                picker.onchange = function () { refreshAddButton(); };
                refreshAddButton();
            }).catch(function (err) {
                document.getElementById("solutionPicker").innerHTML = '<option value="">Error loading solutions</option>';
            });
        }

        // --- Add solution ---
        document.getElementById("addBtn").onclick = function () {
            var picker = document.getElementById("solutionPicker");
            var uniqueName = picker.value;
            if (!uniqueName) return;

            var displayName = picker.options[picker.selectedIndex].textContent.split(" (")[0];
            var frequency = parseInt(document.getElementById("frequencyPicker").value);

            if (maxComponents !== -1 && currentConfigs.length >= maxComponents) {
                showError("Component limit reached (" + maxComponents + "). Upgrade your license or remove an existing solution.");
                return;
            }

            var btn = document.getElementById("addBtn");
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Adding...';

            apiPost("vb_crawlconfigurations", {
                "vb_name": displayName,
                "vb_solution_unique_name": uniqueName,
                "vb_solution_display_name": displayName,
                "vb_crawl_frequency": frequency,
                "vb_is_active": true,
                "vb_last_crawl_status": 10001000
            }).then(function () {
                showSuccess("Added " + displayName + ". Click 'Crawl Now' to discover components.");
                btn.innerHTML = "Add Solution";
                loadConfigs();
                loadAvailableSolutions();
            }).catch(function (err) {
                showError("Failed to add solution: " + err.message);
                btn.innerHTML = "Add Solution";
                btn.disabled = false;
            });
        };

        // --- Remove solution ---
        function removeSolution(id, name, compCount) {
            var msg = "This will permanently delete the <strong>" + htmlEncode(name) + "</strong> configuration and all associated data:" +
                "<br><br><span class='warn'>" + compCount + " components, all AI summaries, diagrams, user guides, and crawl logs will be deleted.</span>" +
                "<br><br>This cannot be undone.";
            showConfirm("Remove " + name + "?", msg, function () {
                apiDelete("vb_crawlconfigurations(" + id + ")").then(function () {
                    showSuccess(name + " removed. All associated data has been deleted.");
                    loadConfigs();
                    loadAvailableSolutions();
                }).catch(function (err) { showError("Failed to remove: " + err.message); });
            });
        }

        // --- Generate AI Summaries for all components in a solution ---
        function generateAllSummaries(crawlConfigId, btn) {
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner" style="width:12px;height:12px;border-width:2px;margin-right:4px;"></span>Summarizing...';

            // Get all components that need summaries (status = Crawled, not yet AI Summarized)
            apiGet("vb_components?$filter=_vb_crawlconfiguration_value eq " + crawlConfigId + " and vb_crawl_status eq 10001001&$select=vb_componentid,vb_name&$top=200")
            .then(function (data) {
                var components = data.value || [];
                if (components.length === 0) {
                    btn.disabled = false;
                    btn.textContent = 'AI Summarize All';
                    showSuccess("All components already have AI summaries.");
                    return;
                }

                var total = components.length;
                var done = 0;
                var failed = 0;

                function processNext() {
                    if (done + failed >= total) {
                        btn.disabled = false;
                        btn.textContent = 'AI Summarize All';
                        showSuccess("AI summaries complete: " + done + " generated, " + failed + " failed out of " + total + ".");
                        loadConfigs();
                        return;
                    }

                    var comp = components[done + failed];
                    btn.innerHTML = '<span class="spinner" style="width:12px;height:12px;border-width:2px;margin-right:4px;"></span>' + (done + failed + 1) + '/' + total;

                    apiPost("vb_GenerateAISummary", { "ComponentId": comp.vb_componentid })
                    .then(function (r) { return r.json(); })
                    .then(function (result) {
                        if (result.Success) { done++; } else { failed++; }
                        processNext();
                    }).catch(function () {
                        failed++;
                        processNext();
                    });
                }

                processNext();
            }).catch(function (err) {
                btn.disabled = false;
                btn.textContent = 'AI Summarize All';
                showError("Failed to load components: " + err.message);
            });
        }

        // --- Crawl solution ---
        function crawlSolution(id) {
            var btn = event.target;
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Crawling...';

            apiPost("vb_StartCrawl", { "CrawlConfigurationId": id })
            .then(function (r) { return r.json(); })
            .then(function (result) {
                btn.innerHTML = "Crawl Now";
                btn.disabled = false;
                if (result.Success) {
                    showSuccess(result.Message);
                    loadConfigs();
                } else {
                    showError("Crawl failed: " + (result.Message || "Unknown error"));
                }
            }).catch(function (err) {
                btn.innerHTML = "Crawl Now";
                btn.disabled = false;
                showError("Crawl error: " + err.message);
            });
        }

        function htmlEncode(str) {
            if (!str) return "";
            var div = document.createElement("div");
            div.textContent = str;
            return div.innerHTML;
        }

        function refreshAddButton() {
            var addBtn = document.getElementById("addBtn");
            var picker = document.getElementById("solutionPicker");
            if (maxComponents !== -1 && currentConfigs.length >= maxComponents) {
                addBtn.disabled = true;
                addBtn.textContent = "Limit Reached";
            } else if (picker.value) {
                addBtn.disabled = false;
                addBtn.textContent = "Add Solution";
            } else {
                addBtn.disabled = true;
                addBtn.textContent = "Add Solution";
            }
        }

        // --- User Guides ---
        var currentGuideMarkdown = '';

        function loadGuides() {
            var guideList = document.getElementById("guideList");
            var guideCount = document.getElementById("guideCount");

            // First check tier — Free can't use guides
            if (currentTier === 10001000) {
                vbAnalytics('feature_gate_hit', {feature: 'user_guide', tier: 'free'});
                guideList.innerHTML = '<li class="empty" style="color:#92400e;">User Guide generation is available on Professional and Enterprise plans.</li>';
                guideCount.textContent = '';
                return;
            }

            // Get all Model-Driven App components (type 10001020) across all crawl configs
            apiGet("vb_components?$filter=vb_component_type eq 10001020 and vb_crawl_status ne 10001004&$select=vb_componentid,vb_name,vb_source_id,_vb_crawlconfiguration_value&$orderby=vb_name")
            .then(function (data) {
                var apps = data.value || [];

                // Also load existing user guides
                return apiGet("vb_userguides?$select=vb_userguideid,vb_app_name,vb_generation_status,vb_guide_generated_on,vb_guide_content,_vb_crawlconfiguration_value&$orderby=vb_app_name")
                .then(function (guideData) {
                    var guides = guideData.value || [];

                    // Build a map of app component ID -> guide
                    var guideMap = {};
                    guides.forEach(function (g) {
                        guideMap[g.vb_app_name] = g;
                    });

                    if (apps.length === 0 && guides.length === 0) {
                        guideList.innerHTML = '<li class="empty">No Model-Driven Apps found. Crawl a solution that contains apps first.</li>';
                        guideCount.textContent = '';
                        return;
                    }

                    guideCount.textContent = guides.length + " guide" + (guides.length !== 1 ? "s" : "") + " / " + apps.length + " app" + (apps.length !== 1 ? "s" : "");

                    var html = "";
                    apps.forEach(function (app) {
                        var guide = guideMap[app.vb_name];
                        var hasGuide = guide && guide.vb_guide_content;
                        var genDate = hasGuide ? new Date(guide.vb_guide_generated_on).toLocaleString() : '';
                        var contentLen = hasGuide ? guide.vb_guide_content.length : 0;

                        html += '<li class="solution-item">' +
                            '<div class="info">' +
                            '<div class="name">' + htmlEncode(app.vb_name) + '</div>' +
                            '<div class="meta">' +
                            (hasGuide ? 'Generated: ' + genDate + ' &middot; ' + contentLen.toLocaleString() + ' chars' : 'No guide generated yet') +
                            '</div></div>' +
                            '<div class="actions">';

                        if (hasGuide) {
                            html += '<button class="btn btn-crawl btn-sm" onclick="viewGuide(\'' + guide.vb_userguideid + '\', \'' + htmlEncode(app.vb_name) + '\')">View</button>';
                            html += '<button class="btn btn-sm" style="background:#e0e7ff;color:#3730a3;border:1px solid #c7d2fe;" onclick="generateGuide(\'' + app._vb_crawlconfiguration_value + '\', \'' + app.vb_componentid + '\', this)">Regenerate</button>';
                        } else {
                            html += '<button class="btn btn-primary btn-sm" onclick="generateGuide(\'' + app._vb_crawlconfiguration_value + '\', \'' + app.vb_componentid + '\', this)">Generate Guide</button>';
                        }

                        html += '</div></li>';
                    });

                    guideList.innerHTML = html;
                });
            }).catch(function (err) {
                guideList.innerHTML = '<li class="empty" style="color:#991b1b;">Error: ' + htmlEncode(err.message) + '</li>';
            });
        }

        function generateGuide(crawlConfigId, appModuleId, btn) {
            btn.disabled = true;
            var origText = btn.textContent;
            btn.innerHTML = '<span class="spinner" style="width:12px;height:12px;border-width:2px;margin-right:4px;"></span>Generating...';

            apiPost("vb_CreateUserGuide", {
                "CrawlConfigurationId": crawlConfigId,
                "AppModuleId": appModuleId
            }).then(function (r) { return r.json(); })
            .then(function (result) {
                btn.disabled = false;
                btn.textContent = origText;
                if (result.Success) {
                    showSuccess("User guide generated successfully!");
                    loadGuides();
                } else {
                    var err = (result.error && result.error.message) || "Generation failed.";
                    showError(err);
                }
            }).catch(function (err) {
                btn.disabled = false;
                btn.textContent = origText;
                showError("Error: " + err.message);
            });
        }

        function viewGuide(guideId, appName) {
            document.getElementById("guideViewerTitle").textContent = appName + " — User Guide";
            document.getElementById("guideViewerContent").innerHTML = '<div style="text-align:center;padding:40px;color:#6b7280;"><span class="spinner"></span> Loading...</div>';
            document.getElementById("guideOverlay").style.display = "flex";

            apiGet("vb_userguides(" + guideId + ")?$select=vb_guide_content")
            .then(function (data) {
                var markdown = data.vb_guide_content || '';
                currentGuideMarkdown = markdown;
                // Simple markdown-to-HTML conversion
                var html = markdownToHtml(markdown);
                document.getElementById("guideViewerContent").innerHTML = '<div style="max-width:700px;margin:0 auto;">' + html + '</div>';
            }).catch(function (err) {
                document.getElementById("guideViewerContent").innerHTML = '<div style="color:#991b1b;padding:20px;">Error loading guide: ' + htmlEncode(err.message) + '</div>';
            });
        }

        function closeGuideViewer() {
            document.getElementById("guideOverlay").style.display = "none";
        }

        function copyGuideContent() {
            if (currentGuideMarkdown) {
                navigator.clipboard.writeText(currentGuideMarkdown).then(function () {
                    showSuccess("Markdown copied to clipboard!");
                });
            }
        }

        // Simple markdown to HTML
        function markdownToHtml(md) {
            if (!md) return '';
            var html = htmlEncode(md);
            // Headers
            html = html.replace(/^### (.+)$/gm, '<h3 style="font-size:16px;font-weight:700;margin:20px 0 8px;color:#1f2937;">$1</h3>');
            html = html.replace(/^## (.+)$/gm, '<h2 style="font-size:18px;font-weight:700;margin:24px 0 10px;color:#111827;border-bottom:1px solid #e5e7eb;padding-bottom:6px;">$1</h2>');
            html = html.replace(/^# (.+)$/gm, '<h1 style="font-size:22px;font-weight:800;margin:0 0 16px;color:#111827;">$1</h1>');
            // Bold
            html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
            // Italic
            html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
            // List items
            html = html.replace(/^- (.+)$/gm, '<li style="margin:4px 0 4px 20px;">$1</li>');
            html = html.replace(/^(\d+)\. (.+)$/gm, '<li style="margin:4px 0 4px 20px;">$2</li>');
            // Paragraphs (double newlines)
            html = html.replace(/\n\n/g, '</p><p style="margin:8px 0;">');
            // Single newlines within paragraphs
            html = html.replace(/\n/g, '<br>');
            return '<p style="margin:8px 0;">' + html + '</p>';
        }

        // --- Generate Solution Report ---
        function generateReport(crawlConfigId, solutionName) {
            if (!currentTier || currentTier === 10001000) {
                showError("Solution Reports are available on Professional and Enterprise plans.");
                vbAnalytics('feature_gate_hit', {feature: 'solution_report', tier: 'free'});
                return;
            }

            vbAnalytics('report_generate', {solution: solutionName});
            showSuccess("Generating report for " + solutionName + "...");

            var componentsPromise = apiGet(
                "vb_components?$filter=_vb_crawlconfiguration_value eq " + crawlConfigId +
                " and vb_crawl_status ne 10001004" +
                "&$select=vb_name,vb_component_type,vb_entity_logical_name,vb_crawl_status,vb_ai_summary,vb_unique_name" +
                "&$orderby=vb_component_type,vb_name&$top=5000"
            );

            var issuesPromise = apiGet(
                "vb_bestpracticeissues?$filter=_vb_crawlconfiguration_value eq " + crawlConfigId +
                "&$select=vb_name,vb_severity,vb_rule_id,vb_component_name,vb_description,vb_recommendation" +
                "&$orderby=vb_severity&$top=5000"
            ).catch(function () { return { value: [] }; }); // gracefully handle if table doesn't exist

            Promise.all([componentsPromise, issuesPromise]).then(function (results) {
                var components = results[0].value || [];
                var issues = results[1].value || [];
                var reportHtml = buildReportHtml(solutionName, components, issues);
                var reportWindow = window.open('', '_blank');
                if (reportWindow) {
                    reportWindow.document.write(reportHtml);
                    reportWindow.document.close();
                } else {
                    showError("Pop-up blocked. Please allow pop-ups for this site and try again.");
                }
            }).catch(function (err) {
                showError("Failed to generate report: " + err.message);
            });
        }

        function buildReportHtml(solutionName, components, issues) {
            var now = new Date();
            var orgUrl = window.location.origin;
            var totalComponents = components.length;
            var withSummary = 0;

            // Group by type
            var grouped = {};
            components.forEach(function (c) {
                var t = c.vb_component_type || 10001021;
                if (!grouped[t]) grouped[t] = [];
                grouped[t].push(c);
                if (c.vb_ai_summary) withSummary++;
            });

            // Count issues by severity
            var criticalCount = 0, warningCount = 0, infoCount = 0;
            issues.forEach(function (i) {
                if (i.vb_severity === 10001000) criticalCount++;
                else if (i.vb_severity === 10001001) warningCount++;
                else infoCount++;
            });

            var html = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Solution Report — ' + esc(solutionName) + '</title>';
            html += '<style>';
            html += 'body { font-family: "Segoe UI", Arial, sans-serif; color: #1a1a1a; max-width: 900px; margin: 0 auto; padding: 40px; background: #fff; }';
            html += 'h1 { font-size: 28px; border-bottom: 2px solid #2563eb; padding-bottom: 8px; margin-bottom: 4px; }';
            html += 'h2 { font-size: 20px; margin-top: 32px; color: #2563eb; }';
            html += 'h3 { font-size: 16px; margin-top: 24px; }';
            html += 'table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 13px; }';
            html += 'th { background: #f3f4f6; text-align: left; padding: 8px 12px; border: 1px solid #e5e7eb; font-weight: 600; }';
            html += 'td { padding: 8px 12px; border: 1px solid #e5e7eb; vertical-align: top; }';
            html += '.badge-critical { color: #dc2626; font-weight: 600; }';
            html += '.badge-warning { color: #f59e0b; font-weight: 600; }';
            html += '.badge-info { color: #2563eb; font-weight: 600; }';
            html += '.meta-line { font-size: 13px; color: #6b7280; margin: 2px 0; }';
            html += '.print-bar { position: fixed; top: 0; left: 0; right: 0; background: #1f2937; color: #fff; padding: 10px 24px; display: flex; justify-content: space-between; align-items: center; z-index: 999; font-size: 14px; }';
            html += '.print-bar button { font-family: inherit; padding: 6px 16px; border-radius: 6px; border: none; cursor: pointer; font-weight: 600; font-size: 13px; }';
            html += '.print-bar .btn-print { background: #2563eb; color: #fff; }';
            html += '.print-bar .btn-close { background: #4b5563; color: #fff; margin-left: 8px; }';
            html += '@media print { .print-bar { display: none !important; } body { padding-top: 0; } }';
            html += '@media screen { body { padding-top: 60px; } }';
            html += '.summary-td { max-width: 400px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px; line-height: 1.5; }';
            html += 'tr:nth-child(even) { background: #fafafa; }';
            html += '.footer { margin-top: 48px; padding-top: 16px; border-top: 1px solid #e5e7eb; font-size: 12px; color: #9ca3af; text-align: center; }';
            html += '</style></head><body>';

            // Print bar
            html += '<div class="print-bar">';
            html += '<span>' + esc(solutionName) + ' — Solution Report</span>';
            html += '<div><button class="btn-print" onclick="window.print()">Print / Save as PDF</button>';
            html += '<button class="btn-close" onclick="window.close()">Close</button></div>';
            html += '</div>';

            // Header
            html += '<h1>' + esc(solutionName) + '</h1>';
            html += '<p class="meta-line">Generated: ' + now.toLocaleString() + '</p>';
            html += '<p class="meta-line">Organization: ' + esc(orgUrl) + '</p>';
            html += '<p class="meta-line">' + totalComponents + ' components &middot; ' + withSummary + ' with AI summaries</p>';

            // Executive Summary
            html += '<h2>Executive Summary</h2>';
            html += '<table><thead><tr><th>Component Type</th><th style="width:80px;text-align:right;">Count</th></tr></thead><tbody>';
            COMPONENT_TYPE_ORDER.forEach(function (typeCode) {
                if (grouped[typeCode]) {
                    html += '<tr><td>' + (COMPONENT_TYPE_LABELS[typeCode] || 'Type ' + typeCode) + '</td>';
                    html += '<td style="text-align:right;">' + grouped[typeCode].length + '</td></tr>';
                }
            });
            // Any types not in our defined order
            Object.keys(grouped).forEach(function (k) {
                var code = parseInt(k);
                if (COMPONENT_TYPE_ORDER.indexOf(code) === -1) {
                    html += '<tr><td>' + (COMPONENT_TYPE_LABELS[code] || 'Type ' + code) + '</td>';
                    html += '<td style="text-align:right;">' + grouped[code].length + '</td></tr>';
                }
            });
            html += '<tr style="font-weight:600;"><td>Total</td><td style="text-align:right;">' + totalComponents + '</td></tr>';
            html += '</tbody></table>';

            html += '<p style="font-size:13px;margin-top:8px;">AI Summaries: ' + withSummary + ' of ' + totalComponents + ' components (' + (totalComponents > 0 ? Math.round((withSummary / totalComponents) * 100) : 0) + '%)</p>';

            if (issues.length > 0) {
                html += '<p style="font-size:13px;">Best Practice Issues: ';
                if (criticalCount > 0) html += '<span class="badge-critical">' + criticalCount + ' critical</span> ';
                if (warningCount > 0) html += '<span class="badge-warning">' + warningCount + ' warning' + (warningCount !== 1 ? 's' : '') + '</span> ';
                if (infoCount > 0) html += '<span class="badge-info">' + infoCount + ' info</span>';
                html += '</p>';
            }

            // Components by Type
            html += '<h2>Components by Type</h2>';
            COMPONENT_TYPE_ORDER.forEach(function (typeCode) {
                if (grouped[typeCode]) {
                    html += buildComponentSection(typeCode, grouped[typeCode]);
                }
            });
            // Any extra types
            Object.keys(grouped).forEach(function (k) {
                var code = parseInt(k);
                if (COMPONENT_TYPE_ORDER.indexOf(code) === -1) {
                    html += buildComponentSection(code, grouped[code]);
                }
            });

            // Best Practice Issues
            if (issues.length > 0) {
                html += '<h2>Best Practice Issues</h2>';
                html += '<table><thead><tr><th style="width:80px;">Severity</th><th style="width:100px;">Rule ID</th><th>Component</th><th>Description</th><th>Recommendation</th></tr></thead><tbody>';
                issues.forEach(function (i) {
                    var sevLabel = SEVERITY_LABELS[i.vb_severity] || 'Info';
                    var sevClass = i.vb_severity === 10001000 ? 'badge-critical' : (i.vb_severity === 10001001 ? 'badge-warning' : 'badge-info');
                    html += '<tr>';
                    html += '<td><span class="' + sevClass + '">' + sevLabel + '</span></td>';
                    html += '<td>' + esc(i.vb_rule_id) + '</td>';
                    html += '<td>' + esc(i.vb_component_name) + '</td>';
                    html += '<td>' + esc(i.vb_description) + '</td>';
                    html += '<td>' + esc(i.vb_recommendation) + '</td>';
                    html += '</tr>';
                });
                html += '</tbody></table>';
            }

            // Footer
            html += '<div class="footer">';
            html += 'Generated by Cartographer &mdash; <a href="https://verseblocks.com" style="color:#9ca3af;">verseblocks.com</a>';
            html += '<br>' + now.toLocaleString();
            html += '</div>';

            html += '</body></html>';
            return html;
        }

        function buildComponentSection(typeCode, comps) {
            var label = COMPONENT_TYPE_LABELS[typeCode] || ('Type ' + typeCode);
            var html = '<h3>' + label + ' &mdash; ' + comps.length + ' component' + (comps.length !== 1 ? 's' : '') + '</h3>';
            html += '<table><thead><tr><th>Name</th><th style="width:140px;">Entity</th><th style="width:80px;">Status</th><th>AI Summary</th></tr></thead><tbody>';
            comps.forEach(function (c) {
                var statusLabel = CRAWL_STATUS_LABELS[c.vb_crawl_status] || 'Unknown';
                html += '<tr>';
                html += '<td>' + esc(c.vb_name || c.vb_unique_name || '(unnamed)') + '</td>';
                html += '<td>' + esc(c.vb_entity_logical_name || '—') + '</td>';
                html += '<td>' + statusLabel + '</td>';
                html += '<td class="summary-td">' + esc(c.vb_ai_summary || '—') + '</td>';
                html += '</tr>';
            });
            html += '</tbody></table>';
            return html;
        }

        function esc(str) {
            if (!str) return '';
            var d = document.createElement('div');
            d.textContent = str;
            return d.innerHTML;
        }

        // --- Init ---
        loadLicense().then(function () {
            loadConfigs();
            loadGuides();
            setTimeout(function () { loadAvailableSolutions(); }, 500);
        });
    </script>
</body>
</html>
