<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Activation — VerseBlocks</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: #fff; 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: 800px; 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; }
        .section-header h2 { font-size: 15px; font-weight: 600; }
        .section-body { padding: 16px 20px; }
        .field { margin-bottom: 14px; }
        .field-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #6b7280; margin-bottom: 3px; }
        .field-value { font-size: 14px; color: #1f2937; }
        .field-row { display: flex; gap: 24px; flex-wrap: wrap; }
        .field-row .field { flex: 1; min-width: 140px; }
        .badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 600; }
        .badge-active { background: #d1fae5; color: #065f46; }
        .badge-pending { background: #fef3c7; color: #92400e; }
        .badge-warn { background: #fee2e2; color: #991b1b; }
        .btn { font-family: inherit; font-size: 13px; padding: 8px 20px; border-radius: 6px; border: none; cursor: pointer; font-weight: 600; transition: all 0.15s; }
        .btn-primary { background: #2563eb; color: #fff; }
        .btn-primary:hover { background: #1d4ed8; }
        .btn-primary:disabled { background: #93c5fd; cursor: not-allowed; }
        .btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }
        .btn-secondary:hover { background: #e5e7eb; }
        .btn-link { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 13px; font-weight: 600; padding: 0; }
        .btn-link:hover { text-decoration: underline; }
        .actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
        .activate-form { display: flex; gap: 10px; align-items: end; flex-wrap: wrap; }
        .activate-form input { font-family: 'Cascadia Code', Consolas, monospace; font-size: 14px; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; width: 340px; letter-spacing: 1px; }
        .activate-form input:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37,99,235,0.15); }
        .usage-bar { margin-top: 6px; }
        .usage-bar .bar { height: 6px; background: #e5e7eb; border-radius: 3px; overflow: hidden; width: 200px; }
        .usage-bar .fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
        .usage-bar .text { font-size: 11px; color: #6b7280; margin-top: 2px; }
        .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); } }
        .divider { border-top: 1px solid #f3f4f6; margin: 16px 0; }
        @media (max-width: 640px) { .activate-form input { width: 100%; } .field-row { flex-direction: column; gap: 0; } }
    </style>
</head>
<body>
    <div class="header">
        <h1>Activation</h1>
        <p>Activate Cartographer and manage how it documents your solutions.</p>
    </div>

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

        <!-- License Status -->
        <div class="section">
            <div class="section-header"><h2>Activation Status</h2></div>
            <div class="section-body" id="licenseBody">
                <div style="text-align:center;padding:20px;color:#6b7280;"><span class="spinner"></span> Loading...</div>
            </div>
        </div>

        <!-- Activate (shown only when not activated) -->
        <div class="section" id="activateSection" style="display:none;">
            <div class="section-header"><h2>Activate Cartographer</h2></div>
            <div class="section-body">
                <p style="font-size:13px;color:#6b7280;margin-bottom:12px;">Enter your name, company, and email to activate -- no license key needed.</p>
                <div class="activate-form" style="flex-direction:column;align-items:stretch;max-width:360px;">
                    <input type="text" id="activateName" placeholder="Your name" style="width:100%;">
                    <input type="text" id="activateCompany" placeholder="Company" style="width:100%;margin-top:8px;">
                    <input type="email" id="activateEmail" placeholder="Email address" style="width:100%;margin-top:8px;">
                    <button class="btn btn-primary" id="activateBtn" style="margin-top:10px;align-self:flex-start;">Activate</button>
                </div>
                <p style="font-size:12px;color:#9ca3af;margin-top:12px;">
                    <a href="#" id="legacyKeyToggle" onclick="toggleLegacyKey(event)" style="color:#6b7280;text-decoration:underline;">Have an existing license key?</a>
                </p>
                <div id="legacyKeySection" style="display:none;margin-top:10px;">
                    <div class="activate-form">
                        <input type="text" id="keyInput" placeholder="CARTO-XXXXX-XXXXX-XXXXX-XXXXX" maxlength="29" spellcheck="false" autocomplete="off">
                        <button class="btn btn-secondary" id="legacyActivateBtn">Activate with key</button>
                    </div>
                </div>
            </div>
        </div>

        <!-- Scheduled crawling readiness (shown when active) -->
        <div class="section" id="scheduleSection" style="display:none;">
            <div class="section-header"><h2>Scheduled Crawling</h2></div>
            <div class="section-body" id="scheduleBody"></div>
        </div>

        <!-- Usage (shown when active) -->
        <div class="section" id="usageSection" style="display:none;">
            <div class="section-header"><h2>Usage</h2></div>
            <div class="section-body" id="usageBody"></div>
        </div>

        <!-- AI Configuration (shown when active) -->
        <div class="section" id="aiSection" style="display:none;">
            <div class="section-header"><h2>AI Configuration</h2></div>
            <div class="section-body" id="aiBody">
                <p style="font-size:13px;color:#6b7280;margin-bottom:12px;">Choose how Cartographer generates component summaries and user guides. Summaries always start from a deterministic template -- AI only enriches them when a mode other than None is selected.</p>
                <div style="display:flex;flex-direction:column;gap:10px;max-width:480px;">
                    <div>
                        <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">AI Mode</label>
                        <select id="aiMode" style="margin-top:4px;width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;background:#fff;">
                            <option value="10001003">None (template only)</option>
                            <option value="10001002">AI Builder (Dataverse)</option>
                            <option value="10001001">Bring Your Own Key (OpenAI / Azure OpenAI)</option>
                        </select>
                    </div>

                    <div id="aiBuilderFields" style="display:none;">
                        <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">AI Builder Prompt Name</label>
                        <input id="aiBuilderPromptName" type="text" placeholder="Leave blank to use the prebuilt summarization model" style="margin-top:4px;width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;">
                        <p style="font-size:11px;color:#9ca3af;margin-top:2px;">The name of an Active AI Builder prompt in this environment. Optional -- leave blank to use Dataverse's prebuilt AISummarize model.</p>
                    </div>

                    <div id="byokFields" style="display:none;flex-direction:column;gap:10px;">
                        <div>
                            <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">Provider</label>
                            <select id="aiProvider" style="margin-top:4px;width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;background:#fff;">
                                <option value="openai">OpenAI</option>
                                <option value="azure">Azure OpenAI</option>
                            </select>
                        </div>
                        <div>
                            <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">API Key</label>
                            <div style="display:flex;gap:6px;margin-top:4px;">
                                <input id="aiKey" type="password" placeholder="sk-... or Azure key" style="flex:1;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;font-family:monospace;">
                                <button onclick="toggleAiKeyVisibility()" id="aiKeyToggle" style="padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;background:#f9fafb;font-size:12px;cursor:pointer;">Show</button>
                            </div>
                        </div>
                        <div id="aiEndpointRow">
                            <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">Endpoint</label>
                            <input id="aiEndpoint" type="text" placeholder="https://your-resource.openai.azure.com/openai/deployments/gpt-4o-mini" style="margin-top:4px;width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;font-family:monospace;">
                            <p style="font-size:11px;color:#9ca3af;margin-top:2px;">For Azure OpenAI: include the full deployment path. For OpenAI: leave blank (uses default).</p>
                        </div>
                        <div>
                            <label style="font-size:11px;font-weight:600;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;">Model</label>
                            <input id="aiModel" type="text" placeholder="gpt-4o-mini" value="gpt-4o-mini" style="margin-top:4px;width:100%;padding:8px 12px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;">
                        </div>
                    </div>

                    <div style="display:flex;gap:8px;margin-top:4px;">
                        <button class="btn btn-primary" onclick="saveAiConfig()" id="aiSaveBtn">Save AI Configuration</button>
                        <button class="btn btn-secondary" onclick="testAiConfig()" id="aiTestBtn">Test Connection</button>
                    </div>
                    <div id="aiStatus" style="display:none;padding:8px 12px;border-radius:6px;font-size:13px;margin-top:4px;"></div>
                </div>
            </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: 'license_billing'});

        var API_BASE = '/api/data/v9.2/';

        var STATUS_LABELS = { 10001000: 'Not Activated', 10001001: 'Active', 10001002: 'Payment Failed', 10001003: 'Suspended', 10001004: 'Expired', 10001005: 'Invalid' };
        var STATUS_CLASSES = { 10001000: 'badge-pending', 10001001: 'badge-active', 10001002: 'badge-warn', 10001003: 'badge-warn', 10001004: 'badge-warn', 10001005: 'badge-warn' };
        var ACTIVE_STATUS = 10001001;
        // AI enrichment is intentionally NOT exposed in the product yet.
        //
        // The AI Builder and BYOK code paths are complete and tested and stay in the plugins, but
        // neither is offered in the UI: AI Builder output quality is unproven (this tenant has no
        // credits assigned) and BYOK asks a customer for an API key. Cartographer ships as
        // template-only, which needs no credits, no keys and no external calls. Flip this to true
        // to surface the AI Configuration panel again -- nothing else needs to change.
        var AI_MODES_ENABLED = false;

        var AI_MODE = { BYOK: 10001001, AI_BUILDER: 10001002, NONE: 10001003 };

        // Known raw error codes returned by the license API, mapped to plain-English sentences.
        // Anything not in this map either passes through as-is (already a human sentence) or gets
        // a generic fallback that still surfaces the code for support purposes.
        var LICENSE_ERROR_MESSAGES = {
            INVALID_KEY: "That license key isn't valid. Double-check it and try again.",
            KEY_ALREADY_BOUND: 'That license key is already active on a different environment.',
            LICENSE_EXPIRED: 'That license has expired.',
            LICENSE_SUSPENDED: 'That license has been suspended.',
            LICENSE_REVOKED: 'That license has been revoked.',
            LICENSE_INACTIVE: 'That license is not currently active.'
        };
        function friendlyLicenseError(raw, fallback) {
            var text = (raw || '').trim();
            if (LICENSE_ERROR_MESSAGES[text]) return LICENSE_ERROR_MESSAGES[text];
            if (text && /^[A-Z][A-Z0-9_]*$/.test(text)) {
                return 'Activation failed (' + text + '). Please try again or contact support.';
            }
            return text || fallback || 'Something went wrong. Please try again.';
        }

        var licenseKey = null;
        var licenseActionInFlight = false;

        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); }

        // Both activation paths (name/company/email and legacy key) write the same configuration
        // row -- keep only one in flight at a time and give the clicked button a clear busy state
        // for the whole ~24s call.
        function setLicenseButtonsBusy(busy) {
            var a = document.getElementById('activateBtn');
            var l = document.getElementById('legacyActivateBtn');
            if (a) a.disabled = busy;
            if (l) l.disabled = busy;
        }

        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(t); }); return r.json(); });
        }

        function formatDate(d) { return d ? new Date(d).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) : '—'; }

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

        // --- Scheduled crawling readiness -------------------------------------------------
        //
        // Two things have to be true before a crawl schedule does anything: the connection
        // reference the flows use must be bound to a connection, and the flows must be on. Only an
        // administrator can do either, and nothing else in the product reports on them, so a
        // half-finished setup looks exactly like a working one until someone notices no crawl has
        // run.
        var SCHEDULE_FLOW_NAMES = ["Cartographer - Scheduled Crawl Processor", "Cartographer - On-Demand Crawl"];
        var CONNECTION_REFERENCE = "vb_CartographerDataverse";

        function scheduleRow(ok, label, detail) {
            var colour = ok ? "#065f46" : "#92400e";
            var bg = ok ? "#d1fae5" : "#fef3c7";
            var mark = ok ? "&#10003;" : "!";
            return '<div style="display:flex;gap:10px;align-items:flex-start;padding:8px 0;">' +
                '<span style="flex:0 0 18px;height:18px;border-radius:50%;background:' + bg + ';color:' + colour +
                ';font-size:11px;font-weight:700;text-align:center;line-height:18px;">' + mark + '</span>' +
                '<div><div style="font-size:13px;color:#1f2937;">' + htmlEncode(label) + '</div>' +
                (detail ? '<div style="font-size:12px;color:#6b7280;margin-top:2px;">' + htmlEncode(detail) + '</div>' : '') +
                '</div></div>';
        }

        // A failed probe must say so somewhere. Both callers previously swallowed the rejection,
        // and the panel then blamed the user's security role -- a cause it had no evidence for.
        function logScheduleProbeFailure(what, err) {
            if (window.console && console.warn) {
                console.warn("Cartographer: scheduled-crawl probe failed reading " + what, err);
            }
        }

        function loadScheduleStatus() {
            var section = document.getElementById("scheduleSection");
            var body = document.getElementById("scheduleBody");
            if (!section || !body) return;
            section.style.display = "block";
            body.innerHTML = '<p style="font-size:13px;color:#6b7280;">Checking\u2026</p>';

            var flowFilter = SCHEDULE_FLOW_NAMES.map(function (n) {
                return "name eq '" + n.replace(/'/g, "''") + "'";
            }).join(" or ");

            Promise.all([
                apiGet("connectionreferences?$select=connectionreferencelogicalname,connectionid&$filter=connectionreferencelogicalname eq '" + CONNECTION_REFERENCE + "'")
                    .catch(function (err) { logScheduleProbeFailure("connection reference", err); return null; }),
                apiGet("workflows?$select=name,statecode&$filter=" + encodeURIComponent(flowFilter))
                    .catch(function (err) { logScheduleProbeFailure("cloud flows", err); return null; })
            ]).then(function (results) {
                var refData = results[0], flowData = results[1];
                if (!refData && !flowData) {
                    // Say what happened, not why. The previous wording asserted a privilege
                    // problem as fact; when a defect in this page made both probes fail, it told
                    // a System Administrator their security role was inadequate and sent them to
                    // IT for a problem that did not exist. Restricted privileges are ONE possible
                    // cause among several, and this panel cannot tell which from here.
                    body.innerHTML = '<p style="font-size:13px;color:#6b7280;">Cartographer could not read the ' +
                        'scheduled-crawl setup, so it cannot confirm whether scheduled crawling is ready. ' +
                        'Crawl Now is unaffected. If you are not an administrator, your security role may not ' +
                        'permit reading flows or connection references; otherwise check the browser console ' +
                        'for the underlying error.</p>';
                    return;
                }

                var ref = refData && refData.value && refData.value[0];
                var bound = !!(ref && ref.connectionid);
                var flows = (flowData && flowData.value) || [];
                var on = flows.filter(function (f) { return f.statecode === 1; }).length;
                var total = SCHEDULE_FLOW_NAMES.length;
                var ready = bound && on === total;

                var html = scheduleRow(bound, "Dataverse connection",
                    bound ? "The Cartographer Dataverse Connection reference is bound."
                          : "Not bound yet. Open the Cartographer solution, select Cartographer Dataverse Connection, and choose a connection.");
                // Name the flows that are actually off. Listing both when one is already on
                // reads as though the user has done nothing.
                var offNames = SCHEDULE_FLOW_NAMES.filter(function (n) {
                    var match = flows.filter(function (f) { return f.name === n; })[0];
                    return !match || match.statecode !== 1;
                });
                html += scheduleRow(on === total, "Crawl flows",
                    on === total ? "Both cloud flows are on."
                                 : on + " of " + total + " on. Turn on " + offNames.join(" and ") + ".");

                html += ready
                    ? '<p style="font-size:12px;color:#065f46;margin-top:10px;">Scheduled crawling is ready. ' +
                      'Set a Crawl Frequency on a solution in Solution Manager and it will run on its own.</p>'
                    : '<p style="font-size:12px;color:#92400e;margin-top:10px;">Until both are done, a Crawl Frequency ' +
                      'set in Solution Manager will not run. Crawl Now still works.</p>';
                body.innerHTML = html;
            });
        }

        function loadLicense() {
            apiGet("vb_configurations?$select=vb_configurationid,vb_license_key,vb_license_status,vb_license_validated_on,vb_ai_mode,vb_org_id,vb_openai_api_key,vb_openai_endpoint,vb_openai_model,vb_ai_builder_prompt_name&$top=1")
            .then(function (data) {
                if (!data.value || data.value.length === 0) {
                    document.getElementById('licenseBody').innerHTML =
                        '<div style="padding:16px 0;">' +
                        '<p style="color:#1f2937;font-size:15px;font-weight:600;margin-bottom:4px;">Welcome to Cartographer</p>' +
                        '<p style="color:#6b7280;font-size:13px;margin-bottom:16px;">Activate with your name, company, and email below -- no license key needed.</p>' +
                        '</div>';
                    document.getElementById('activateSection').style.display = 'block';
                    return;
                }

                var c = data.value[0];
                licenseKey = c.vb_license_key;
                var status = c.vb_license_status;
                var statusLabel = STATUS_LABELS[status] || 'Unknown';
                var statusClass = STATUS_CLASSES[status] || 'badge-pending';

                var html = '';

                // Status row -- there's no tier or expiry to show anymore (Cartographer is free
                // and activation never expires), and AI mode is intentionally not user-facing.
                html += '<div class="field-row">';
                html += '<div class="field"><div class="field-label">Status</div><div class="field-value"><span class="badge ' + statusClass + '">' + htmlEncode(statusLabel) + '</span></div></div>';
                html += '<div class="field"><div class="field-label">Last Validated</div><div class="field-value">' + formatDate(c.vb_license_validated_on) + '</div></div>';
                html += '<div class="field"><div class="field-label">Org ID</div><div class="field-value" style="font-family:monospace;font-size:12px;">' + htmlEncode(c.vb_org_id || '—') + '</div></div>';
                html += '</div>';

                // Add refresh button
                html += '<div style="margin-top:12px;"><button class="btn btn-secondary" onclick="refreshLicense()" id="refreshBtn" style="font-size:12px;padding:6px 14px;">Refresh License</button></div>';

                document.getElementById('licenseBody').innerHTML = html;

                // Show/hide sections based on status. The activation form must reappear for ANY
                // non-Active status (Not Activated, Invalid, Expired, Suspended, Payment Failed) --
                // otherwise a customer whose key ends up Invalid (e.g. a failed re-activation
                // attempt) has no way back in.
                if (status !== ACTIVE_STATUS) {
                    document.getElementById('activateSection').style.display = 'block';
                } else {
                    document.getElementById('activateSection').style.display = 'none';

                    // Load AI configuration
                    loadAiConfig(c);

                    // Usage section — count components and solutions
                    var usageHtml = '';

                    // Count components
                    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) {
                        var compCount = parseInt(count) || 0;

                        // Count solutions
                        return fetch(API_BASE + 'vb_crawlconfigurations/$count', {
                            headers: {'Accept':'text/plain','OData-MaxVersion':'4.0','OData-Version':'4.0'},
                            credentials: 'include'
                        }).then(function(r2) { return r2.text(); }).then(function(solCount) {
                            var solutions = parseInt(solCount) || 0;

                            usageHtml += '<div class="field-row">';
                            usageHtml += '<div class="field"><div class="field-label">Solutions Crawled</div><div class="field-value">' + solutions + '</div></div>';
                            usageHtml += '<div class="field"><div class="field-label">Components</div><div class="field-value">' + compCount.toLocaleString() + '</div></div>';
                            usageHtml += '</div>';

                            document.getElementById('usageBody').innerHTML = usageHtml;
                        });
                    }).catch(function() {
                        document.getElementById('usageBody').innerHTML = '<p style="color:#6b7280;font-size:13px;">Unable to load usage data.</p>';
                    });

                    document.getElementById('usageSection').style.display = 'block';
                    // Only worth showing once the product is activated -- before that, the
                    // scheduled-crawl setup is not the reader's next problem.
                    loadScheduleStatus();
                }
            }).catch(function (err) {
                document.getElementById('licenseBody').innerHTML = '<p style="color:#991b1b;">Error loading license: ' + htmlEncode(err.message) + '</p>';
            });
        }

        // Show/hide the legacy pasted-key section
        function toggleLegacyKey(evt) {
            if (evt && evt.preventDefault) evt.preventDefault();
            var section = document.getElementById('legacyKeySection');
            section.style.display = section.style.display === 'none' ? 'block' : 'none';
        }

        // Activate -- name + company + email only, no license key shown to or typed by the
        // customer. Calls vb_ActivateFree, the same custom API the Configuration form's
        // VB.Activation.onSave uses (see docs/free-activation.md).
        document.getElementById('activateBtn').addEventListener('click', function () {
            if (licenseActionInFlight) return;

            var name = document.getElementById('activateName').value.trim();
            var company = document.getElementById('activateCompany').value.trim();
            var email = document.getElementById('activateEmail').value.trim();

            if (!name) { showError('Please enter your name.'); return; }
            if (!company) { showError('Please enter your company name.'); return; }
            if (!email || email.indexOf('@') === -1) { showError('Please enter a valid email address.'); return; }

            licenseActionInFlight = true;
            var btn = document.getElementById('activateBtn');
            setLicenseButtonsBusy(true);
            btn.innerHTML = '<span class="spinner"></span>Activating... this can take up to 30 seconds.';

            fetch(API_BASE + 'vb_ActivateFree', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                credentials: 'include',
                body: JSON.stringify({ Name: name, Company: company, Email: email, OrgUrl: window.location.origin })
            }).then(function (r) {
                if (!r.ok) return r.text().then(function (t) { throw new Error(t); });
                return r.json();
            }).then(function (result) {
                licenseActionInFlight = false;
                setLicenseButtonsBusy(false);
                btn.textContent = 'Activate';
                if (result.Valid) {
                    showSuccess(result.Message || 'Cartographer activated.');
                    loadLicense();
                } else {
                    showError(friendlyLicenseError(result.Message, 'Activation failed.'));
                }
            }).catch(function (err) {
                licenseActionInFlight = false;
                setLicenseButtonsBusy(false);
                btn.textContent = 'Activate';
                showError(friendlyLicenseError(err.message, 'Activation failed.'));
            });
        });

        // Legacy pasted-key activation, kept for support / pre-existing paid keys -- see
        // docs/free-activation.md, "How legacy keys keep working".
        document.getElementById('legacyActivateBtn').addEventListener('click', function () {
            if (licenseActionInFlight) return;

            var key = document.getElementById('keyInput').value.trim().toUpperCase();
            if (!key || !key.startsWith('CARTO-') || key.length < 29) {
                showError('Please enter a valid license key (format: CARTO-XXXXX-XXXXX-XXXXX-XXXXX)');
                return;
            }

            licenseActionInFlight = true;
            var btn = document.getElementById('legacyActivateBtn');
            setLicenseButtonsBusy(true);
            btn.innerHTML = '<span class="spinner"></span>Activating... this can take up to 30 seconds.';

            fetch(API_BASE + 'vb_ValidateLicense', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                credentials: 'include',
                body: JSON.stringify({ LicenseKey: key })
            }).then(function (r) { return r.json(); })
            .then(function (result) {
                licenseActionInFlight = false;
                setLicenseButtonsBusy(false);
                btn.textContent = 'Activate with key';
                if (result.Valid) {
                    showSuccess('License activated.');
                    loadLicense();
                } else {
                    showError(friendlyLicenseError(result.Message, 'Activation failed.'));
                }
            }).catch(function (err) {
                licenseActionInFlight = false;
                setLicenseButtonsBusy(false);
                btn.textContent = 'Activate with key';
                showError(friendlyLicenseError(err.message, 'Activation failed.'));
            });
        });

        // --- AI Configuration ---
        var configId = null;

        function loadAiConfig(config) {
            configId = config.vb_configurationid;
            var key = config.vb_openai_api_key || '';
            var endpoint = config.vb_openai_endpoint || '';
            var model = config.vb_openai_model || 'gpt-4o-mini';
            var promptName = config.vb_ai_builder_prompt_name || '';
            var aiMode = config.vb_ai_mode;
            // The legacy "Hosted AI" mode has no runtime behavior left (the hosted proxy is gone)
            // -- display and treat it as None, same as the plugin-side mapping does.
            if (!aiMode || aiMode === 10001000) aiMode = AI_MODE.NONE;

            // Hidden unless AI_MODES_ENABLED. The value is still read and applied below so an
            // environment already configured for AI keeps working exactly as it is.
            document.getElementById('aiSection').style.display = AI_MODES_ENABLED ? 'block' : 'none';
            document.getElementById('aiMode').value = String(aiMode);
            document.getElementById('aiKey').value = key;
            document.getElementById('aiEndpoint').value = endpoint;
            document.getElementById('aiModel').value = model;
            document.getElementById('aiBuilderPromptName').value = promptName;

            // Auto-detect provider from a saved endpoint
            if (endpoint && (endpoint.includes('openai.azure.com') || endpoint.includes('cognitiveservices.azure.com'))) {
                document.getElementById('aiProvider').value = 'azure';
            } else {
                document.getElementById('aiProvider').value = 'openai';
            }
            updateAiModeVisibility();
        }

        function updateEndpointVisibility() {
            var provider = document.getElementById('aiProvider').value;
            document.getElementById('aiEndpointRow').style.display = provider === 'azure' ? 'block' : 'block';
            if (provider === 'openai') {
                document.getElementById('aiEndpoint').placeholder = 'https://api.openai.com/v1 (leave blank for default)';
            } else {
                document.getElementById('aiEndpoint').placeholder = 'https://your-resource.openai.azure.com/openai/deployments/gpt-4o-mini';
            }
        }

        // Shows only the fields relevant to the selected AI Mode: the OpenAI/Azure fields for
        // Bring Your Own Key, the prompt-name field for AI Builder, neither for None.
        function updateAiModeVisibility() {
            var mode = parseInt(document.getElementById('aiMode').value, 10);
            var byok = document.getElementById('byokFields');
            var builder = document.getElementById('aiBuilderFields');
            byok.style.display = (mode === AI_MODE.BYOK) ? 'flex' : 'none';
            builder.style.display = (mode === AI_MODE.AI_BUILDER) ? 'block' : 'none';
            if (mode === AI_MODE.BYOK) updateEndpointVisibility();
        }

        function toggleAiKeyVisibility() {
            var input = document.getElementById('aiKey');
            var btn = document.getElementById('aiKeyToggle');
            if (input.type === 'password') { input.type = 'text'; btn.textContent = 'Hide'; }
            else { input.type = 'password'; btn.textContent = 'Show'; }
        }

        function saveAiConfig() {
            if (!configId) { showError('No configuration record found.'); return; }

            var mode = parseInt(document.getElementById('aiMode').value, 10);
            var payload = { vb_ai_mode: mode };

            if (mode === AI_MODE.BYOK) {
                var key = document.getElementById('aiKey').value.trim();
                var endpoint = document.getElementById('aiEndpoint').value.trim();
                var model = document.getElementById('aiModel').value.trim() || 'gpt-4o-mini';
                if (!key) { showError('API key is required for Bring Your Own Key mode.'); return; }
                payload.vb_openai_api_key = key;
                payload.vb_openai_endpoint = endpoint;
                payload.vb_openai_model = model;
            } else if (mode === AI_MODE.AI_BUILDER) {
                payload.vb_ai_builder_prompt_name = document.getElementById('aiBuilderPromptName').value.trim();
            }

            var btn = document.getElementById('aiSaveBtn');
            btn.disabled = true;
            btn.textContent = 'Saving...';

            fetch(API_BASE + 'vb_configurations(' + configId + ')', {
                method: 'PATCH',
                headers: { 'Content-Type': 'application/json', 'Accept': 'application/json',
                           'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                credentials: 'include',
                body: JSON.stringify(payload)
            }).then(function (r) {
                btn.disabled = false;
                btn.textContent = 'Save AI Configuration';
                if (r.ok) {
                    showSuccess('AI configuration saved.');
                } else {
                    return r.text().then(function (t) { throw new Error(t); });
                }
            }).catch(function (err) {
                btn.disabled = false;
                btn.textContent = 'Save AI Configuration';
                showError('Failed to save: ' + err.message);
            });
        }

        function testAiConfig() {
            if (!configId) { showError('Save your configuration first.'); return; }

            var btn = document.getElementById('aiTestBtn');
            var status = document.getElementById('aiStatus');
            btn.disabled = true;
            btn.textContent = 'Testing...';
            status.style.display = 'none';

            // Pick a simple component to test with
            fetch(API_BASE + 'vb_components?$select=vb_componentid&$top=1', {
                headers: { 'Accept': 'application/json', 'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                credentials: 'include'
            }).then(function (r) { return r.json(); }).then(function (data) {
                if (!data.value || data.value.length === 0) {
                    throw new Error('No components found. Crawl a solution first.');
                }
                return fetch(API_BASE + 'vb_GenerateAISummary', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Accept': 'application/json',
                               'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                    credentials: 'include',
                    body: JSON.stringify({ ComponentId: data.value[0].vb_componentid })
                });
            }).then(function (r) { return r.json(); }).then(function (result) {
                btn.disabled = false;
                btn.textContent = 'Test Connection';
                if (result.Success) {
                    status.style.display = 'block';
                    status.style.background = '#f0fdf4';
                    status.style.color = '#166534';
                    status.style.border = '1px solid #bbf7d0';
                    status.textContent = 'Connection successful! AI summary generated.';
                } else {
                    throw new Error(result.error ? result.error.message : 'AI generation failed');
                }
            }).catch(function (err) {
                btn.disabled = false;
                btn.textContent = 'Test Connection';
                status.style.display = 'block';
                status.style.background = '#fef2f2';
                status.style.color = '#991b1b';
                status.style.border = '1px solid #fecaca';
                status.textContent = 'Connection failed: ' + err.message;
            });
        }

        // Provider change handler
        if (document.getElementById('aiProvider')) {
            document.getElementById('aiProvider').addEventListener('change', updateEndpointVisibility);
        }

        // AI Mode change handler -- toggles which field group is visible
        if (document.getElementById('aiMode')) {
            document.getElementById('aiMode').addEventListener('change', updateAiModeVisibility);
        }

        // Refresh License — re-validates against backend API to pull latest tier/status
        function refreshLicense() {
            if (!licenseKey) { showError('No license key found.'); return; }

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

            fetch(API_BASE + 'vb_ValidateLicense', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'Accept': 'application/json',
                           'OData-MaxVersion': '4.0', 'OData-Version': '4.0' },
                credentials: 'include',
                body: JSON.stringify({ LicenseKey: licenseKey })
            }).then(function (r) { return r.json(); })
            .then(function (result) {
                btn.disabled = false;
                btn.textContent = 'Refresh License';
                if (result.Valid) {
                    showSuccess('License refreshed.');
                    loadLicense(); // Reload all sections with updated data
                } else {
                    showError(friendlyLicenseError(result.Message, 'License validation failed.'));
                    loadLicense();
                }
            }).catch(function (err) {
                btn.disabled = false;
                btn.textContent = 'Refresh License';
                showError('Refresh failed: ' + err.message);
            });
        }

        // Init
        loadLicense();
    </script>
</body>
</html>
