// file: kb_demo_app/frontend/ts/demo_pipeline.ts

import * as bootstrap from "bootstrap";
import "simplebar";
import ResizeObserver from "resize-observer-polyfill";
import { invoke } from "@tauri-apps/api/core";
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
import { DemoPipelineInspectRequest } from './bindings/DemoPipelineInspectRequest.ts';
import { DemoPipelineInspectPayload } from './bindings/DemoPipelineInspectPayload.ts';
import { DemoPipelineInspectTokenRequest } from './bindings/DemoPipelineInspectTokenRequest.ts';
import { DemoPipelineInspectPairRequest } from './bindings/DemoPipelineInspectPairRequest.ts';
import { DemoPipelineInspectPoolRequest } from './bindings/DemoPipelineInspectPoolRequest.ts';
import { DemoPipelineBackfillTokenRequest } from './bindings/DemoPipelineBackfillTokenRequest.ts';
import { DemoPipelineBackfillTokenPayload } from './bindings/DemoPipelineBackfillTokenPayload.ts';
import { DemoPipelineBackfillPoolRequest } from './bindings/DemoPipelineBackfillPoolRequest.ts';
import { DemoPipelineBackfillPoolPayload } from './bindings/DemoPipelineBackfillPoolPayload.ts';
import * as echarts from "echarts";
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;


interface DemoPipelinePairCandle {
    id: number | null;
    pair_id: number;
    timeframe_seconds: number;
    bucket_start_unix: number;
    bucket_end_unix: number;
    open_price_quote_per_base: number;
    high_price_quote_per_base: number;
    low_price_quote_per_base: number;
    close_price_quote_per_base: number;
    trade_count: number;
    buy_count: number;
    sell_count: number;
    base_volume_raw: string | null;
    quote_volume_raw: string | null;
    first_trade_signature: string | null;
    last_trade_signature: string | null;
    created_at: string;
    updated_at: string;
}

interface DemoPipelinePairCandleGroup {
    pairId: number;
    timeframeSeconds: number;
    candles: DemoPipelinePairCandle[];
}

function parsePairCandleGroups(rawJson: string): DemoPipelinePairCandleGroup[] {
    if (rawJson.trim() === "") {
        return [];
    }

    try {
        const parsed = JSON.parse(rawJson) as unknown;
        if (!Array.isArray(parsed)) {
            return [];
        }

        const groups: DemoPipelinePairCandleGroup[] = [];

        for (const value of parsed) {
            if (typeof value !== "object" || value === null) {
                continue;
            }

            const maybeGroup = value as {
                pairId?: unknown;
                timeframeSeconds?: unknown;
                candles?: unknown;
            };

            if (
                typeof maybeGroup.pairId !== "number" ||
                typeof maybeGroup.timeframeSeconds !== "number" ||
                !Array.isArray(maybeGroup.candles)
            ) {
                continue;
            }

            groups.push({
                pairId: maybeGroup.pairId,
                timeframeSeconds: maybeGroup.timeframeSeconds,
                candles: maybeGroup.candles as DemoPipelinePairCandle[],
            });
        }

        return groups;
    } catch {
        return [];
    }
}

function formatTimeframeLabel(timeframeSeconds: number): string {
    if (timeframeSeconds % 3600 === 0) {
        return `${timeframeSeconds / 3600}h`;
    }
    if (timeframeSeconds % 60 === 0) {
        return `${timeframeSeconds / 60}m`;
    }
    return `${timeframeSeconds}s`;
}

function parseRawVolume(text: string | null, fallback: number): number {
    if (text === null || text.trim() === "") {
        return fallback;
    }

    const parsed = Number.parseFloat(text);
    if (Number.isNaN(parsed)) {
        return fallback;
    }

    return parsed;
}

function setEmptyCandlesChart(
    chart: echarts.ECharts,
    chartMeta: HTMLElement,
    message: string,
): void {
    chartMeta.textContent = message;

    chart.setOption({
        animation: false,
        title: {
            text: message,
            left: "center",
            top: "middle",
            textStyle: {
                fontSize: 14,
                fontWeight: "normal",
            },
        },
        tooltip: {},
        xAxis: { show: false, type: "category", data: [] },
        yAxis: { show: false, type: "value" },
        series: [],
    }, true);
}

function refreshCandlesSelectors(
    groups: DemoPipelinePairCandleGroup[],
    pairSelect: HTMLSelectElement,
    timeframeSelect: HTMLSelectElement,
): void {
    const currentPairValue = pairSelect.value;
    const currentTimeframeValue = timeframeSelect.value;

    const uniquePairs = Array.from(new Set(groups.map((group) => group.pairId))).sort((left, right) => left - right);

    pairSelect.innerHTML = "";
    if (uniquePairs.length === 0) {
        const option = document.createElement("option");
        option.value = "";
        option.textContent = "Aucune";
        pairSelect.appendChild(option);

        timeframeSelect.innerHTML = "";
        const tfOption = document.createElement("option");
        tfOption.value = "";
        tfOption.textContent = "Aucun";
        timeframeSelect.appendChild(tfOption);
        return;
    }

    for (const pairId of uniquePairs) {
        const option = document.createElement("option");
        option.value = String(pairId);
        option.textContent = `Pair #${pairId}`;
        if (option.value === currentPairValue) {
            option.selected = true;
        }
        pairSelect.appendChild(option);
    }

    if (pairSelect.value === "" && uniquePairs.length > 0) {
        pairSelect.value = String(uniquePairs[0]);
    }

    const selectedPairId = Number.parseInt(pairSelect.value, 10);
    const pairGroups = groups
        .filter((group) => group.pairId === selectedPairId)
        .sort((left, right) => left.timeframeSeconds - right.timeframeSeconds);

    timeframeSelect.innerHTML = "";

    if (pairGroups.length === 0) {
        const option = document.createElement("option");
        option.value = "";
        option.textContent = "Aucun";
        timeframeSelect.appendChild(option);
        return;
    }

    for (const group of pairGroups) {
        const option = document.createElement("option");
        option.value = String(group.timeframeSeconds);
        option.textContent = formatTimeframeLabel(group.timeframeSeconds);
        if (option.value === currentTimeframeValue) {
            option.selected = true;
        }
        timeframeSelect.appendChild(option);
    }

    if (timeframeSelect.value === "" && pairGroups.length > 0) {
        timeframeSelect.value = String(pairGroups[0].timeframeSeconds);
    }
}

function renderSelectedCandlesChart(
    chart: echarts.ECharts,
    chartMeta: HTMLElement,
    groups: DemoPipelinePairCandleGroup[],
    pairSelect: HTMLSelectElement,
    timeframeSelect: HTMLSelectElement,
): void {
    if (groups.length === 0) {
        setEmptyCandlesChart(chart, chartMeta, "Aucune candle disponible.");
        return;
    }

    const selectedPairId = Number.parseInt(pairSelect.value, 10);
    const selectedTimeframe = Number.parseInt(timeframeSelect.value, 10);

    if (Number.isNaN(selectedPairId) || Number.isNaN(selectedTimeframe)) {
        setEmptyCandlesChart(chart, chartMeta, "Sélection de paire/timeframe invalide.");
        return;
    }

    const group = groups.find(
        (value) =>
            value.pairId === selectedPairId &&
            value.timeframeSeconds === selectedTimeframe,
    );

    if (!group || group.candles.length === 0) {
        setEmptyCandlesChart(
            chart,
            chartMeta,
            `Aucune candle pour la pair #${selectedPairId} en ${formatTimeframeLabel(selectedTimeframe)}.`,
        );
        return;
    }

    const candles = [...group.candles].sort(
        (left, right) => left.bucket_start_unix - right.bucket_start_unix,
    );

    const categoryData = candles.map((candle) =>
        new Date(candle.bucket_start_unix * 1000).toLocaleString("fr-CH", {
            hour12: false,
            year: "2-digit",
            month: "2-digit",
            day: "2-digit",
            hour: "2-digit",
            minute: "2-digit",
        }),
    );

    const ohlcData = candles.map((candle) => [
        candle.open_price_quote_per_base,
        candle.close_price_quote_per_base,
        candle.low_price_quote_per_base,
        candle.high_price_quote_per_base,
    ]);

    const volumeData = candles.map((candle) =>
        parseRawVolume(candle.quote_volume_raw, candle.trade_count),
    );

    chartMeta.textContent =
        `Pair #${selectedPairId} • ${formatTimeframeLabel(selectedTimeframe)} • ${candles.length} candles`;

    chart.setOption(
        {
            animation: false,
            legend: {
                data: ["OHLC", "Volume"],
                top: 0,
            },
            tooltip: {
                trigger: "axis",
                axisPointer: {
                    type: "cross",
                },
            },
            axisPointer: {
                link: [{ xAxisIndex: "all" }],
            },
            grid: [
                { left: 60, right: 24, top: 40, height: "58%" },
                { left: 60, right: 24, top: "74%", height: "16%" },
            ],
            xAxis: [
                {
                    type: "category",
                    data: categoryData,
                    boundaryGap: true,
                    axisLine: { onZero: false },
                    splitLine: { show: false },
                    min: "dataMin",
                    max: "dataMax",
                },
                {
                    type: "category",
                    gridIndex: 1,
                    data: categoryData,
                    boundaryGap: true,
                    axisLine: { onZero: false },
                    axisTick: { show: false },
                    splitLine: { show: false },
                    axisLabel: { show: false },
                    min: "dataMin",
                    max: "dataMax",
                },
            ],
            yAxis: [
                {
                    scale: true,
                    splitArea: { show: false },
                },
                {
                    gridIndex: 1,
                    scale: true,
                    splitNumber: 2,
                },
            ],
            dataZoom: [
                {
                    type: "inside",
                    xAxisIndex: [0, 1],
                    start: 0,
                    end: 100,
                },
                {
                    show: true,
                    type: "slider",
                    xAxisIndex: [0, 1],
                    bottom: 6,
                    start: 0,
                    end: 100,
                },
            ],
            series: [
                {
                    name: "OHLC",
                    type: "candlestick",
                    data: ohlcData,
                },
                {
                    name: "Volume",
                    type: "bar",
                    xAxisIndex: 1,
                    yAxisIndex: 1,
                    data: volumeData,
                },
            ],
        },
        true,
    );
}

function applyInspectionPayload(
    payload: DemoPipelineInspectPayload,
    summaryTextarea: HTMLTextAreaElement,
    transactionTextarea: HTMLTextAreaElement,
    decodedEventsTextarea: HTMLTextAreaElement,
    poolsTextarea: HTMLTextAreaElement,
    pairsTextarea: HTMLTextAreaElement,
    launchAttributionsTextarea: HTMLTextAreaElement,
    poolOriginsTextarea: HTMLTextAreaElement,
    walletsTextarea: HTMLTextAreaElement,
    tradeEventsTextarea: HTMLTextAreaElement,
    pairMetricsTextarea: HTMLTextAreaElement,
    pairCandlesTextarea: HTMLTextAreaElement,
    pairAnalyticSignalsTextarea: HTMLTextAreaElement,
    chart: echarts.ECharts,
    chartMeta: HTMLElement,
    pairSelect: HTMLSelectElement,
    timeframeSelect: HTMLSelectElement,
): void {
    summaryTextarea.value = payload.summaryJson;
    transactionTextarea.value = payload.transactionJson;
    decodedEventsTextarea.value = payload.decodedEventsJson;
    poolsTextarea.value = payload.poolsJson;
    pairsTextarea.value = payload.pairsJson;
    launchAttributionsTextarea.value = payload.launchAttributionsJson;
    poolOriginsTextarea.value = payload.poolOriginsJson;
    walletsTextarea.value = payload.walletsJson;
    tradeEventsTextarea.value = payload.tradeEventsJson;
    pairMetricsTextarea.value = payload.pairMetricsJson;
    pairCandlesTextarea.value = payload.pairCandlesJson;
    pairAnalyticSignalsTextarea.value = payload.pairAnalyticSignalsJson;

    const groups = parsePairCandleGroups(payload.pairCandlesJson);
    refreshCandlesSelectors(groups, pairSelect, timeframeSelect);
    renderSelectedCandlesChart(chart, chartMeta, groups, pairSelect, timeframeSelect);
}

function appendLogLine(textarea: HTMLTextAreaElement, line: string): void {
    const now = new Date();
    const timestamp = now.toLocaleTimeString("fr-CH", { hour12: false });

    const lines = textarea.value === "" ? [] : textarea.value.split("\n");
    lines.push(`[${timestamp}] ${line}`);

    const maxLines = 300;
    textarea.value = lines.slice(-maxLines).join("\n");
    textarea.scrollTop = textarea.scrollHeight;
}

function clearInspection(
    backfillTextarea: HTMLTextAreaElement,
    summaryTextarea: HTMLTextAreaElement,
    transactionTextarea: HTMLTextAreaElement,
    decodedEventsTextarea: HTMLTextAreaElement,
    poolsTextarea: HTMLTextAreaElement,
    pairsTextarea: HTMLTextAreaElement,
    launchAttributionsTextarea: HTMLTextAreaElement,
    poolOriginsTextarea: HTMLTextAreaElement,
    walletsTextarea: HTMLTextAreaElement,
    tradeEventsTextarea: HTMLTextAreaElement,
    pairMetricsTextarea: HTMLTextAreaElement,
    pairCandlesTextarea: HTMLTextAreaElement,
    pairAnalyticSignalsTextarea: HTMLTextAreaElement,
): void {
    backfillTextarea.value = "";
    summaryTextarea.value = "";
    transactionTextarea.value = "";
    decodedEventsTextarea.value = "";
    poolsTextarea.value = "";
    pairsTextarea.value = "";
    launchAttributionsTextarea.value = "";
    poolOriginsTextarea.value = "";
    walletsTextarea.value = "";
    tradeEventsTextarea.value = "";
    pairMetricsTextarea.value = "";
    pairCandlesTextarea.value = "";
    pairAnalyticSignalsTextarea.value = "";
}

function readCustomTimeframeSeconds(
    input: HTMLInputElement,
    logTextarea: HTMLTextAreaElement,
): bigint | null | undefined {
    const customTimeframeText = input.value.trim();
    if (customTimeframeText === "") {
        return null;
    }

    const parsed = Number.parseInt(customTimeframeText, 10);
    if (Number.isNaN(parsed) || parsed <= 0) {
        appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
        return undefined;
    }

    return BigInt(parsed);
}

function readPositiveIntegerInput(
    input: HTMLInputElement,
    logTextarea: HTMLTextAreaElement,
    label: string,
): number | undefined {
    const text = input.value.trim();
    if (text === "") {
        appendLogLine(logTextarea, `[ui] ${label} is required`);
        return undefined;
    }

    const parsed = Number.parseInt(text, 10);
    if (Number.isNaN(parsed) || parsed <= 0) {
        appendLogLine(logTextarea, `[ui] invalid ${label} '${text}'`);
        return undefined;
    }

    return parsed;
}

document.addEventListener("DOMContentLoaded", async () => {
    void takeoverConsole();
    debug("demo_pipeline window loaded");

    const sidebarToggle = document.querySelector<HTMLButtonElement>('#sidebarToggle');
    if (sidebarToggle) {
        // restaurer l’état depuis localStorage
        if (localStorage.getItem('sidebar-toggle') === 'true') {
            document.body.classList.add('sidenav-toggled');
        }

        sidebarToggle.addEventListener('click', (event) => {
            event.preventDefault();
            document.body.classList.toggle('sidenav-toggled');
            localStorage.setItem('sidebar-toggle', document.body.classList.contains('sidenav-toggled') ? 'true' : 'false');
        });
    }

    const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
    Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
    const toastElList = document.querySelectorAll('.toast');
    Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl));
    const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
    Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));

    const gobackto = location.pathname + location.search;

    document.querySelectorAll<HTMLAnchorElement>('a[data-setlang]').forEach((a) => {
        const href = a.getAttribute("href");
        if (!href) return; // pas de href => on ignore

        const url = new URL(href, location.origin);
        url.searchParams.set("gobackto", gobackto);

        // conserve une URL relative (path + query)
        a.setAttribute("href", url.pathname + "?" + url.searchParams.toString());
    });

    const signatureInput = document.querySelector<HTMLInputElement>("#demoPipelineSignatureInput");
    const customTimeframeInput = document.querySelector<HTMLInputElement>("#demoPipelineCustomTimeframeInput");
    const inspectButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectButton");
    const clearButton = document.querySelector<HTMLButtonElement>("#demoPipelineClearButton");
    const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipelineClearLogButton");

    const summaryTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineSummaryTextarea");
    const transactionTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineTransactionTextarea");
    const decodedEventsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineDecodedEventsTextarea");
    const poolsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePoolsTextarea");
    const pairsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairsTextarea");
    const launchAttributionsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineLaunchAttributionsTextarea");
    const poolOriginsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePoolOriginsTextarea");
    const walletsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineWalletsTextarea");
    const tradeEventsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineTradeEventsTextarea");
    const pairMetricsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairMetricsTextarea");
    const pairCandlesTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairCandlesTextarea");
    const pairAnalyticSignalsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelinePairAnalyticSignalsTextarea");
    const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineLogTextarea");
    const tokenMintInput = document.querySelector<HTMLInputElement>("#demoPipelineTokenMintInput");
    const inspectTokenButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectTokenButton");
    const pairIdInput = document.querySelector<HTMLInputElement>("#demoPipelinePairIdInput");
    const inspectPairButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectPairButton");
    const poolAddressInput = document.querySelector<HTMLInputElement>("#demoPipelinePoolAddressInput");
    const inspectPoolButton = document.querySelector<HTMLButtonElement>("#demoPipelineInspectPoolButton");
    const backfillTokenMintInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillTokenMintInput");
    const backfillHttpRoleInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillHttpRoleInput");
    const backfillMintLimitInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillMintLimitInput");
    const backfillPoolLimitInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillPoolLimitInput");
    const backfillTokenButton = document.querySelector<HTMLButtonElement>("#demoPipelineBackfillTokenButton");
    const backfillTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipelineBackfillTextarea");

    const chartPairSelect = document.querySelector<HTMLSelectElement>("#demoPipelineChartPairSelect");
    const chartTimeframeSelect = document.querySelector<HTMLSelectElement>("#demoPipelineChartTimeframeSelect");
    const candlesChartElement = document.querySelector<HTMLDivElement>("#demoPipelineCandlesChart");
    const candlesChartMeta = document.querySelector<HTMLDivElement>("#demoPipelineCandlesChartMeta");

    const backfillPoolAddressInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillPoolAddressInput");
    const backfillPoolOnlyLimitInput = document.querySelector<HTMLInputElement>("#demoPipelineBackfillPoolOnlyLimitInput");
    const backfillPoolButton = document.querySelector<HTMLButtonElement>("#demoPipelineBackfillPoolButton");


    if (
        !chartPairSelect ||
        !chartTimeframeSelect ||
        !candlesChartElement ||
        !candlesChartMeta ||
        !backfillTokenMintInput ||
        !backfillHttpRoleInput ||
        !backfillMintLimitInput ||
        !backfillPoolLimitInput ||
        !backfillTokenButton ||
        !backfillTextarea ||
        !backfillPoolAddressInput ||
        !backfillPoolOnlyLimitInput ||
        !backfillPoolButton ||
        !pairIdInput ||
        !inspectPairButton ||
        !poolAddressInput ||
        !inspectPoolButton ||
        !tokenMintInput ||
        !inspectTokenButton ||
        !signatureInput ||
        !customTimeframeInput ||
        !inspectButton ||
        !clearButton ||
        !clearLogButton ||
        !summaryTextarea ||
        !transactionTextarea ||
        !decodedEventsTextarea ||
        !poolsTextarea ||
        !pairsTextarea ||
        !launchAttributionsTextarea ||
        !poolOriginsTextarea ||
        !walletsTextarea ||
        !tradeEventsTextarea ||
        !pairMetricsTextarea ||
        !pairCandlesTextarea ||
        !pairAnalyticSignalsTextarea ||
        !logTextarea
    ) {
        console.error("demo_pipeline DOM is incomplete");
        return;
    }

    const candlesChart = echarts.init(candlesChartElement);
    setEmptyCandlesChart(candlesChart, candlesChartMeta, "Aucune candle disponible.");

    window.addEventListener("resize", () => {
        candlesChart.resize();
    });

    chartPairSelect.addEventListener("change", () => {
        const groups = parsePairCandleGroups(pairCandlesTextarea.value);
        refreshCandlesSelectors(groups, chartPairSelect, chartTimeframeSelect);
        renderSelectedCandlesChart(
            candlesChart,
            candlesChartMeta,
            groups,
            chartPairSelect,
            chartTimeframeSelect,
        );
    });

    chartTimeframeSelect.addEventListener("change", () => {
        const groups = parsePairCandleGroups(pairCandlesTextarea.value);
        renderSelectedCandlesChart(
            candlesChart,
            candlesChartMeta,
            groups,
            chartPairSelect,
            chartTimeframeSelect,
        );
    });

    clearButton.addEventListener("click", () => {
        clearInspection(
            backfillTextarea,
            summaryTextarea,
            transactionTextarea,
            decodedEventsTextarea,
            poolsTextarea,
            pairsTextarea,
            launchAttributionsTextarea,
            poolOriginsTextarea,
            walletsTextarea,
            tradeEventsTextarea,
            pairMetricsTextarea,
            pairCandlesTextarea,
            pairAnalyticSignalsTextarea,
        );
        backfillTokenMintInput.value = "";
        backfillHttpRoleInput.value = "history_backfill";
        backfillMintLimitInput.value = "50";
        backfillPoolLimitInput.value = "50";
        signatureInput.value = "";
        customTimeframeInput.value = "";
        tokenMintInput.value = "";
        pairIdInput.value = "";
        poolAddressInput.value = "";
        backfillPoolAddressInput.value = "";
        backfillPoolOnlyLimitInput.value = "50";
        chartPairSelect.innerHTML = `<option value="">Aucune</option>`;
        chartTimeframeSelect.innerHTML = `<option value="">Aucun</option>`;
        setEmptyCandlesChart(candlesChart, candlesChartMeta, "Aucune candle disponible.");
        appendLogLine(logTextarea, "[ui] inspection state cleared");
    });

    backfillPoolButton.addEventListener("click", async () => {
        const poolAddress = backfillPoolAddressInput.value.trim();
        if (poolAddress === "") {
            appendLogLine(logTextarea, "[ui] backfill pool address is required");
            return;
        }

        const poolSignatureLimit = readPositiveIntegerInput(
            backfillPoolOnlyLimitInput,
            logTextarea,
            "poolSignatureLimit",
        );
        if (poolSignatureLimit === undefined) {
            return;
        }

        const httpRoleText = backfillHttpRoleInput.value.trim();
        const httpRole = httpRoleText === "" ? null : httpRoleText;

        appendLogLine(
            logTextarea,
            `[ui] launching pool backfill for '${poolAddress}' with role '${httpRole ?? "history_backfill"}' (pool=${poolSignatureLimit})`,
        );

        const request: DemoPipelineBackfillPoolRequest = {
            poolAddress,
            httpRole,
            poolSignatureLimit,
        };

        try {
            const payload = await invoke<DemoPipelineBackfillPoolPayload>(
                "demo_pipeline_backfill_pool_address",
                { request },
            );

            backfillTextarea.value = payload.backfillJson;
            appendLogLine(
                logTextarea,
                `[ui] pool backfill completed for '${payload.poolAddress}' with role '${payload.httpRole}'`,
            );

            if (!payload.poolPersistedAfterBackfill) {
                appendLogLine(
                    logTextarea,
                    `[ui] backfill completed but pool '${payload.poolAddress}' is still absent from persisted pool objects; automatic pool inspection skipped`,
                );
                return;
            }

            const inspectRequest: DemoPipelineInspectPoolRequest = {
                poolAddress: payload.poolAddress,
                customTimeframeSeconds: null,
            };

            try {
                const inspectPayload = await invoke<DemoPipelineInspectPayload>(
                    "demo_pipeline_inspect_pool_address",
                    { request: inspectRequest },
                );

                applyInspectionPayload(
                    inspectPayload,
                    summaryTextarea,
                    transactionTextarea,
                    decodedEventsTextarea,
                    poolsTextarea,
                    pairsTextarea,
                    launchAttributionsTextarea,
                    poolOriginsTextarea,
                    walletsTextarea,
                    tradeEventsTextarea,
                    pairMetricsTextarea,
                    pairCandlesTextarea,
                    pairAnalyticSignalsTextarea,
                    candlesChart,
                    candlesChartMeta,
                    chartPairSelect,
                    chartTimeframeSelect,
                );

                appendLogLine(
                    logTextarea,
                    `[ui] pool inspection refreshed after backfill for '${payload.poolAddress}'`,
                );
            } catch (error) {
                appendLogLine(
                    logTextarea,
                    `[ui] backfill completed but automatic pool inspection failed for '${payload.poolAddress}': ${String(error)}`,
                );
            }
        } catch (error) {
            appendLogLine(logTextarea, `[ui] pool backfill error: ${String(error)}`);
        }
    });

    clearLogButton.addEventListener("click", () => {
        logTextarea.value = "";
    });

    inspectButton.addEventListener("click", async () => {
        const signature = signatureInput.value.trim();
        if (signature === "") {
            appendLogLine(logTextarea, "[ui] signature is required");
            return;
        }

        let customTimeframeSeconds: bigint | null = null;
        const customTimeframeText = customTimeframeInput.value.trim();
        if (customTimeframeText !== "") {
            const parsed = Number.parseInt(customTimeframeText, 10);
            if (Number.isNaN(parsed) || parsed <= 0) {
                appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
                return;
            }
            customTimeframeSeconds = BigInt(parsed);
        }

        appendLogLine(
            logTextarea,
            `[ui] inspecting signature '${signature}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
        );

        const request: DemoPipelineInspectRequest = {
            signature,
            customTimeframeSeconds,
        };

        try {
            const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_signature", { request });

            applyInspectionPayload(
                payload,
                summaryTextarea,
                transactionTextarea,
                decodedEventsTextarea,
                poolsTextarea,
                pairsTextarea,
                launchAttributionsTextarea,
                poolOriginsTextarea,
                walletsTextarea,
                tradeEventsTextarea,
                pairMetricsTextarea,
                pairCandlesTextarea,
                pairAnalyticSignalsTextarea,
                candlesChart,
                candlesChartMeta,
                chartPairSelect,
                chartTimeframeSelect,
            );

            appendLogLine(logTextarea, `[ui] inspection completed for '${payload.signature}'`);
        } catch (error) {
            appendLogLine(logTextarea, `[ui] inspect error: ${String(error)}`);
        }
    });

    inspectTokenButton.addEventListener("click", async () => {
        const tokenMint = tokenMintInput.value.trim();
        if (tokenMint === "") {
            appendLogLine(logTextarea, "[ui] token mint is required");
            return;
        }

        let customTimeframeSeconds: bigint | null = null;
        const customTimeframeText = customTimeframeInput.value.trim();
        if (customTimeframeText !== "") {
            const parsed = Number.parseInt(customTimeframeText, 10);
            if (Number.isNaN(parsed) || parsed <= 0) {
                appendLogLine(logTextarea, `[ui] invalid custom timeframe '${customTimeframeText}'`);
                return;
            }
            customTimeframeSeconds = BigInt(parsed);
        }

        appendLogLine(
            logTextarea,
            `[ui] inspecting token mint '${tokenMint}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
        );

        const request: DemoPipelineInspectTokenRequest = {
            tokenMint,
            customTimeframeSeconds,
        };

        try {
            const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_token_mint", { request });

            applyInspectionPayload(
                payload,
                summaryTextarea,
                transactionTextarea,
                decodedEventsTextarea,
                poolsTextarea,
                pairsTextarea,
                launchAttributionsTextarea,
                poolOriginsTextarea,
                walletsTextarea,
                tradeEventsTextarea,
                pairMetricsTextarea,
                pairCandlesTextarea,
                pairAnalyticSignalsTextarea,
                candlesChart,
                candlesChartMeta,
                chartPairSelect,
                chartTimeframeSelect,
            );

            appendLogLine(logTextarea, `[ui] token inspection completed for '${payload.signature}'`);
        } catch (error) {
            appendLogLine(logTextarea, `[ui] token inspect error: ${String(error)}`);
        }
    });

    inspectPairButton.addEventListener("click", async () => {
        const pairIdText = pairIdInput.value.trim();
        if (pairIdText === "") {
            appendLogLine(logTextarea, "[ui] pair id is required");
            return;
        }

        const parsedPairId = Number.parseInt(pairIdText, 10);
        if (Number.isNaN(parsedPairId) || parsedPairId <= 0) {
            appendLogLine(logTextarea, `[ui] invalid pair id '${pairIdText}'`);
            return;
        }

        const customTimeframeSeconds = readCustomTimeframeSeconds(customTimeframeInput, logTextarea);
        if (customTimeframeSeconds === undefined) {
            return;
        }

        appendLogLine(
            logTextarea,
            `[ui] inspecting pair id '${parsedPairId}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
        );

        const request: DemoPipelineInspectPairRequest = {
            pairId: BigInt(parsedPairId),
            customTimeframeSeconds,
        };

        try {
            const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pair_id", { request });

            applyInspectionPayload(
                payload,
                summaryTextarea,
                transactionTextarea,
                decodedEventsTextarea,
                poolsTextarea,
                pairsTextarea,
                launchAttributionsTextarea,
                poolOriginsTextarea,
                walletsTextarea,
                tradeEventsTextarea,
                pairMetricsTextarea,
                pairCandlesTextarea,
                pairAnalyticSignalsTextarea,
                candlesChart,
                candlesChartMeta,
                chartPairSelect,
                chartTimeframeSelect,
            );

            appendLogLine(logTextarea, `[ui] pair inspection completed for '${payload.signature}'`);
        } catch (error) {
            appendLogLine(logTextarea, `[ui] pair inspect error: ${String(error)}`);
        }
    });

    inspectPoolButton.addEventListener("click", async () => {
        const poolAddress = poolAddressInput.value.trim();
        if (poolAddress === "") {
            appendLogLine(logTextarea, "[ui] pool address is required");
            return;
        }

        const customTimeframeSeconds = readCustomTimeframeSeconds(customTimeframeInput, logTextarea);
        if (customTimeframeSeconds === undefined) {
            return;
        }

        appendLogLine(
            logTextarea,
            `[ui] inspecting pool '${poolAddress}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
        );

        const request: DemoPipelineInspectPoolRequest = {
            poolAddress,
            customTimeframeSeconds,
        };

        try {
            const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pool_address", { request });

            applyInspectionPayload(
                payload,
                summaryTextarea,
                transactionTextarea,
                decodedEventsTextarea,
                poolsTextarea,
                pairsTextarea,
                launchAttributionsTextarea,
                poolOriginsTextarea,
                walletsTextarea,
                tradeEventsTextarea,
                pairMetricsTextarea,
                pairCandlesTextarea,
                pairAnalyticSignalsTextarea,
                candlesChart,
                candlesChartMeta,
                chartPairSelect,
                chartTimeframeSelect,
            );

            appendLogLine(logTextarea, `[ui] pool inspection completed for '${payload.signature}'`);
        } catch (error) {
            appendLogLine(logTextarea, `[ui] pool inspect error: ${String(error)}`);
        }
    });

    backfillTokenButton.addEventListener("click", async () => {
        const tokenMint = backfillTokenMintInput.value.trim();
        if (tokenMint === "") {
            appendLogLine(logTextarea, "[ui] backfill token mint is required");
            return;
        }

        const mintSignatureLimit = readPositiveIntegerInput(
            backfillMintLimitInput,
            logTextarea,
            "mintSignatureLimit",
        );
        if (mintSignatureLimit === undefined) {
            return;
        }

        const poolSignatureLimit = readPositiveIntegerInput(
            backfillPoolLimitInput,
            logTextarea,
            "poolSignatureLimit",
        );
        if (poolSignatureLimit === undefined) {
            return;
        }

        const httpRoleText = backfillHttpRoleInput.value.trim();
        const httpRole = httpRoleText === "" ? null : httpRoleText;

        appendLogLine(
            logTextarea,
            `[ui] launching token backfill for '${tokenMint}' with role '${httpRole ?? "history_backfill"}' (mint=${mintSignatureLimit}, pool=${poolSignatureLimit})`,
        );

        const request: DemoPipelineBackfillTokenRequest = {
            tokenMint,
            httpRole,
            mintSignatureLimit,
            poolSignatureLimit,
        };

        try {
            const payload = await invoke<DemoPipelineBackfillTokenPayload>(
                "demo_pipeline_backfill_token_mint",
                { request },
            );

            backfillTextarea.value = payload.backfillJson;
            appendLogLine(
                logTextarea,
                `[ui] token backfill completed for '${payload.tokenMint}' with role '${payload.httpRole}'`,
            );

            if (!payload.tokenPersistedAfterBackfill) {
                appendLogLine(
                    logTextarea,
                    `[ui] backfill completed but token '${payload.tokenMint}' is still absent from persisted token objects; automatic token inspection skipped`,
                );
                return;
            }

            const inspectRequest: DemoPipelineInspectTokenRequest = {
                tokenMint: payload.tokenMint,
                customTimeframeSeconds: null,
            };

            try {
                const inspectPayload = await invoke<DemoPipelineInspectPayload>(
                    "demo_pipeline_inspect_token_mint",
                    { request: inspectRequest },
                );

                summaryTextarea.value = inspectPayload.summaryJson;
                transactionTextarea.value = inspectPayload.transactionJson;
                decodedEventsTextarea.value = inspectPayload.decodedEventsJson;
                poolsTextarea.value = inspectPayload.poolsJson;
                pairsTextarea.value = inspectPayload.pairsJson;
                launchAttributionsTextarea.value = inspectPayload.launchAttributionsJson;
                poolOriginsTextarea.value = inspectPayload.poolOriginsJson;
                walletsTextarea.value = inspectPayload.walletsJson;
                tradeEventsTextarea.value = inspectPayload.tradeEventsJson;
                pairMetricsTextarea.value = inspectPayload.pairMetricsJson;
                pairCandlesTextarea.value = inspectPayload.pairCandlesJson;
                pairAnalyticSignalsTextarea.value = inspectPayload.pairAnalyticSignalsJson;

                appendLogLine(
                    logTextarea,
                    `[ui] token inspection refreshed after backfill for '${payload.tokenMint}'`,
                );
            } catch (error) {
                appendLogLine(
                    logTextarea,
                    `[ui] backfill completed but automatic token inspection failed for '${payload.tokenMint}': ${String(error)}`,
                );
            }
        } catch (error) {
            appendLogLine(logTextarea, `[ui] token backfill error: ${String(error)}`);
        }
    });
});