var orchestratorApiBaseUrl = '';
var orchestratorApiKey = '';
let userLookup = {};
let globalTimerInterval = null;
let refreshInterval;
let cachedPullRequestUrl = '';
let cachedOrganizationId = '';
let userLookupLastFetchedAt = 0;
let lastExecutionRenderKey = null;
let latestAgentExecutions = [];
let polledExecutionIdsKey = '';
let currentStatusFilter = 'all';
let currentViewMode = 'table';
let currentSortField = 'Queued';
let currentSortDirection = 'desc';
let currentSearchTerm = '';
let latestVisibleExecutions = [];

const USER_LOOKUP_CACHE_TTL_MS = 5 * 60 * 1000;
const EXECUTION_POLL_INTERVAL_MS = 5000;
const CODE_REVIEW_TABLE_HTML =
    `<div class="cr-toolbar">
        <div class="cr-filter-chips" id="crFilterChips">
            <button type="button" class="cr-filter-chip active" data-filter="all">All</button>
            <button type="button" class="cr-filter-chip" data-filter="running">Running</button>
            <button type="button" class="cr-filter-chip" data-filter="completed">Completed</button>
            <button type="button" class="cr-filter-chip" data-filter="failed">Failed</button>
        </div>
        <div id="CodeReviewSummary" class="cr-summary-inline">
            <div class="cr-summary-item total">
                <span class="label">Total</span>
                <span id="summaryTotal" class="value">0</span>
            </div>
            <div class="cr-summary-item running">
                <span class="label">Running</span>
                <span id="summaryRunning" class="value">0</span>
            </div>
            <div class="cr-summary-item completed">
                <span class="label">Completed</span>
                <span id="summaryCompleted" class="value">0</span>
            </div>
            <div class="cr-summary-item failed">
                <span class="label">Failed</span>
                <span id="summaryFailed" class="value">0</span>
            </div>
            <div class="cr-summary-item success-rate">
                <span class="label">Success Rate</span>
                <span id="summarySuccessRateText" class="value">0%</span>
                <div class="cr-success-track compact">
                    <div id="summarySuccessRateBar" class="cr-success-bar" style="width:0%"></div>
                </div>
            </div>
        </div>
    </div>
    <div id="CodeReviewCardView" class="cr-card-grid cr-theme-azure" style="display:none;"></div>
    <div class="cr-table-scroll" id="CodeReviewTableScroll">
        <table id='CodeReviewExecutionTable' class="cr-table" style='width:100%'>
            <thead>
                <tr>
                    <th data-sort="QueuedBy"><i class="bi bi-person"></i> Queued By <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="Queued">Queued <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="Started">Started <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="Completed">Completed <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="Duration">Duration <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="Status">Status <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                    <th data-sort="ReviewDetails">Review Details <span class="sort-icon"><i class="bi bi-caret-down-fill"></i></span></th>
                </tr>
            </thead>
            <tbody id="CodeReviewExecutionTableBody"></tbody>
        </table>
    </div>`;

// Helper methods
const dateTimeFormatOptions = {
    month: 'short',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
    hour12: true
};

function parseUTCDate(dateStr) {
    if (!dateStr) return null;
    return new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z');
}

function formatDateTimeLocal(dateStr) {
    const date = parseUTCDate(dateStr);
    if (!date) return "-";
    const dateStr_formatted = date.toLocaleDateString("en-US", { month: 'short', day: 'numeric' });
    const timeStr_formatted = date.toLocaleTimeString("en-US", { hour: 'numeric', minute: '2-digit', hour12: true });
    return `<div class="datetime-cell"><div class="datetime-date"><i class="bi bi-calendar3"></i> ${dateStr_formatted}</div><div class="datetime-time"><i class="bi bi-clock"></i> ${timeStr_formatted}</div></div>`;
}

async function ensureExecutionQueryContext() {
    if (!cachedPullRequestUrl) {
        const pullRequestData = await IndexLibrary.GetPullRequestData();
        cachedPullRequestUrl = `${pullRequestData.repository.webUrl}/pullrequest/${pullRequestData.pullRequestId}`;
    }

    if (!cachedOrganizationId) {
        cachedOrganizationId = await IndexLibrary.GetAccountIdAsync();
    }
}

function setCodeReviewQueueButtonState(buttonSelector, isEnabled) {
    const btn = $(buttonSelector);
    if (btn.length) {
        btn.prop('disabled', !isEnabled);
        btn.attr("title", isEnabled ? "" : "You do not have permission")
        btn.css('opacity', isEnabled ? '1' : '0.5');
        btn.css('cursor', isEnabled ? 'pointer' : 'not-allowed');
        btn.css('pointer-events', isEnabled ? 'auto' : 'none');
    }
}

function formatDuration(seconds) {
    const hrs = Math.floor(seconds / 3600);
    const mins = Math.floor((seconds % 3600) / 60);
    const secs = seconds % 60;

    let parts = [];

    if (hrs > 0) parts.push(`${hrs}h`);
    if (mins > 0 || hrs > 0) parts.push(`${mins}m`);
    parts.push(`${secs}s`);

    return parts.join(" ");
}

function getExecutionStatusInfo(status) {
    const value = (status || "Unknown").toLowerCase();

    if (value === "completed") return { css: "completed", icon: "bi-check-circle-fill", label: "Completed" };
    if (value === "failed") return { css: "failed", icon: "bi-x-octagon-fill", label: "Failed" };
    if (value === "queued") return { css: "queued", icon: "bi-hourglass-split", label: "Queued" };
    if (value === "running" || value === "inprogress" || value === "in_progress") return { css: "running", icon: "bi-arrow-repeat", label: "Running" };

    return { css: "unknown", icon: "bi-question-circle-fill", label: status || "Unknown" };
}

function updateExecutionSummary(agentExecutions) {
    const executions = Array.isArray(agentExecutions) ? agentExecutions : [];
    const total = executions.length;
    const completed = executions.filter(e => (e.status || "").toLowerCase() === "completed").length;
    const failed = executions.filter(e => (e.status || "").toLowerCase() === "failed").length;
    const running = executions.filter(e => {
        const status = (e.status || "").toLowerCase();
        return status !== "completed" && status !== "failed";
    }).length;
    const finished = completed + failed;
    const successRate = finished > 0 ? Math.round((completed / finished) * 100) : 0;

    $("#summaryTotal").text(total);
    $("#summaryRunning").text(running);
    $("#summaryCompleted").text(completed);
    $("#summaryFailed").text(failed);
    $("#summarySuccessRateBar").css("width", `${successRate}%`);
    $("#summarySuccessRateText").text(`${successRate}%`);
}

function applyExecutionFilter(executions) {
    const source = Array.isArray(executions) ? executions : [];

    if (currentStatusFilter === 'all') return source;

    if (currentStatusFilter === 'running') {
        return source.filter(e => {
            const status = (e.status || '').toLowerCase();
            return status !== 'completed' && status !== 'failed';
        });
    }

    return source.filter(e => (e.status || '').toLowerCase() === currentStatusFilter);
}

function applyAdvancedGridFilters(executions) {
    const source = Array.isArray(executions) ? executions : [];
    const search = currentSearchTerm.trim().toLowerCase();

    return source.filter((execution) => {
        if (!search) return true;

        const haystack = [
            execution.id,
            execution.createdBy,
            execution.status,
            execution.codeReviewFeedback,
            execution.createdDate,
            execution.startedAt,
            execution.completedAt,
            execution.durationInSeconds
        ].map(v => (v ?? '').toString().toLowerCase()).join(' ');

        return haystack.includes(search);
    });
}

function getSortValue(execution, sortField) {
    switch (sortField) {
        case 'QueuedBy':
            return (execution.createdBy || '').toLowerCase();
        case 'Queued':
            return parseUTCDate(execution.createdDate)?.getTime() || 0;
        case 'Started':
            return parseUTCDate(execution.startedAt)?.getTime() || 0;
        case 'Completed':
            return parseUTCDate(execution.completedAt)?.getTime() || 0;
        case 'Duration':
            return Number(execution.durationInSeconds) || 0;
        case 'Status':
            return (execution.status || '').toLowerCase();
        case 'ReviewDetails':
            return (execution.codeReviewFeedback || '').toLowerCase();
        default:
            return '';
    }
}

function applyExecutionSort(executions) {
    const source = Array.isArray(executions) ? [...executions] : [];

    source.sort((a, b) => {
        const aVal = getSortValue(a, currentSortField);
        const bVal = getSortValue(b, currentSortField);

        if (aVal < bVal) return currentSortDirection === 'asc' ? -1 : 1;
        if (aVal > bVal) return currentSortDirection === 'asc' ? 1 : -1;
        return 0;
    });

    return source;
}

function updateSortHeaderUi() {
    let activeColumnIndex = -1;

    $("#CodeReviewExecutionTable th[data-sort]").each(function () {
        const th = $(this);
        const field = (th.data("sort") || '').toString();
        const icon = th.find(".sort-icon i");

        if (!icon.length) return;

        if (field === currentSortField) {
            activeColumnIndex = th.index();
            icon.removeClass("bi-caret-up-fill bi-caret-down-fill").addClass(
                currentSortDirection === 'asc' ? "bi-caret-up-fill" : "bi-caret-down-fill"
            );
            th.addClass("active-sort active-sort-column column-center");
        } else {
            icon.removeClass("bi-caret-up-fill bi-caret-down-fill").addClass("bi-caret-down-fill");
            th.removeClass("active-sort active-sort-column column-center");
        }
    });

    const tableRows = $("#CodeReviewExecutionTableBody tr");
    tableRows.children('td').removeClass('active-sort-column column-center');

    if (activeColumnIndex >= 0) {
        tableRows.each(function () {
            $(this).children().eq(activeColumnIndex).addClass('active-sort-column column-center');
        });
    }
}

function updateSortActionUi() {
    // Sorting remains available through table header clicks.
}

function syncGridActionControlsUi() {
    const searchInput = $('#crSearchInput');

    if (searchInput.length && searchInput.val() !== currentSearchTerm) {
        searchInput.val(currentSearchTerm);
    }

    updateSortActionUi();
}

function exportVisibleRowsToCsv() {
    if (!Array.isArray(latestVisibleExecutions) || latestVisibleExecutions.length === 0) {
        IndexLibrary.ShowToast('warning', 'No visible rows to export.');
        return;
    }

    const escapeCsv = (value) => {
        const text = (value ?? '').toString();
        if (/[",\n]/.test(text)) {
            return `"${text.replace(/"/g, '""')}"`;
        }
        return text;
    };

    const header = [
        'Queued By',
        'Queued',
        'Started',
        'Completed',
        'Duration Seconds',
        'Status',
        'Review Details'
    ];

    const rows = latestVisibleExecutions.map((execution) => [
        execution.createdBy || '',
        execution.createdDate || '',
        execution.startedAt || '',
        execution.completedAt || '',
        execution.durationInSeconds || 0,
        execution.status || '',
        execution.codeReviewFeedback || ''
    ]);

    const csv = [header, ...rows]
        .map(row => row.map(escapeCsv).join(','))
        .join('\n');

    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');

    link.href = url;
    link.download = `code-review-${timestamp}.csv`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);

    IndexLibrary.ShowToast('success', 'CSV exported successfully.');
}

function updateViewControlsUi() {
    $(".cr-filter-chip").removeClass("active");
    $(`.cr-filter-chip[data-filter='${currentStatusFilter}']`).addClass("active");

    $(".cr-view-btn").removeClass("active");
    $(`.cr-view-btn[data-view='${currentViewMode}']`).addClass("active");

    if (currentViewMode === 'cards') {
        $("#CodeReviewTableScroll").hide();
        $("#CodeReviewCardView").show();
    } else {
        $("#CodeReviewCardView").hide();
        $("#CodeReviewTableScroll").show();
    }
}

function bindReviewDisplayControls() {
    $("#crFilterChips").off("click", ".cr-filter-chip").on("click", ".cr-filter-chip", async function () {
        const nextFilter = ($(this).data("filter") || 'all').toString();
        if (nextFilter === currentStatusFilter) return;

        currentStatusFilter = nextFilter;
        await updateCodeReviewTableBody(latestAgentExecutions);
    });

    $("#crViewSwitch").off("click", ".cr-view-btn").on("click", ".cr-view-btn", async function () {
        const nextView = ($(this).data("view") || 'table').toString();
        if (nextView === currentViewMode) return;

        currentViewMode = nextView;
        await updateCodeReviewTableBody(latestAgentExecutions);
    });

    $("#CodeReviewExecutionTable")
        .off("click", "th[data-sort]")
        .on("click", "th[data-sort]", async function () {
            const selectedField = ($(this).data("sort") || '').toString();
            if (!selectedField) return;

            if (selectedField === currentSortField) {
                currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
            } else {
                currentSortField = selectedField;
                currentSortDirection = 'asc';
            }

            await updateCodeReviewTableBody(latestAgentExecutions);
        });

    $(document)
        .off('input', '#crSearchInput')
        .on('input', '#crSearchInput', async function () {
            currentSearchTerm = ($(this).val() || '').toString();
            await updateCodeReviewTableBody(latestAgentExecutions);
        });

    $(document)
        .off('click', '#crResetGridBtn')
        .on('click', '#crResetGridBtn', async function () {
            currentStatusFilter = 'all';
            currentSearchTerm = '';
            currentSortField = 'Queued';
            currentSortDirection = 'desc';
            await updateCodeReviewTableBody(latestAgentExecutions);
            await IndexLibrary.ShowToast('success', 'Grid filters and sorting reset.');
        });

    $(document)
        .off('click', '#crExportCsvBtn')
        .on('click', '#crExportCsvBtn', function () {
            exportVisibleRowsToCsv();
        });
}

function buildDurationMarkup(element) {
    const isRunning = element.startedAt && element.status !== "Completed" && element.status !== "Failed";
    if (isRunning) {
        const startTime = new Date(
            element.startedAt.endsWith('Z') ? element.startedAt : element.startedAt + 'Z'
        ).getTime();

        const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
        return `
            <span class="running-duration">
                <i class="bi bi-stopwatch"></i>
                <span class="live-duration" data-started="${element.startedAt}">${formatDuration(elapsedSeconds)}</span>
            </span>
        `;
    }

    return `<i class="bi bi-clock"></i> ${formatDuration(element.durationInSeconds)}`;
}

function buildExecutionCardMarkup(element, statusInfo, userHtml, durationHtml, queuedStr, startedStr, completedStr) {
    const reviewText = element.codeReviewFeedback || "-";
    const safeReviewText = IndexLibrary.escapeHtml(reviewText).replace(/\r?\n/g, "<br />");
    const reviewTooltipHtml = IndexLibrary.buildReviewDetailsTooltipHtml(element.codeReviewFeedback);

    return `
        <article class="cr-execution-card cr-card-status-${statusInfo.css}">
            <div class="cr-card-top">
                <div>${userHtml}</div>
                <span class="status-pill ${statusInfo.css}">
                    <i class="bi ${statusInfo.icon}"></i>
                    ${statusInfo.label}
                </span>
            </div>
            <div class="cr-card-meta">
                <div><span>Queued</span><div class="cr-card-meta-value">${queuedStr}</div></div>
                <div><span>Started</span><div class="cr-card-meta-value">${startedStr}</div></div>
                <div><span>Completed</span><div class="cr-card-meta-value">${completedStr}</div></div>
                <div><span>Duration</span><div class="cr-card-meta-value">${durationHtml}</div></div>
            </div>
            <div class="cr-card-feedback" data-bs-toggle="tooltip" data-bs-title="${reviewTooltipHtml}" data-bs-html="true" data-bs-custom-class="review-details-tooltip" data-bs-placement="top">
                ${safeReviewText}
            </div>
        </article>
    `;
}

function buildExecutionRenderKey(agentExecutions) {
    if (!Array.isArray(agentExecutions)) {
        return [
            'none',
            currentStatusFilter,
            currentViewMode,
            currentSortField,
            currentSortDirection,
            currentSearchTerm.trim().toLowerCase()
        ].join('|');
    }

    const dataKey = agentExecutions.map(e => [
        e.id,
        e.status,
        e.createdBy,
        e.createdDate,
        e.startedAt,
        e.completedAt,
        e.durationInSeconds,
        e.codeReviewFeedback
    ].join("|")).join("~~");

    return [
        dataKey,
        currentStatusFilter,
        currentViewMode,
        currentSortField,
        currentSortDirection,
        currentSearchTerm.trim().toLowerCase()
    ].join('|');
}

function startGlobalTimer() {
    if (globalTimerInterval) return;

    globalTimerInterval = setInterval(() => {
        const hasRunningExecutions = latestAgentExecutions.some(e => e.status !== "Completed" && e.status !== "Failed");
        if (!hasRunningExecutions) {
            stopGlobalTimer();
            return;
        }

        document.querySelectorAll(".live-duration[data-started]").forEach(el => {
            const startedAt = el.getAttribute("data-started");
            if (!startedAt) return;

            const startTime = new Date(
                startedAt.endsWith('Z') ? startedAt : startedAt + 'Z'
            ).getTime();

            const seconds = Math.floor((Date.now() - startTime) / 1000);
            el.innerText = formatDuration(Math.max(0, seconds));
        });
    }, 1000);
}

function stopGlobalTimer() {
    if (!globalTimerInterval) return;

    clearInterval(globalTimerInterval);
    globalTimerInterval = null;
}

function mergeExecutionUpdates(existingExecutions, updatedExecutions) {
    const byId = new Map((existingExecutions || []).map(e => [e.id, e]));
    (updatedExecutions || []).forEach(updated => {
        const existing = byId.get(updated.id) || {};
        byId.set(updated.id, { ...existing, ...updated });
    });

    return Array.from(byId.values());
}

function manageTablePolling(agentExecutions) {
    const executions = Array.isArray(agentExecutions) ? agentExecutions : [];
    const runningExecutionIds = executions
        .filter(e => e.status !== "Completed" && e.status !== "Failed")
        .map(e => Number(e.id))
        .filter(id => Number.isFinite(id) && id > 0);
    const hasRunning = runningExecutionIds.length > 0;
    const currentIdsKey = runningExecutionIds.join(",");

    if (hasRunning) {
        startGlobalTimer();

        if (refreshInterval && polledExecutionIdsKey !== currentIdsKey) {
            clearInterval(refreshInterval);
            refreshInterval = null;
        }

        if (!refreshInterval) {
            polledExecutionIdsKey = currentIdsKey;
            refreshInterval = setInterval(async () => {
                const executionUpdates = await getAgentExecutionsByIds(runningExecutionIds);
                if (!executionUpdates.length) return;

                latestAgentExecutions = mergeExecutionUpdates(latestAgentExecutions, executionUpdates);
                await updateCodeReviewTableBody(latestAgentExecutions);
            }, EXECUTION_POLL_INTERVAL_MS);
        }
    } else {
        stopGlobalTimer();

        if (refreshInterval) {
            clearInterval(refreshInterval);
            refreshInterval = null;
            polledExecutionIdsKey = '';
        }
    }
}

// Initialization
initializeCodeReviewPage().catch(() => {
    $("#validationMessage").show();
});

// Data loading
async function initializeCodeReviewPage() {
    try {
        // $("#main").show();
        $("#loadingSpinner").show();
        $("#codeReviewTrialPill").hide();

        await IndexLibrary.ensureSdkReady();

        const config = await IndexLibrary.GetExtensionConfig();
        orchestratorApiBaseUrl = config.orchestratorApiBaseUrl;
        orchestratorApiKey = config.orchestratorApiKey;

        const email = IndexLibrary.GetUserEmail();
        const licenseStatusResponse = await IndexLibrary.GetRequest({
            endpoint: `/api/License/GetOrgAndUserLicenseStatusByModule?email=${encodeURIComponent(email)}&moduleUserType=crUserType`
        });

        if (!licenseStatusResponse.ok) {
            if (licenseStatusResponse.status === 404) {
                $("#validationMessage").show();
                $("#loadingSpinner").hide();
                // $("#main").hide();
                return;
            }

            await IndexLibrary.ShowToast('error', `Unable to get license status: ${licenseStatusResponse.status}`);
            $("#loadingSpinner").hide();
            return;
        }

        const licenseStatus = await licenseStatusResponse.json();
        const isOrgLicenseValid = licenseStatus?.isOrgLicenseValid === true;
        const isOrgLicenseExpired = licenseStatus?.isOrgLicenseExpired === true;
        const isTrialLicense = licenseStatus?.isTrialLicense === true;
        const isUserLicensed = licenseStatus?.isUserLicensed === true;
        const hasModuleLicense = licenseStatus?.hasModuleLicense === true;
        const organizationMessage = (licenseStatus?.organizationMessage || "").trim();
        const userMessage = (licenseStatus?.userMessage || "").trim();

        const trialExpiryCandidate =
            licenseStatus?.trialExpiryDateTimeUtc ||
            licenseStatus?.expiryDateTimeUtc ||
            licenseStatus?.licenseExpiryDateTimeUtc ||
            licenseStatus?.expiryDateUtc ||
            null;
        const trialExpiryDate = trialExpiryCandidate ? new Date(trialExpiryCandidate) : null;
        const hasValidTrialExpiry = trialExpiryDate instanceof Date && !Number.isNaN(trialExpiryDate.getTime());

        // Organization license doesn't exist
        if (!isOrgLicenseValid) {
            $("#codeReviewTrialPill").hide();
            $("#validationMessage").show();
            setCodeReviewQueueButtonState("#btnQueueCodeReview", false);
            $("#loadingSpinner").hide();
            //$("#main").hide();
            return;
        }

        await PopulateCodeReviewExecutionTable();
        // $("#main").show();

        // User license doesn't exist || user doesn't have module license
        if (!isUserLicensed || !hasModuleLicense) {
            $("#codeReviewTrialPill").hide();
            $("#validationMessage").text(userMessage).show();
            setCodeReviewQueueButtonState("#btnQueueCodeReview", false);
            $("#loadingSpinner").hide();
            return;
        }

        // Organization license is expired
        if (isOrgLicenseExpired) {
            $("#codeReviewTrialPill").hide();
            $("#validationMessage").text(organizationMessage).show();
            setCodeReviewQueueButtonState("#btnQueueCodeReview", false);
            $("#loadingSpinner").hide();
            return;
        }

        if (isTrialLicense) {
            const trialMessage = hasValidTrialExpiry
                ? `Trial until ${trialExpiryDate.toLocaleDateString('en-US')}`
                : 'Trial';
            $("#codeReviewTrialPillText").text(trialMessage);
            $("#codeReviewTrialPill").show();
        } else {
            $("#codeReviewTrialPill").hide();
        }

        setCodeReviewQueueButtonState("#btnQueueCodeReview", true);

    } catch (error) {
        console.error("Error initializing code review page:", error);
        $("#loadingSpinner").hide();
        // $("#main").hide();
    }
}

async function PopulateCodeReviewExecutionTable() {
    const now = Date.now();
    const shouldRefreshUserLookup =
        !Object.keys(userLookup).length ||
        (now - userLookupLastFetchedAt) > USER_LOOKUP_CACHE_TTL_MS;

    if (shouldRefreshUserLookup) {
        const allUsers = await IndexLibrary.getAllUsers();
        userLookup = IndexLibrary.buildUserLookup(allUsers);
        userLookupLastFetchedAt = now;
    }

    const codeReviewListBody = $("#CodeReviewListBody");
    if (!$("#CodeReviewExecutionTable").length) {
        codeReviewListBody.html(CODE_REVIEW_TABLE_HTML);
        bindReviewDisplayControls();
    }

    codeReviewListBody.show();
    await updateCodeReviewTableBody();
    startGlobalTimer();
}

async function updateCodeReviewTableBody(agentExecutions = null) {
    if (!agentExecutions) {
        agentExecutions = await getAgentExecutions();
    }

    agentExecutions = Array.isArray(agentExecutions) ? agentExecutions : [];
    latestAgentExecutions = agentExecutions;
    updateExecutionSummary(agentExecutions);

    const renderKey = buildExecutionRenderKey(agentExecutions);
    if (lastExecutionRenderKey === renderKey) {
        manageTablePolling(agentExecutions);
        $("#loadingSpinner").hide();
        $("#CodeReviewListBody").show();
        return;
    }

    const statusFilteredExecutions = applyExecutionFilter(agentExecutions);
    const advancedFilteredExecutions = applyAdvancedGridFilters(statusFilteredExecutions);
    const sortedExecutions = applyExecutionSort(advancedFilteredExecutions);
    latestVisibleExecutions = sortedExecutions;

    // Dispose existing tooltips before re-rendering
    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
        const tooltip = bootstrap.Tooltip.getInstance(el);
        if (tooltip) tooltip.dispose();
    });

    const tbody = $("#CodeReviewExecutionTableBody");
    const cardView = $("#CodeReviewCardView");
    const rows = [];
    const cards = [];

    if (!sortedExecutions.length) {
        const emptyMarkup = `
            <div class="cr-empty-state">
                <i class="bi bi-inboxes"></i>
                <span>No code review executions for the selected view.</span>
            </div>
        `;

        rows.push(`
            <tr>
                <td colspan="7" class="cr-empty-state">
                    <i class="bi bi-inboxes"></i>
                    <span>No code review executions for the selected view.</span>
                </td>
            </tr>
        `);

        cards.push(emptyMarkup);
    }

    await IndexLibrary.loadAvatarsFor(sortedExecutions.map(execution => execution.createdBy));

    sortedExecutions.forEach((element) => {
        const rawQueued = element.createdDate;
        const rawStarted = element.startedAt;
        const rawCompleted = element.completedAt;

        const queuedStr = formatDateTimeLocal(rawQueued);
        const startedStr = formatDateTimeLocal(rawStarted);
        const completedStr = formatDateTimeLocal(rawCompleted);

        const userEmail = element.createdBy?.toLowerCase();
        const user = userLookup[userEmail];
        const userName = user ? user.name || element.createdBy : element.createdBy;
        const avatarLink = IndexLibrary.getCachedAvatar(element.createdBy);
        const userNameTrimmed = userName?.trim();
        const userEmailTrimmed = element.createdBy?.trim();

        const statusInfo = getExecutionStatusInfo(element.status);
        const showEmail =
            userEmailTrimmed &&
            userNameTrimmed &&
            userEmailTrimmed &&
            userNameTrimmed.toLowerCase() !== userEmailTrimmed.toLowerCase();
        const tooltipHtml = IndexLibrary.buildQueuedByTooltipHtml({
            displayName: userNameTrimmed || userEmailTrimmed || "Unknown user",
            email: userEmailTrimmed || "",
            showEmail,
            statusCss: statusInfo.css,
            statusIcon: statusInfo.icon
        });
        const reviewTooltipHtml = IndexLibrary.buildReviewDetailsTooltipHtml(element.codeReviewFeedback);
        const reviewText = IndexLibrary.escapeHtml(element.codeReviewFeedback || "-");

        const userHtml = avatarLink
            ? `
                    <div class="user-cell" data-bs-toggle="tooltip" data-bs-title="${tooltipHtml}" data-bs-html="true" data-bs-custom-class="queued-by-tooltip" data-bs-placement="top">
                        <img src="${avatarLink}" class="avatar-img" />
                        <span>${userName}</span>
                    </div>
                `
            : `
                    <div class="user-cell" data-bs-toggle="tooltip" data-bs-title="${tooltipHtml}" data-bs-html="true" data-bs-custom-class="queued-by-tooltip" data-bs-placement="top">
                        <span class="avatar">${getInitials(element.createdBy)}</span>
                        <span>${userName}</span>
                    </div>
                `;

        const durationHtml = buildDurationMarkup(element);

        rows.push(`
            <tr>
                <td>${userHtml}</td>
                <td>${queuedStr}</td>
                <td>${startedStr}</td>
                <td>${completedStr}</td>
                <td>${durationHtml}</td>
                <td>
                    <span class="status-pill ${statusInfo.css}">
                        <i class="bi ${statusInfo.icon}"></i>
                        ${statusInfo.label}
                    </span>
                </td>
                <td>
                    <div class="review-feedback-cell" data-bs-toggle="tooltip" data-bs-title="${reviewTooltipHtml}" data-bs-html="true" data-bs-custom-class="review-details-tooltip" data-bs-placement="top">
                        ${reviewText}
                    </div>
                </td>
            </tr>
        `);

        cards.push(buildExecutionCardMarkup(element, statusInfo, userHtml, durationHtml, queuedStr, startedStr, completedStr));
    });

    tbody.html(rows.join(""));
    cardView.html(cards.join(""));

    syncGridActionControlsUi();
    updateViewControlsUi();
    updateSortHeaderUi();

    // Initialize Bootstrap tooltips for user cells
    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
        new bootstrap.Tooltip(el);
    });
    lastExecutionRenderKey = renderKey;
    manageTablePolling(agentExecutions);
    $("#loadingSpinner").hide();
    $("#CodeReviewListBody").show();
}


async function getAgentExecutions() {
    await ensureExecutionQueryContext();

    const reviewRequest = {
        organizationId: cachedOrganizationId,
        workItemOrPullRequestId: cachedPullRequestUrl,
    };

    const query = new URLSearchParams(reviewRequest).toString();
    const response = await IndexLibrary.GetRequest({
        baseAPIUrl: orchestratorApiBaseUrl,
        endpoint: `/api/agentexecution/get?${query}`,
        xApiKey: orchestratorApiKey
    });

    if (!response.ok) {
        if (response.status === 400) {
            var responseJson = await response.json();
            await IndexLibrary.ShowToast('error', responseJson.message);

        }
        else {
            await IndexLibrary.ShowToast('error', `API call failed: ${response.status}`);
        }

        return [];
    }
    else {
        const executions = await response.json();
        return Array.isArray(executions) ? executions : [];
    }


}

async function getAgentExecutionsByIds(executionIds) {
    if (!Array.isArray(executionIds) || executionIds.length === 0) {
        await IndexLibrary.ShowToast('error', 'At least one executionId is required.');
        return [];
    }

    const normalizedIds = executionIds.map(id => Number(id));
    if (normalizedIds.some(id => !Number.isFinite(id) || id <= 0)) {
        await IndexLibrary.ShowToast('error', 'All executionIds must be valid positive numbers.');
        return [];
    }

    const query = new URLSearchParams();
    normalizedIds.forEach(id => query.append("executionIds", id.toString()));

    const response = await IndexLibrary.GetRequest({
        baseAPIUrl: orchestratorApiBaseUrl,
        endpoint: `/api/agentexecution/by-ids?${query.toString()}`,
        xApiKey: orchestratorApiKey
    });

    if (!response.ok) {
        if (response.status === 400) {
            const result = await response.json().catch(() => null);
            await IndexLibrary.ShowToast('error', result?.message || 'At least one executionId is required.');
            return [];
        }

        const errorText = await response.text().catch(() => "");
        await IndexLibrary.ShowToast('error', errorText || `API call failed: ${response.status}`);
        return [];
    }

    const executions = await response.json();
    return Array.isArray(executions) ? executions : [];
}

// Actions

async function CreateTrialLicense() {
    await IndexLibrary.AutoCreateTrialLicense(true);
}

async function refreshCodeReviewTable() {
    const refreshBtn = $("#btnRefreshCodeReview");

    try {
        refreshBtn.prop("disabled", true);
        $("#loadingSpinner").show();

        const executions = await getAgentExecutions();
        await updateCodeReviewTableBody(executions);
        await IndexLibrary.ShowToast('success', 'Code review data refreshed.');
    } catch (error) {
        await IndexLibrary.ShowToast('error', 'Unable to refresh code review table.');
    } finally {
        refreshBtn.prop("disabled", false);
        $("#loadingSpinner").hide();
    }
}

async function queueCodeReviewPR() {
    const queueBtn = $("#btnQueueCodeReview");

    try {
        queueBtn.prop("disabled", true);
        $("#loadingSpinner").show();

        await IndexLibrary.QueueCodeReviewPR();

        // Immediate refresh first, then retry briefly for eventual consistency.
        const preQueueCount = latestAgentExecutions.length;
        let executions = await getAgentExecutions();
        await updateCodeReviewTableBody(executions);

        if (executions.length <= preQueueCount) {
            const maxAttempts = 5;
            const retryDelayMs = 900;

            for (let attempt = 0; attempt < maxAttempts; attempt++) {
                await new Promise(resolve => setTimeout(resolve, retryDelayMs));
                executions = await getAgentExecutions();
                await updateCodeReviewTableBody(executions);

                if (executions.length > preQueueCount) {
                    break;
                }
            }
        }
    } catch (error) {
        await IndexLibrary.ShowToast('error', 'Unable to queue code review right now. Please try again.');
    } finally {
        queueBtn.prop("disabled", false);
        $("#loadingSpinner").hide();
    }
}



