// file: kb_lib/src/db/queries/local_pipeline_diagnostics.rs

//! Local pipeline diagnostics SQL queries.

/// Returns global local-pipeline diagnostic counters.
pub async fn query_local_pipeline_diagnostic_get_counters(
    database: &crate::Database,
) -> Result<crate::LocalPipelineDiagnosticCountersDto, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let row_result =
                sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalPipelineDiagnosticCountersRow>(
                    r#"
SELECT
    (SELECT COUNT(*) FROM k_sol_chain_transactions) AS transaction_count,
    (SELECT COUNT(*) FROM k_sol_chain_transactions WHERE err_json IS NULL) AS ok_transaction_count,
    (SELECT COUNT(*) FROM k_sol_chain_transactions WHERE err_json IS NOT NULL) AS failed_transaction_count,
    (SELECT COUNT(*) FROM k_sol_dex_decoded_events) AS decoded_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events
        WHERE json_extract(payload_json, '$.tradeCandidate') = 1
    ) AS decoded_trade_candidate_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events
        WHERE json_extract(payload_json, '$.candleCandidate') = 1
    ) AS decoded_candle_candidate_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events
        WHERE COALESCE(json_extract(payload_json, '$.nonTradeUseful'), 0) = 1
           OR COALESCE(json_extract(payload_json, '$.eventActionability'), '') = 'non_trade_useful'
    ) AS decoded_non_trade_useful_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events
        WHERE COALESCE(json_extract(payload_json, '$.eventActionability'), '') = 'non_actionable_trade'
           OR (
                COALESCE(json_extract(payload_json, '$.eventActionability'), '') = ''
                AND COALESCE(json_extract(payload_json, '$.eventCategory'), '') = 'trade'
                AND COALESCE(json_extract(payload_json, '$.tradeCandidate'), 0) = 0
                AND COALESCE(json_extract(payload_json, '$.transactionFailed'), 0) = 0
           )
    ) AS decoded_non_actionable_trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events
        WHERE COALESCE(json_extract(payload_json, '$.eventCategory'), 'unknown') = 'unknown'
    ) AS decoded_unknown_event_count,
    (SELECT COUNT(*) FROM k_sol_liquidity_events) AS liquidity_event_count,
    (SELECT COUNT(*) FROM k_sol_pool_lifecycle_events) AS pool_lifecycle_event_count,
    (SELECT COUNT(*) FROM k_sol_fee_events) AS fee_event_count,
    (SELECT COUNT(*) FROM k_sol_reward_events) AS reward_event_count,
    (SELECT COUNT(*) FROM k_sol_pool_admin_events) AS pool_admin_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
    ) AS missing_trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
    ) AS decoded_trade_candidate_without_trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
          AND ct.id IS NOT NULL
          AND ct.err_json IS NULL
          AND dde.pool_account IS NOT NULL
          AND dde.token_a_mint IS NOT NULL
          AND dde.token_b_mint IS NOT NULL
          AND EXISTS (
            SELECT 1
            FROM k_sol_pools p
            JOIN k_sol_pairs pair ON pair.pool_id = p.id
            WHERE p.address = dde.pool_account
          )
    ) AS decoded_trade_candidate_without_trade_event_on_ok_transaction_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
          AND ct.id IS NOT NULL
          AND ct.err_json IS NOT NULL
    ) AS decoded_trade_candidate_without_trade_event_on_failed_transaction_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
          AND ct.id IS NOT NULL
          AND ct.err_json IS NULL
          AND dde.pool_account IS NOT NULL
          AND dde.token_a_mint IS NOT NULL
          AND dde.token_b_mint IS NOT NULL
          AND EXISTS (
              SELECT 1
              FROM k_sol_pools p
              JOIN k_sol_pairs pair ON pair.pool_id = p.id
              WHERE p.address = dde.pool_account
          )
    ) AS actionable_missing_trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
          AND ct.id IS NOT NULL
          AND ct.err_json IS NOT NULL
    ) AS ignored_failed_transaction_trade_candidate_count,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde
        LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
        WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
          AND te.id IS NULL
          AND (
                (
                    json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
                    AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
                )
             OR (
                    json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
                    AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
                )
          )
    ) AS decoded_trade_candidate_without_amount_payload_count,
    (SELECT COUNT(*) FROM k_sol_trade_events) AS trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_trade_events
        WHERE base_amount_raw IS NULL
           OR quote_amount_raw IS NULL
           OR price_quote_per_base IS NULL
           OR CAST(base_amount_raw AS INTEGER) <= 0
           OR CAST(quote_amount_raw AS INTEGER) <= 0
           OR price_quote_per_base <= 0
    ) AS invalid_trade_event_count,
    (SELECT COUNT(*) FROM k_sol_pair_candles) AS pair_candle_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT decoded_event_id
            FROM k_sol_trade_events
            WHERE decoded_event_id IS NOT NULL
            GROUP BY decoded_event_id
            HAVING COUNT(*) > 1
        )
    ) AS duplicate_decoded_event_trade_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT signature, pair_id
            FROM k_sol_trade_events
            GROUP BY signature, pair_id
            HAVING COUNT(*) > 1
        )
    ) AS multi_trade_signature_pair_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair_id, timeframe_seconds, bucket_start_unix
            FROM k_sol_pair_candles
            GROUP BY pair_id, timeframe_seconds, bucket_start_unix
            HAVING COUNT(*) > 1
        )
    ) AS duplicate_candle_bucket_count,
    (SELECT COUNT(*) FROM k_sol_tokens) AS token_count,
    (
        SELECT COUNT(*)
        FROM k_sol_tokens
        WHERE symbol IS NULL
           OR TRIM(symbol) = ''
           OR name IS NULL
           OR TRIM(name) = ''
    ) AS token_metadata_missing_count,
    (
        SELECT COUNT(DISTINCT token.id)
        FROM k_sol_tokens token
        JOIN (
            SELECT pair.base_token_id AS token_id
            FROM k_sol_pairs pair
            JOIN k_sol_trade_events te ON te.pair_id = pair.id
            UNION
            SELECT pair.quote_token_id AS token_id
            FROM k_sol_pairs pair
            JOIN k_sol_trade_events te ON te.pair_id = pair.id
        ) tradable_pair_token ON tradable_pair_token.token_id = token.id
        WHERE token.symbol IS NULL
           OR TRIM(token.symbol) = ''
           OR token.name IS NULL
           OR TRIM(token.name) = ''
    ) AS tradable_token_metadata_missing_count,
    (
        SELECT COUNT(DISTINCT quote_token.id)
        FROM k_sol_pairs pair
        JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
        WHERE quote_token.symbol IS NULL
           OR TRIM(quote_token.symbol) = ''
           OR quote_token.name IS NULL
           OR TRIM(quote_token.name) = ''
    ) AS quote_token_metadata_missing_count,
    (
        SELECT COUNT(*)
        FROM k_sol_pairs pair
        JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
        JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
        WHERE pair.symbol IS NULL
           OR TRIM(pair.symbol) = ''
           OR pair.symbol = base_token.mint || '/' || quote_token.mint
           OR instr(pair.symbol, base_token.mint) > 0
           OR instr(pair.symbol, quote_token.mint) > 0
    ) AS pair_symbol_fallback_count,
    (
        SELECT COUNT(*)
        FROM k_sol_pairs pair
        JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
        JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
        WHERE pair.symbol IS NOT NULL
          AND TRIM(pair.symbol) != ''
          AND pair.symbol != base_token.mint || '/' || quote_token.mint
          AND instr(pair.symbol, base_token.mint) = 0
          AND instr(pair.symbol, quote_token.mint) = 0
    ) AS pair_symbol_resolved_count,
    (
        SELECT COUNT(*)
        FROM k_sol_pairs pair
        JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
        WHERE quote_token.mint = 'So11111111111111111111111111111111111111112'
    ) AS wsol_quote_pair_count,
    (
        SELECT COUNT(*)
        FROM k_sol_pairs pair
        JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
        WHERE quote_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        )
    ) AS stable_quote_pair_count,
    (SELECT COUNT(*) FROM k_sol_pools) AS pool_count,
    (SELECT COUNT(*) FROM k_sol_pairs) AS pair_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT te.id) = 0
        )
    ) AS literal_pair_without_trade_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) = 0
        )
    ) AS literal_pair_without_candle_count,
    (
        SELECT COUNT(DISTINCT pair_id)
        FROM k_sol_trade_events
    ) AS trade_materialized_pair_count,
    (
        SELECT COUNT(DISTINCT pair_id)
        FROM k_sol_pair_candles
    ) AS candle_materialized_pair_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
              AND ct.err_json IS NULL
            GROUP BY pair.id
        )
    ) AS actionable_pair_count,
    (
        SELECT COUNT(DISTINCT timeframe_seconds)
        FROM k_sol_pair_candles
    ) AS candle_bucket_timeframe_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
            LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
              AND te.id IS NULL
            GROUP BY pair.id
            HAVING COUNT(DISTINCT CASE
                WHEN ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                 AND dde.pool_account IS NOT NULL
                 AND dde.token_a_mint IS NOT NULL
                 AND dde.token_b_mint IS NOT NULL
                 AND EXISTS (
                    SELECT 1
                    FROM k_sol_pools known_pool
                    JOIN k_sol_pairs known_pair ON known_pair.pool_id = known_pool.id
                    WHERE known_pool.address = dde.pool_account
                 )
                THEN dde.id END) = 0
               AND COUNT(DISTINCT dde.id) > 0
        )
    ) AS non_actionable_pair_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT te.id) = 0
        )
    ) AS blocking_pair_without_trade_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT te.id) = 0
        )
    ) AS pair_without_trade_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.candleCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) = 0
                )
            ) AS blocking_pair_without_candle_count,
    (
        SELECT COUNT(*)
        FROM (
            SELECT pair.id
            FROM k_sol_pairs pair
            JOIN k_sol_pools p ON p.id = pair.pool_id
            LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
            LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
            LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
            GROUP BY pair.id
            HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.candleCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) = 0
                )
            ) AS pair_without_candle_count
                    "#,
                )
                .fetch_one(pool)
                .await;
            let row = match row_result {
                Ok(row) => row,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot read local pipeline diagnostic counters on sqlite: {}",
                        error
                    )));
                },
            };
            return Ok(crate::LocalPipelineDiagnosticCountersDto {
                transaction_count: row.transaction_count,
                ok_transaction_count: row.ok_transaction_count,
                failed_transaction_count: row.failed_transaction_count,
                decoded_event_count: row.decoded_event_count,
                decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                decoded_candle_candidate_count: row.decoded_candle_candidate_count,
                decoded_non_trade_useful_event_count: row.decoded_non_trade_useful_event_count,
                decoded_non_actionable_trade_event_count: row
                    .decoded_non_actionable_trade_event_count,
                decoded_unknown_event_count: row.decoded_unknown_event_count,
                liquidity_event_count: row.liquidity_event_count,
                pool_lifecycle_event_count: row.pool_lifecycle_event_count,
                fee_event_count: row.fee_event_count,
                reward_event_count: row.reward_event_count,
                pool_admin_event_count: row.pool_admin_event_count,
                missing_trade_event_count: row.missing_trade_event_count,
                decoded_trade_candidate_without_trade_event_count: row
                    .decoded_trade_candidate_without_trade_event_count,
                decoded_trade_candidate_without_trade_event_on_ok_transaction_count: row
                    .decoded_trade_candidate_without_trade_event_on_ok_transaction_count,
                decoded_trade_candidate_without_trade_event_on_failed_transaction_count: row
                    .decoded_trade_candidate_without_trade_event_on_failed_transaction_count,
                decoded_trade_candidate_without_amount_payload_count: row
                    .decoded_trade_candidate_without_amount_payload_count,
                trade_event_count: row.trade_event_count,
                invalid_trade_event_count: row.invalid_trade_event_count,
                pair_candle_count: row.pair_candle_count,
                duplicate_decoded_event_trade_count: row.duplicate_decoded_event_trade_count,
                multi_trade_signature_pair_count: row.multi_trade_signature_pair_count,
                duplicate_candle_bucket_count: row.duplicate_candle_bucket_count,
                token_count: row.token_count,
                token_metadata_missing_count: row.token_metadata_missing_count,
                tradable_token_metadata_missing_count: row.tradable_token_metadata_missing_count,
                quote_token_metadata_missing_count: row.quote_token_metadata_missing_count,
                pair_symbol_fallback_count: row.pair_symbol_fallback_count,
                pair_symbol_resolved_count: row.pair_symbol_resolved_count,
                wsol_quote_pair_count: row.wsol_quote_pair_count,
                stable_quote_pair_count: row.stable_quote_pair_count,
                pool_count: row.pool_count,
                pair_count: row.pair_count,
                literal_pair_without_trade_count: row.literal_pair_without_trade_count,
                literal_pair_without_candle_count: row.literal_pair_without_candle_count,
                trade_materialized_pair_count: row.trade_materialized_pair_count,
                candle_materialized_pair_count: row.candle_materialized_pair_count,
                actionable_pair_count: row.actionable_pair_count,
                candle_bucket_timeframe_count: row.candle_bucket_timeframe_count,
                non_actionable_pair_count: row.non_actionable_pair_count,
                blocking_pair_without_trade_count: row.blocking_pair_without_trade_count,
                blocking_pair_without_candle_count: row.blocking_pair_without_candle_count,
                pair_without_trade_count: row.pair_without_trade_count,
                pair_without_candle_count: row.pair_without_candle_count,
                actionable_missing_trade_event_count: row.actionable_missing_trade_event_count,
                ignored_failed_transaction_trade_candidate_count: row
                    .ignored_failed_transaction_trade_candidate_count,
            });
        },
    }
}

/// Lists local DEX diagnostic summaries.
pub async fn query_local_pipeline_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalDexDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalDexDiagnosticSummaryRow>(
                r#"
WITH decoded AS (
    SELECT
        protocol_name AS dex_code,
        COUNT(*) AS decoded_event_count,
        SUM(CASE WHEN json_extract(payload_json, '$.tradeCandidate') = 1 THEN 1 ELSE 0 END) AS decoded_trade_candidate_count,
        SUM(CASE WHEN json_extract(payload_json, '$.candleCandidate') = 1 THEN 1 ELSE 0 END) AS decoded_candle_candidate_count
    FROM k_sol_dex_decoded_events
    GROUP BY protocol_name
),
dex_pool_pairs AS (
    SELECT
        d.code AS dex_code,
        COUNT(DISTINCT p.id) AS pool_count,
        COUNT(DISTINCT pair.id) AS pair_count
    FROM k_sol_dexes d
    LEFT JOIN k_sol_pools p ON p.dex_id = d.id
    LEFT JOIN k_sol_pairs pair ON pair.pool_id = p.id
    GROUP BY d.code
),
trades AS (
    SELECT
        d.code AS dex_code,
        COUNT(te.id) AS trade_event_count
    FROM k_sol_trade_events te
    JOIN k_sol_pairs pair ON pair.id = te.pair_id
    JOIN k_sol_pools p ON p.id = pair.pool_id
    JOIN k_sol_dexes d ON d.id = p.dex_id
    GROUP BY d.code
),
candles AS (
    SELECT
        d.code AS dex_code,
        COUNT(pc.pair_id) AS pair_candle_count
    FROM k_sol_pair_candles pc
    JOIN k_sol_pairs pair ON pair.id = pc.pair_id
    JOIN k_sol_pools p ON p.id = pair.pool_id
    JOIN k_sol_dexes d ON d.id = p.dex_id
    GROUP BY d.code
)
SELECT
    COALESCE(dex_pool_pairs.dex_code, decoded.dex_code) AS dex_code,
    COALESCE(dex_pool_pairs.pool_count, 0) AS pool_count,
    COALESCE(dex_pool_pairs.pair_count, 0) AS pair_count,
    COALESCE(decoded.decoded_event_count, 0) AS decoded_event_count,
    COALESCE(decoded.decoded_trade_candidate_count, 0) AS decoded_trade_candidate_count,
    COALESCE(decoded.decoded_candle_candidate_count, 0) AS decoded_candle_candidate_count,
    COALESCE(trades.trade_event_count, 0) AS trade_event_count,
    COALESCE(candles.pair_candle_count, 0) AS pair_candle_count
FROM dex_pool_pairs
LEFT JOIN decoded ON decoded.dex_code = dex_pool_pairs.dex_code
LEFT JOIN trades ON trades.dex_code = dex_pool_pairs.dex_code
LEFT JOIN candles ON candles.dex_code = dex_pool_pairs.dex_code

UNION

SELECT
    decoded.dex_code AS dex_code,
    0 AS pool_count,
    0 AS pair_count,
    decoded.decoded_event_count AS decoded_event_count,
    decoded.decoded_trade_candidate_count AS decoded_trade_candidate_count,
    decoded.decoded_candle_candidate_count AS decoded_candle_candidate_count,
    0 AS trade_event_count,
    0 AS pair_candle_count
FROM decoded
LEFT JOIN dex_pool_pairs ON dex_pool_pairs.dex_code = decoded.dex_code
WHERE dex_pool_pairs.dex_code IS NULL

ORDER BY dex_code
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local dex diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalDexDiagnosticSummaryDto {
                    dex_code: row.dex_code,
                    pool_count: row.pool_count,
                    pair_count: row.pair_count,
                    decoded_event_count: row.decoded_event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    decoded_candle_candidate_count: row.decoded_candle_candidate_count,
                    trade_event_count: row.trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists observed Raydium program instruction diagnostics.
pub async fn query_local_raydium_program_instruction_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto>, crate::Error>
{
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalRaydiumProgramInstructionDiagnosticSummaryRow,
            >(
                r#"
SELECT
    ix.program_id AS program_id,
    COUNT(ix.id) AS instruction_count,
    COUNT(DISTINCT tx.signature) AS transaction_count,
    MAX(tx.slot) AS latest_slot,
    (
        SELECT tx_latest.signature
        FROM k_sol_chain_instructions ix_latest
        JOIN k_sol_chain_transactions tx_latest
            ON tx_latest.id = ix_latest.transaction_id
        WHERE ix_latest.program_id = ix.program_id
        ORDER BY
            tx_latest.slot DESC,
            tx_latest.id DESC,
            ix_latest.instruction_index DESC,
            COALESCE(ix_latest.inner_instruction_index, -1) DESC
        LIMIT 1
    ) AS latest_signature
FROM k_sol_chain_instructions ix
JOIN k_sol_chain_transactions tx
    ON tx.id = ix.transaction_id
WHERE ix.program_id IN (
    'CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C',
    'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK',
    '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8',
    '5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h',
    'routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS',
    'LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj'
)
GROUP BY ix.program_id
ORDER BY transaction_count DESC, instruction_count DESC, program_id
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local Raydium program instruction diagnostics on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto {
                    program_id: row.program_id,
                    instruction_count: row.instruction_count,
                    transaction_count: row.transaction_count,
                    latest_slot: row.latest_slot,
                    latest_signature: row.latest_signature,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists local pair diagnostic summaries.
pub async fn query_local_pair_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalPairDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalPairDiagnosticSummaryRow>(
                r#"
SELECT
    pair.id AS pair_id,
    p.address AS pool_address,
    d.code AS dex_code,
    base_token.mint AS base_mint,
    base_token.symbol AS base_symbol,
    quote_token.mint AS quote_mint,
    quote_token.symbol AS quote_symbol,
    COALESCE(
        pair.symbol,
        CASE
            WHEN base_token.symbol IS NOT NULL
             AND base_token.symbol != ''
             AND quote_token.symbol IS NOT NULL
             AND quote_token.symbol != ''
            THEN base_token.symbol || '/' || quote_token.symbol
            ELSE NULL
        END
    ) AS pair_symbol,
    COUNT(DISTINCT dde.id) AS decoded_event_count,
    COUNT(DISTINCT CASE WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1 THEN dde.id END) AS decoded_trade_candidate_count,
    COUNT(DISTINCT CASE WHEN json_extract(dde.payload_json, '$.candleCandidate') = 1 THEN dde.id END) AS decoded_candle_candidate_count,
    COUNT(DISTINCT te.id) AS trade_event_count,
    COUNT(
        DISTINCT CASE
            WHEN te.id IS NOT NULL
             AND (
                    te.base_amount_raw IS NULL
                 OR te.quote_amount_raw IS NULL
                 OR te.price_quote_per_base IS NULL
                 OR CAST(te.base_amount_raw AS INTEGER) <= 0
                 OR CAST(te.quote_amount_raw AS INTEGER) <= 0
                 OR te.price_quote_per_base <= 0
             )
            THEN te.id
        END
    ) AS invalid_trade_event_count,
    COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) AS pair_candle_count,
    (
        SELECT te_last.price_quote_per_base
        FROM k_sol_trade_events te_last
        WHERE te_last.pair_id = pair.id
        ORDER BY te_last.id DESC
        LIMIT 1
    ) AS last_price_quote_per_base,
    CASE
        WHEN COUNT(DISTINCT te.id) = 0 THEN 'non_trade_materialized'
        WHEN quote_token.mint = 'So11111111111111111111111111111111111111112' THEN 'direct_wsol_quote'
        WHEN quote_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        ) THEN 'direct_stable_quote'
        WHEN base_token.mint = 'So11111111111111111111111111111111111111112' THEN 'inverse_wsol_base'
        WHEN base_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        ) THEN 'inverse_stable_base'
        WHEN quote_token.mint IS NULL OR quote_token.mint = '' THEN 'unknown_quote'
        ELSE 'cross_quote_requires_router'
    END AS pair_trading_readiness,
    CASE
        WHEN quote_token.mint = 'So11111111111111111111111111111111111111112' THEN 'wsol'
        WHEN quote_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        ) THEN 'stable'
        WHEN quote_token.mint IS NULL OR quote_token.mint = '' THEN 'unknown'
        ELSE 'other'
    END AS quote_asset_class,
    CASE
        WHEN COUNT(DISTINCT te.id) = 0 THEN 0
        WHEN quote_token.mint IS NULL OR quote_token.mint = '' THEN 1
        WHEN quote_token.mint = 'So11111111111111111111111111111111111111112' THEN 0
        WHEN quote_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        ) THEN 0
        WHEN base_token.mint = 'So11111111111111111111111111111111111111112' THEN 0
        WHEN base_token.mint IN (
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
            'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
            'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
        ) THEN 0
        ELSE 1
    END AS trading_route_required
FROM k_sol_pairs pair
JOIN k_sol_pools p ON p.id = pair.pool_id
JOIN k_sol_dexes d ON d.id = p.dex_id
JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
GROUP BY
    pair.id,
    p.address,
    d.code,
    base_token.mint,
    base_token.symbol,
    quote_token.mint,
    quote_token.symbol,
    pair.symbol
ORDER BY pair.id
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local pair diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalPairDiagnosticSummaryDto {
                    pair_id: row.pair_id,
                    pool_address: row.pool_address,
                    dex_code: row.dex_code,
                    base_mint: row.base_mint,
                    base_symbol: row.base_symbol,
                    quote_mint: row.quote_mint,
                    quote_symbol: row.quote_symbol,
                    pair_symbol: row.pair_symbol,
                    decoded_event_count: row.decoded_event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    decoded_candle_candidate_count: row.decoded_candle_candidate_count,
                    trade_event_count: row.trade_event_count,
                    invalid_trade_event_count: row.invalid_trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                    last_price_quote_per_base: row.last_price_quote_per_base,
                    pair_trading_readiness: row.pair_trading_readiness,
                    quote_asset_class: row.quote_asset_class,
                    trading_route_required: row.trading_route_required != 0,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists local pair materialization/actionability summaries.
pub async fn query_local_pair_actionability_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalPairActionabilityDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalPairActionabilityDiagnosticSummaryRow,
            >(
                r#"
WITH pair_state AS (
    SELECT
        pair.id AS pair_id,
        COUNT(DISTINCT dde.id) AS decoded_event_count,
        COUNT(DISTINCT CASE WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1 THEN dde.id END) AS decoded_trade_candidate_count,
        COUNT(DISTINCT CASE
            WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1
             AND ct.id IS NOT NULL
             AND ct.err_json IS NULL
            THEN dde.id END) AS actionable_trade_candidate_count,
        COUNT(DISTINCT CASE
            WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1
             AND ct.id IS NOT NULL
             AND ct.err_json IS NOT NULL
            THEN dde.id END) AS failed_trade_candidate_count,
        COUNT(DISTINCT te.id) AS trade_event_count,
        COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) AS pair_candle_count
    FROM k_sol_pairs pair
    JOIN k_sol_pools p ON p.id = pair.pool_id
    LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
    LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
    LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
    LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
    GROUP BY pair.id
), classified AS (
    SELECT
        CASE
            WHEN trade_event_count > 0 THEN 'trade_materialized'
            WHEN actionable_trade_candidate_count > 0 THEN 'actionable_without_materialized_trade'
            WHEN failed_trade_candidate_count > 0 THEN 'failed_trade_candidate_only'
            WHEN decoded_trade_candidate_count > 0 THEN 'non_actionable_trade_candidate_only'
            WHEN decoded_event_count > 0 THEN 'decoded_without_trade_candidate'
            ELSE 'catalog_only'
        END AS pair_actionability,
        pair_id,
        decoded_event_count,
        decoded_trade_candidate_count,
        actionable_trade_candidate_count,
        failed_trade_candidate_count,
        trade_event_count,
        pair_candle_count
    FROM pair_state
)
SELECT
    pair_actionability AS pair_actionability,
    COUNT(pair_id) AS pair_count,
    SUM(decoded_event_count) AS decoded_event_count,
    SUM(decoded_trade_candidate_count) AS decoded_trade_candidate_count,
    SUM(actionable_trade_candidate_count) AS actionable_trade_candidate_count,
    SUM(failed_trade_candidate_count) AS failed_trade_candidate_count,
    SUM(trade_event_count) AS trade_event_count,
    SUM(pair_candle_count) AS pair_candle_count
FROM classified
GROUP BY pair_actionability
ORDER BY
    CASE pair_actionability
        WHEN 'trade_materialized' THEN 1
        WHEN 'actionable_without_materialized_trade' THEN 2
        WHEN 'failed_trade_candidate_only' THEN 3
        WHEN 'non_actionable_trade_candidate_only' THEN 4
        WHEN 'decoded_without_trade_candidate' THEN 5
        ELSE 6
    END,
    pair_actionability
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local pair actionability diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalPairActionabilityDiagnosticSummaryDto {
                    pair_actionability: row.pair_actionability,
                    pair_count: row.pair_count,
                    decoded_event_count: row.decoded_event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    actionable_trade_candidate_count: row.actionable_trade_candidate_count,
                    failed_trade_candidate_count: row.failed_trade_candidate_count,
                    trade_event_count: row.trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists local pair trading-readiness summaries.
pub async fn query_local_pair_trading_readiness_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalPairTradingReadinessDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalPairTradingReadinessDiagnosticSummaryRow,
            >(
                r#"
WITH pair_state AS (
    SELECT
        pair.id AS pair_id,
        base_token.mint AS base_mint,
        quote_token.mint AS quote_mint,
        COUNT(DISTINCT dde.id) AS decoded_event_count,
        COUNT(DISTINCT CASE WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1 THEN dde.id END) AS decoded_trade_candidate_count,
        COUNT(DISTINCT te.id) AS trade_event_count,
        COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) AS pair_candle_count
    FROM k_sol_pairs pair
    JOIN k_sol_pools p ON p.id = pair.pool_id
    JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
    JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
    LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
    LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
    LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
    GROUP BY pair.id, base_token.mint, quote_token.mint
), classified AS (
    SELECT
        CASE
            WHEN trade_event_count = 0 THEN 'non_trade_materialized'
            WHEN quote_mint = 'So11111111111111111111111111111111111111112' THEN 'direct_wsol_quote'
            WHEN quote_mint IN (
                'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
                'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
                'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
                'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
            ) THEN 'direct_stable_quote'
            WHEN base_mint = 'So11111111111111111111111111111111111111112' THEN 'inverse_wsol_base'
            WHEN base_mint IN (
                'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
                'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
                'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
                'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
            ) THEN 'inverse_stable_base'
            WHEN quote_mint IS NULL OR quote_mint = '' THEN 'unknown_quote'
            ELSE 'cross_quote_requires_router'
        END AS pair_trading_readiness,
        CASE
            WHEN quote_mint = 'So11111111111111111111111111111111111111112' THEN 'wsol'
            WHEN quote_mint IN (
                'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
                'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
                'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
                'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
            ) THEN 'stable'
            WHEN quote_mint IS NULL OR quote_mint = '' THEN 'unknown'
            ELSE 'other'
        END AS quote_asset_class,
        CASE
            WHEN trade_event_count = 0 THEN 0
            WHEN quote_mint IS NULL OR quote_mint = '' THEN 1
            WHEN quote_mint = 'So11111111111111111111111111111111111111112' THEN 0
            WHEN quote_mint IN (
                'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
                'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
                'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
                'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
            ) THEN 0
            WHEN base_mint = 'So11111111111111111111111111111111111111112' THEN 0
            WHEN base_mint IN (
                'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
                'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
                'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
                'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
            ) THEN 0
            ELSE 1
        END AS trading_route_required,
        pair_id,
        decoded_event_count,
        decoded_trade_candidate_count,
        trade_event_count,
        pair_candle_count
    FROM pair_state
)
SELECT
    pair_trading_readiness AS pair_trading_readiness,
    quote_asset_class AS quote_asset_class,
    trading_route_required AS trading_route_required,
    COUNT(pair_id) AS pair_count,
    SUM(decoded_event_count) AS decoded_event_count,
    SUM(decoded_trade_candidate_count) AS decoded_trade_candidate_count,
    SUM(trade_event_count) AS trade_event_count,
    SUM(pair_candle_count) AS pair_candle_count
FROM classified
GROUP BY pair_trading_readiness, quote_asset_class, trading_route_required
ORDER BY
    CASE pair_trading_readiness
        WHEN 'direct_wsol_quote' THEN 1
        WHEN 'direct_stable_quote' THEN 2
        WHEN 'inverse_wsol_base' THEN 3
        WHEN 'inverse_stable_base' THEN 4
        WHEN 'cross_quote_requires_router' THEN 5
        WHEN 'unknown_quote' THEN 6
        ELSE 7
    END,
    pair_trading_readiness,
    quote_asset_class
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local pair trading readiness diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalPairTradingReadinessDiagnosticSummaryDto {
                    pair_trading_readiness: row.pair_trading_readiness,
                    quote_asset_class: row.quote_asset_class,
                    trading_route_required: row.trading_route_required != 0,
                    pair_count: row.pair_count,
                    decoded_event_count: row.decoded_event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    trade_event_count: row.trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists local decoded-event diagnostic summaries.
pub async fn query_local_decoded_event_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalDecodedEventDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalDecodedEventDiagnosticSummaryRow,
            >(
                r#"
SELECT
    dde.protocol_name AS protocol_name,
    dde.event_kind AS event_kind,
    json_extract(dde.payload_json, '$.eventCategory') AS event_category,
    json_extract(dde.payload_json, '$.eventLifecycleKind') AS event_lifecycle_kind,
    json_extract(dde.payload_json, '$.eventActionability') AS event_actionability,
    json_extract(dde.payload_json, '$.nonTradeUseful') AS non_trade_useful,
    json_extract(dde.payload_json, '$.tradeCandidate') AS trade_candidate,
    json_extract(dde.payload_json, '$.candleCandidate') AS candle_candidate,
    COUNT(dde.id) AS event_count,
    COUNT(te.id) AS trade_event_count
FROM k_sol_dex_decoded_events dde
LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
GROUP BY
    dde.protocol_name,
    dde.event_kind,
    event_category,
    event_lifecycle_kind,
    event_actionability,
    non_trade_useful,
    trade_candidate,
    candle_candidate
ORDER BY
    dde.protocol_name,
    dde.event_kind
                    "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local decoded event diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalDecodedEventDiagnosticSummaryDto {
                    protocol_name: row.protocol_name,
                    event_kind: row.event_kind,
                    event_category: row.event_category,
                    event_lifecycle_kind: row.event_lifecycle_kind,
                    event_actionability: row.event_actionability,
                    non_trade_useful: sqlite_bool_to_option(row.non_trade_useful),
                    trade_candidate: sqlite_bool_to_option(row.trade_candidate),
                    candle_candidate: sqlite_bool_to_option(row.candle_candidate),
                    event_count: row.event_count,
                    trade_event_count: row.trade_event_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists local decoded-event classification diagnostic summaries.
pub async fn query_local_event_classification_diagnostic_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalEventClassificationDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalEventClassificationDiagnosticSummaryRow,
            >(
                r#"
SELECT
    COALESCE(json_extract(dde.payload_json, '$.eventCategory'), 'unknown') AS event_category,
    COALESCE(json_extract(dde.payload_json, '$.eventLifecycleKind'), 'unknown') AS event_lifecycle_kind,
    COALESCE(json_extract(dde.payload_json, '$.eventActionability'), 'unknown') AS event_actionability,
    CASE WHEN COALESCE(json_extract(dde.payload_json, '$.nonTradeUseful'), 0) = 1 THEN 1 ELSE 0 END AS non_trade_useful,
    COUNT(dde.id) AS event_count,
    COUNT(CASE WHEN COALESCE(json_extract(dde.payload_json, '$.tradeCandidate'), 0) = 1 THEN dde.id END) AS decoded_trade_candidate_count,
    COUNT(CASE WHEN COALESCE(json_extract(dde.payload_json, '$.candleCandidate'), 0) = 1 THEN dde.id END) AS decoded_candle_candidate_count,
    COUNT(te.id) AS trade_event_count
FROM k_sol_dex_decoded_events dde
LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
GROUP BY
    event_category,
    event_lifecycle_kind,
    event_actionability,
    non_trade_useful
ORDER BY
    event_category,
    event_lifecycle_kind,
    event_actionability
                    "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list local decoded event classification summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalEventClassificationDiagnosticSummaryDto {
                    event_category: row.event_category,
                    event_lifecycle_kind: row.event_lifecycle_kind,
                    event_actionability: row.event_actionability,
                    non_trade_useful: row.non_trade_useful != 0,
                    event_count: row.event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    decoded_candle_candidate_count: row.decoded_candle_candidate_count,
                    trade_event_count: row.trade_event_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists missing trade events grouped by diagnostic reason.
pub async fn query_local_missing_trade_event_reason_list_summaries(
    database: &crate::Database,
) -> Result<std::vec::Vec<crate::LocalMissingTradeEventReasonSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalMissingTradeEventReasonSummaryRow,
            >(
                r#"
WITH missing AS (
    SELECT
        CASE WHEN ct.err_json IS NOT NULL THEN 1 ELSE 0 END AS transaction_failed,
        CASE
            WHEN ct.id IS NOT NULL
             AND ct.err_json IS NULL
             AND dde.pool_account IS NOT NULL
             AND dde.token_a_mint IS NOT NULL
             AND dde.token_b_mint IS NOT NULL
             AND EXISTS (
                 SELECT 1
                 FROM k_sol_pools p
                 JOIN k_sol_pairs pair ON pair.pool_id = p.id
                 WHERE p.address = dde.pool_account
             )
            THEN 1
            ELSE 0
        END AS actionable,
        CASE
            WHEN ct.err_json IS NOT NULL THEN 'failed_transaction'
            WHEN ct.id IS NULL THEN 'missing_transaction'
            WHEN dde.pool_account IS NULL THEN 'ok_transaction_without_pool_account'
            WHEN dde.token_a_mint IS NULL OR dde.token_b_mint IS NULL THEN 'ok_transaction_without_token_mints'
            WHEN NOT EXISTS (
                 SELECT 1
                 FROM k_sol_pools p
                 JOIN k_sol_pairs pair ON pair.pool_id = p.id
                 WHERE p.address = dde.pool_account
            ) THEN 'ok_transaction_without_known_pair'
            WHEN (
                    json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
                AND json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
                AND json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
                AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
            ) THEN 'ok_transaction_without_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
            ) THEN 'ok_transaction_without_base_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
            ) THEN 'ok_transaction_without_quote_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
                AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
            ) THEN 'ok_transaction_without_price_payload'
            ELSE 'ok_transaction_unclassified'
        END AS reason,
        CASE
            WHEN json_extract(dde.payload_json, '$.baseAmountRaw') IS NOT NULL
              OR json_extract(dde.payload_json, '$.base_amount_raw') IS NOT NULL
            THEN 1
            ELSE 0
        END AS has_base_amount_payload,
        CASE
            WHEN json_extract(dde.payload_json, '$.quoteAmountRaw') IS NOT NULL
              OR json_extract(dde.payload_json, '$.quote_amount_raw') IS NOT NULL
            THEN 1
            ELSE 0
        END AS has_quote_amount_payload,
        CASE
            WHEN json_extract(dde.payload_json, '$.priceQuotePerBase') IS NOT NULL
              OR json_extract(dde.payload_json, '$.price_quote_per_base') IS NOT NULL
            THEN 1
            ELSE 0
        END AS has_price_payload
    FROM k_sol_dex_decoded_events dde
    LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
    LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
    WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
      AND te.id IS NULL
)
SELECT
    reason AS reason,
    transaction_failed AS transaction_failed,
    actionable AS actionable,
    COUNT(*) AS event_count,
    SUM(has_base_amount_payload) AS has_base_amount_payload_count,
    SUM(has_quote_amount_payload) AS has_quote_amount_payload_count,
    SUM(has_price_payload) AS has_price_payload_count
FROM missing
GROUP BY reason, transaction_failed, actionable
ORDER BY actionable DESC, transaction_failed ASC, event_count DESC, reason
                "#,
            )
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list missing trade event reason summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalMissingTradeEventReasonSummaryDto {
                    reason: row.reason,
                    transaction_failed: sqlite_i64_to_bool(row.transaction_failed),
                    actionable: sqlite_i64_to_bool(row.actionable),
                    event_count: row.event_count,
                    has_base_amount_payload_count: row.has_base_amount_payload_count,
                    has_quote_amount_payload_count: row.has_quote_amount_payload_count,
                    has_price_payload_count: row.has_price_payload_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists pair summaries for non-actionable missing trade events.
pub async fn query_local_non_actionable_pair_diagnostic_list_summaries(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalNonActionablePairDiagnosticSummaryDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalNonActionablePairDiagnosticSummaryRow,
            >(
                r#"
WITH missing AS (
    SELECT
        pair.id AS pair_id,
        p.address AS pool_address,
        d.code AS dex_code,
        base_token.mint AS base_mint,
        base_token.symbol AS base_symbol,
        quote_token.mint AS quote_mint,
        quote_token.symbol AS quote_symbol,
        pair.symbol AS pair_symbol,
        dde.id AS decoded_event_id,
        CASE
            WHEN ct.id IS NOT NULL
             AND ct.err_json IS NULL
             AND dde.pool_account IS NOT NULL
             AND dde.token_a_mint IS NOT NULL
             AND dde.token_b_mint IS NOT NULL
             AND EXISTS (
                 SELECT 1
                 FROM k_sol_pools known_pool
                 JOIN k_sol_pairs known_pair ON known_pair.pool_id = known_pool.id
                 WHERE known_pool.address = dde.pool_account
             )
            THEN 1
            ELSE 0
        END AS actionable,
        CASE
            WHEN ct.err_json IS NOT NULL THEN 'failed_transaction'
            WHEN ct.id IS NULL THEN 'missing_transaction'
            WHEN dde.pool_account IS NULL THEN 'ok_transaction_without_pool_account'
            WHEN dde.token_a_mint IS NULL OR dde.token_b_mint IS NULL THEN 'ok_transaction_without_token_mints'
            WHEN NOT EXISTS (
                 SELECT 1
                 FROM k_sol_pools known_pool
                 JOIN k_sol_pairs known_pair ON known_pair.pool_id = known_pool.id
                 WHERE known_pool.address = dde.pool_account
            ) THEN 'ok_transaction_without_known_pair'
            WHEN (
                    json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
                AND json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
                AND json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
                AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
            ) THEN 'ok_transaction_without_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
            ) THEN 'ok_transaction_without_base_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
                AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
            ) THEN 'ok_transaction_without_quote_amount_payload'
            WHEN (
                    json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
                AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
            ) THEN 'ok_transaction_without_price_payload'
            ELSE 'ok_transaction_unclassified'
        END AS reason
    FROM k_sol_pairs pair
    JOIN k_sol_pools p ON p.id = pair.pool_id
    JOIN k_sol_dexes d ON d.id = p.dex_id
    JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
    JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
    JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
    LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
    LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
    WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
      AND te.id IS NULL
)
SELECT
    pair_id AS pair_id,
    pool_address AS pool_address,
    dex_code AS dex_code,
    base_mint AS base_mint,
    base_symbol AS base_symbol,
    quote_mint AS quote_mint,
    quote_symbol AS quote_symbol,
    pair_symbol AS pair_symbol,
    (
        SELECT COUNT(*)
        FROM k_sol_dex_decoded_events dde_all
        WHERE dde_all.pool_account = missing.pool_address
          AND json_extract(dde_all.payload_json, '$.tradeCandidate') = 1
    ) AS decoded_trade_candidate_count,
    COUNT(CASE WHEN actionable = 0 THEN decoded_event_id END) AS non_actionable_missing_trade_event_count,
    SUM(CASE WHEN actionable = 0 AND reason = 'failed_transaction' THEN 1 ELSE 0 END) AS failed_transaction_candidate_count,
    SUM(CASE WHEN actionable = 0 AND reason = 'ok_transaction_without_token_mints' THEN 1 ELSE 0 END) AS ok_transaction_without_token_mints_count,
    SUM(CASE WHEN actionable = 0 AND reason = 'ok_transaction_without_amount_payload' THEN 1 ELSE 0 END) AS ok_transaction_without_amount_payload_count,
    GROUP_CONCAT(DISTINCT CASE WHEN actionable = 0 THEN reason END) AS reason_summary,
    (
        SELECT COUNT(*)
        FROM k_sol_trade_events te_pair
        WHERE te_pair.pair_id = missing.pair_id
    ) AS trade_event_count,
    (
        SELECT COUNT(*)
        FROM k_sol_pair_candles pc_pair
        WHERE pc_pair.pair_id = missing.pair_id
    ) AS pair_candle_count
FROM missing
GROUP BY
    pair_id,
    pool_address,
    dex_code,
    base_mint,
    base_symbol,
    quote_mint,
    quote_symbol,
    pair_symbol
HAVING COUNT(CASE WHEN actionable = 0 THEN decoded_event_id END) > 0
   AND COUNT(CASE WHEN actionable = 1 THEN decoded_event_id END) = 0
ORDER BY non_actionable_missing_trade_event_count DESC, pair_id
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list non-actionable pair diagnostic summaries on sqlite: {}",
                        error
                    )));
                },
            };
            let mut summaries = std::vec::Vec::new();
            for row in rows {
                summaries.push(crate::LocalNonActionablePairDiagnosticSummaryDto {
                    pair_id: row.pair_id,
                    pool_address: row.pool_address,
                    dex_code: row.dex_code,
                    base_mint: row.base_mint,
                    base_symbol: row.base_symbol,
                    quote_mint: row.quote_mint,
                    quote_symbol: row.quote_symbol,
                    pair_symbol: row.pair_symbol,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    non_actionable_missing_trade_event_count: row
                        .non_actionable_missing_trade_event_count,
                    failed_transaction_candidate_count: row.failed_transaction_candidate_count,
                    ok_transaction_without_token_mints_count: row
                        .ok_transaction_without_token_mints_count,
                    ok_transaction_without_amount_payload_count: row
                        .ok_transaction_without_amount_payload_count,
                    reason_summary: row.reason_summary,
                    trade_event_count: row.trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                });
            }
            return Ok(summaries);
        },
    }
}

/// Lists samples of decoded trade candidates without linked trade event.
pub async fn query_local_missing_trade_event_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalMissingTradeEventDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result =
                sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalMissingTradeEventDiagnosticSampleRow>(
                    r#"
SELECT
    dde.id AS decoded_event_id,
    dde.transaction_id AS transaction_id,
    ct.signature AS signature,
    dde.protocol_name AS protocol_name,
    dde.event_kind AS event_kind,
    dde.pool_account AS pool_account,
    CASE WHEN ct.err_json IS NOT NULL THEN 1 ELSE 0 END AS transaction_failed,
    CASE
        WHEN ct.id IS NOT NULL
         AND ct.err_json IS NULL
         AND dde.pool_account IS NOT NULL
         AND dde.token_a_mint IS NOT NULL
         AND dde.token_b_mint IS NOT NULL
         AND EXISTS (
             SELECT 1
             FROM k_sol_pools p
             JOIN k_sol_pairs pair ON pair.pool_id = p.id
             WHERE p.address = dde.pool_account
         )
        THEN 1
        ELSE 0
    END AS actionable,
    CASE
        WHEN ct.err_json IS NOT NULL THEN 'failed_transaction'
        WHEN ct.id IS NULL THEN 'missing_transaction'
        WHEN dde.pool_account IS NULL THEN 'ok_transaction_without_pool_account'
        WHEN dde.token_a_mint IS NULL OR dde.token_b_mint IS NULL THEN 'ok_transaction_without_token_mints'
        WHEN NOT EXISTS (
             SELECT 1
             FROM k_sol_pools p
             JOIN k_sol_pairs pair ON pair.pool_id = p.id
             WHERE p.address = dde.pool_account
        ) THEN 'ok_transaction_without_known_pair'
        WHEN (
                json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
            AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
            AND json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
            AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
            AND json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
            AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
        ) THEN 'ok_transaction_without_amount_payload'
        WHEN (
                json_extract(dde.payload_json, '$.baseAmountRaw') IS NULL
            AND json_extract(dde.payload_json, '$.base_amount_raw') IS NULL
        ) THEN 'ok_transaction_without_base_amount_payload'
        WHEN (
                json_extract(dde.payload_json, '$.quoteAmountRaw') IS NULL
            AND json_extract(dde.payload_json, '$.quote_amount_raw') IS NULL
        ) THEN 'ok_transaction_without_quote_amount_payload'
        WHEN (
                json_extract(dde.payload_json, '$.priceQuotePerBase') IS NULL
            AND json_extract(dde.payload_json, '$.price_quote_per_base') IS NULL
        ) THEN 'ok_transaction_without_price_payload'
        ELSE 'ok_transaction_unclassified'
    END AS reason,
    CASE
        WHEN json_extract(dde.payload_json, '$.baseAmountRaw') IS NOT NULL
          OR json_extract(dde.payload_json, '$.base_amount_raw') IS NOT NULL
        THEN 1
        ELSE 0
    END AS has_base_amount_payload,
    CASE
        WHEN json_extract(dde.payload_json, '$.quoteAmountRaw') IS NOT NULL
          OR json_extract(dde.payload_json, '$.quote_amount_raw') IS NOT NULL
        THEN 1
        ELSE 0
    END AS has_quote_amount_payload,
    CASE
        WHEN json_extract(dde.payload_json, '$.priceQuotePerBase') IS NOT NULL
          OR json_extract(dde.payload_json, '$.price_quote_per_base') IS NOT NULL
        THEN 1
        ELSE 0
    END AS has_price_payload
FROM k_sol_dex_decoded_events dde
LEFT JOIN k_sol_trade_events te ON te.decoded_event_id = dde.id
LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
WHERE json_extract(dde.payload_json, '$.tradeCandidate') = 1
  AND te.id IS NULL
ORDER BY
    actionable DESC,
    transaction_failed ASC,
    dde.protocol_name,
    dde.event_kind,
    dde.id
LIMIT ?
                    "#,
                )
                .bind(limit)
                .fetch_all(pool)
                .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list missing trade event diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalMissingTradeEventDiagnosticSampleDto {
                    decoded_event_id: row.decoded_event_id,
                    transaction_id: row.transaction_id,
                    signature: row.signature,
                    protocol_name: row.protocol_name,
                    event_kind: row.event_kind,
                    pool_account: row.pool_account,
                    transaction_failed: sqlite_i64_to_bool(row.transaction_failed),
                    actionable: sqlite_i64_to_bool(row.actionable),
                    reason: row.reason,
                    has_base_amount_payload: sqlite_i64_to_bool(row.has_base_amount_payload),
                    has_quote_amount_payload: sqlite_i64_to_bool(row.has_quote_amount_payload),
                    has_price_payload: sqlite_i64_to_bool(row.has_price_payload),
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists samples of duplicated trade rows by decoded event id.
pub async fn query_local_duplicate_decoded_event_trade_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalDuplicateDecodedEventTradeDiagnosticSampleDto>, crate::Error>
{
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalDuplicateDecodedEventTradeDiagnosticSampleRow,
            >(
                r#"
SELECT
    te.decoded_event_id AS decoded_event_id,
    dde.protocol_name AS protocol_name,
    dde.event_kind AS event_kind,
    dde.pool_account AS pool_account,
    COUNT(te.id) AS trade_event_count,
    GROUP_CONCAT(te.id) AS trade_event_ids,
    GROUP_CONCAT(te.signature) AS signatures
FROM k_sol_trade_events te
LEFT JOIN k_sol_dex_decoded_events dde ON dde.id = te.decoded_event_id
WHERE te.decoded_event_id IS NOT NULL
GROUP BY
    te.decoded_event_id,
    dde.protocol_name,
    dde.event_kind,
    dde.pool_account
HAVING COUNT(te.id) > 1
ORDER BY trade_event_count DESC, te.decoded_event_id
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list duplicate decoded event trade diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalDuplicateDecodedEventTradeDiagnosticSampleDto {
                    decoded_event_id: row.decoded_event_id,
                    protocol_name: row.protocol_name,
                    event_kind: row.event_kind,
                    pool_account: row.pool_account,
                    trade_event_count: row.trade_event_count,
                    trade_event_ids: row.trade_event_ids,
                    signatures: row.signatures,
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists samples of multi-trade signature/pair groups.
pub async fn query_local_multi_trade_signature_pair_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalMultiTradeSignaturePairDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalMultiTradeSignaturePairDiagnosticSampleRow,
            >(
                r#"
SELECT
    te.signature AS signature,
    te.pair_id AS pair_id,
    p.address AS pool_address,
    d.code AS dex_code,
    COUNT(te.id) AS trade_event_count,
    COUNT(DISTINCT te.decoded_event_id) AS decoded_event_count,
    GROUP_CONCAT(te.id) AS trade_event_ids,
    GROUP_CONCAT(te.decoded_event_id) AS decoded_event_ids
FROM k_sol_trade_events te
LEFT JOIN k_sol_pairs pair ON pair.id = te.pair_id
LEFT JOIN k_sol_pools p ON p.id = pair.pool_id
LEFT JOIN k_sol_dexes d ON d.id = p.dex_id
GROUP BY
    te.signature,
    te.pair_id,
    p.address,
    d.code
HAVING COUNT(te.id) > 1
ORDER BY trade_event_count DESC, te.signature, te.pair_id
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list multi-trade signature/pair diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalMultiTradeSignaturePairDiagnosticSampleDto {
                    signature: row.signature,
                    pair_id: row.pair_id,
                    pool_address: row.pool_address,
                    dex_code: row.dex_code,
                    trade_event_count: row.trade_event_count,
                    decoded_event_count: row.decoded_event_count,
                    trade_event_ids: row.trade_event_ids,
                    decoded_event_ids: row.decoded_event_ids,
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists samples of launch-origin attributions.
pub async fn query_local_launch_origin_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalLaunchOriginDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalLaunchOriginDiagnosticSampleRow,
            >(
                r#"
SELECT
    la.id AS launch_attribution_id,
    ls.code AS launch_surface_code,
    ls.name AS launch_surface_name,
    ct.signature AS transaction_signature,
    la.decoded_event_id AS decoded_event_id,
    la.protocol_name AS protocol_name,
    la.match_kind AS match_kind,
    la.matched_value AS matched_value,
    la.pool_id AS pool_id,
    p.address AS pool_address,
    la.pair_id AS pair_id,
    pair.symbol AS pair_symbol
FROM k_sol_launch_attributions la
JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
JOIN k_sol_chain_transactions ct ON ct.id = la.transaction_id
LEFT JOIN k_sol_pools p ON p.id = la.pool_id
LEFT JOIN k_sol_pairs pair ON pair.id = la.pair_id
ORDER BY la.id DESC
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list launch-origin diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalLaunchOriginDiagnosticSampleDto {
                    launch_attribution_id: row.launch_attribution_id,
                    launch_surface_code: row.launch_surface_code,
                    launch_surface_name: row.launch_surface_name,
                    transaction_signature: row.transaction_signature,
                    decoded_event_id: row.decoded_event_id,
                    protocol_name: row.protocol_name,
                    match_kind: row.match_kind,
                    matched_value: row.matched_value,
                    pool_id: row.pool_id,
                    pool_address: row.pool_address,
                    pair_id: row.pair_id,
                    pair_symbol: row.pair_symbol,
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists samples of pool-origin rows and their optional launch linkage.
pub async fn query_local_pool_origin_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalPoolOriginDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalPoolOriginDiagnosticSampleRow,
            >(
                r#"
SELECT
    po.id AS pool_origin_id,
    d.code AS dex_code,
    po.pool_id AS pool_id,
    p.address AS pool_address,
    po.pair_id AS pair_id,
    pair.symbol AS pair_symbol,
    ls.code AS launch_surface_code,
    po.founding_signature AS founding_signature,
    po.founding_protocol_name AS founding_protocol_name,
    po.founding_event_kind AS founding_event_kind
FROM k_sol_pool_origins po
JOIN k_sol_dexes d ON d.id = po.dex_id
JOIN k_sol_pools p ON p.id = po.pool_id
LEFT JOIN k_sol_pairs pair ON pair.id = po.pair_id
LEFT JOIN k_sol_launch_attributions la ON la.id = po.launch_attribution_id
LEFT JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
ORDER BY po.id DESC
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list pool-origin diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalPoolOriginDiagnosticSampleDto {
                    pool_origin_id: row.pool_origin_id,
                    dex_code: row.dex_code,
                    pool_id: row.pool_id,
                    pool_address: row.pool_address,
                    pair_id: row.pair_id,
                    pair_symbol: row.pair_symbol,
                    launch_surface_code: row.launch_surface_code,
                    founding_signature: row.founding_signature,
                    founding_protocol_name: row.founding_protocol_name,
                    founding_event_kind: row.founding_event_kind,
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists prioritized samples of tokens whose metadata is still incomplete.
pub async fn query_local_token_metadata_gap_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalTokenMetadataGapDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let rows_result = sqlx::query_as::<
                sqlx::Sqlite,
                crate::db::dtos::LocalTokenMetadataGapDiagnosticSampleRow,
            >(
                r#"
WITH token_pair_usage AS (
    SELECT
        token.id AS token_id,
        COUNT(DISTINCT pair.id) AS total_pair_count,
        COUNT(DISTINCT CASE WHEN te.id IS NOT NULL THEN pair.id END) AS trade_materialized_pair_count,
        COUNT(DISTINCT CASE WHEN pair.quote_token_id = token.id THEN pair.id END) AS quote_pair_count
    FROM k_sol_tokens token
    LEFT JOIN k_sol_pairs pair
        ON pair.base_token_id = token.id
        OR pair.quote_token_id = token.id
    LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
    GROUP BY token.id
)
SELECT
    token.id AS token_id,
    token.mint AS mint,
    token.symbol AS symbol,
    token.name AS name,
    token.decimals AS decimals,
    token.token_program AS token_program,
    token.is_quote_token AS is_quote_token,
    CASE WHEN usage.trade_materialized_pair_count > 0 THEN 1 ELSE 0 END AS used_by_trade_materialized_pair,
    CASE WHEN usage.quote_pair_count > 0 THEN 1 ELSE 0 END AS used_as_quote_token,
    COALESCE(usage.trade_materialized_pair_count, 0) AS trade_materialized_pair_count,
    COALESCE(usage.total_pair_count, 0) AS total_pair_count,
    CASE
        WHEN usage.trade_materialized_pair_count > 0 AND usage.quote_pair_count > 0 THEN 'tradable_quote_missing_metadata'
        WHEN usage.trade_materialized_pair_count > 0 THEN 'tradable_token_missing_metadata'
        WHEN usage.quote_pair_count > 0 THEN 'quote_token_missing_metadata'
        ELSE 'catalog_token_missing_metadata'
    END AS priority
FROM k_sol_tokens token
LEFT JOIN token_pair_usage usage ON usage.token_id = token.id
WHERE token.symbol IS NULL
   OR TRIM(token.symbol) = ''
   OR token.name IS NULL
   OR TRIM(token.name) = ''
ORDER BY
    CASE
        WHEN usage.trade_materialized_pair_count > 0 AND usage.quote_pair_count > 0 THEN 1
        WHEN usage.trade_materialized_pair_count > 0 THEN 2
        WHEN usage.quote_pair_count > 0 THEN 3
        ELSE 4
    END,
    usage.trade_materialized_pair_count DESC,
    usage.total_pair_count DESC,
    token.mint
LIMIT ?
                "#,
            )
            .bind(limit)
            .fetch_all(pool)
            .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list token metadata gap diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalTokenMetadataGapDiagnosticSampleDto {
                    token_id: row.token_id,
                    mint: row.mint,
                    symbol: row.symbol,
                    name: row.name,
                    decimals: row.decimals,
                    token_program: row.token_program,
                    is_quote_token: sqlite_i64_to_bool(row.is_quote_token),
                    used_by_trade_materialized_pair: sqlite_i64_to_bool(
                        row.used_by_trade_materialized_pair,
                    ),
                    used_as_quote_token: sqlite_i64_to_bool(row.used_as_quote_token),
                    trade_materialized_pair_count: row.trade_materialized_pair_count,
                    total_pair_count: row.total_pair_count,
                    priority: row.priority,
                });
            }
            return Ok(samples);
        },
    }
}

/// Lists samples of pairs without trade events.
pub async fn query_local_pair_without_trade_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalPairGapDiagnosticSampleDto>, crate::Error> {
    return list_local_pair_gap_diagnostic_samples(database, limit, true).await;
}

/// Lists samples of pairs without candles.
pub async fn query_local_pair_without_candle_diagnostic_list_samples(
    database: &crate::Database,
    limit: i64,
) -> Result<std::vec::Vec<crate::LocalPairGapDiagnosticSampleDto>, crate::Error> {
    return list_local_pair_gap_diagnostic_samples(database, limit, false).await;
}

async fn list_local_pair_gap_diagnostic_samples(
    database: &crate::Database,
    limit: i64,
    without_trade: bool,
) -> Result<std::vec::Vec<crate::LocalPairGapDiagnosticSampleDto>, crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let having_clause = if without_trade {
                r#"
HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT te.id) = 0
                "#
            } else {
                r#"
HAVING COUNT(DISTINCT CASE
                WHEN json_extract(dde.payload_json, '$.candleCandidate') = 1
                 AND ct.id IS NOT NULL
                 AND ct.err_json IS NULL
                THEN dde.id END) > 0
               AND COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) = 0
                "#
            };
            let mut builder = sqlx::QueryBuilder::<sqlx::Sqlite>::new(format!(
                r#"
SELECT
    pair.id AS pair_id,
    p.address AS pool_address,
    d.code AS dex_code,
    base_token.mint AS base_mint,
    base_token.symbol AS base_symbol,
    quote_token.mint AS quote_mint,
    quote_token.symbol AS quote_symbol,
    COALESCE(
        pair.symbol,
        CASE
            WHEN base_token.symbol IS NOT NULL
             AND base_token.symbol != ''
             AND quote_token.symbol IS NOT NULL
             AND quote_token.symbol != ''
            THEN base_token.symbol || '/' || quote_token.symbol
            ELSE NULL
        END
    ) AS pair_symbol,
    COUNT(DISTINCT dde.id) AS decoded_event_count,
    COUNT(DISTINCT CASE WHEN json_extract(dde.payload_json, '$.tradeCandidate') = 1 THEN dde.id END) AS decoded_trade_candidate_count,
    COUNT(DISTINCT te.id) AS trade_event_count,
    COUNT(DISTINCT pc.bucket_start_unix || ':' || pc.timeframe_seconds) AS pair_candle_count
FROM k_sol_pairs pair
JOIN k_sol_pools p ON p.id = pair.pool_id
JOIN k_sol_dexes d ON d.id = p.dex_id
JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
LEFT JOIN k_sol_dex_decoded_events dde ON dde.pool_account = p.address
LEFT JOIN k_sol_chain_transactions ct ON ct.id = dde.transaction_id
LEFT JOIN k_sol_trade_events te ON te.pair_id = pair.id
LEFT JOIN k_sol_pair_candles pc ON pc.pair_id = pair.id
GROUP BY
    pair.id,
    p.address,
    d.code,
    base_token.mint,
    base_token.symbol,
    quote_token.mint,
    quote_token.symbol,
    pair.symbol
{}
ORDER BY decoded_trade_candidate_count DESC, pair.id
LIMIT "#,
                having_clause
            ));
            builder.push_bind(limit);
            let rows_result = builder
                .build_query_as::<crate::db::dtos::LocalPairGapDiagnosticSampleRow>()
                .fetch_all(pool)
                .await;
            let rows = match rows_result {
                Ok(rows) => rows,
                Err(error) => {
                    return Err(crate::Error::Db(format!(
                        "cannot list pair gap diagnostic samples on sqlite: {}",
                        error
                    )));
                },
            };
            let mut samples = std::vec::Vec::new();
            for row in rows {
                samples.push(crate::LocalPairGapDiagnosticSampleDto {
                    pair_id: row.pair_id,
                    pool_address: row.pool_address,
                    dex_code: row.dex_code,
                    base_mint: row.base_mint,
                    base_symbol: row.base_symbol,
                    quote_mint: row.quote_mint,
                    quote_symbol: row.quote_symbol,
                    pair_symbol: row.pair_symbol,
                    decoded_event_count: row.decoded_event_count,
                    decoded_trade_candidate_count: row.decoded_trade_candidate_count,
                    trade_event_count: row.trade_event_count,
                    pair_candle_count: row.pair_candle_count,
                });
            }
            return Ok(samples);
        },
    }
}

fn sqlite_bool_to_option(value: std::option::Option<i64>) -> std::option::Option<bool> {
    match value {
        Some(0) => return Some(false),
        Some(_) => return Some(true),
        None => return None,
    }
}

fn sqlite_i64_to_bool(value: i64) -> bool {
    return value != 0;
}
