// file: kb_lib/src/db/schema.rs

//! Database schema initialization.

/// Ensures that the database schema exists.
pub(crate) async fn ensure_schema(database: &crate::Database) -> Result<(), crate::Error> {
    match database.connection() {
        crate::DatabaseConnection::Sqlite(pool) => {
            let result = create_tbl_db_metadata(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_known_http_endpoints(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_known_ws_endpoints(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_db_runtime_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_db_runtime_events_created_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_observed_tokens(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_observed_tokens_mint(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_observed_tokens_status(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_onchain_observations(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_onchain_observations_object_key(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_onchain_observations_observed_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_analysis_signals(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_analysis_signals_object_key(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_analysis_signals_created_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_dexes(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_tokens(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_tokens_token_program(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_tokens_is_quote_token(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pools(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pools_dex_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pairs(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pairs_dex_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pairs_base_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pairs_quote_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pool_tokens(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_tokens_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_tokens_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pool_listings(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_listings_detected_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_listings_dex_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_swaps(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_swaps_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_swaps_executed_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_swaps_pair_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_swaps_slot(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_liquidity_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_liquidity_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_liquidity_events_executed_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_liquidity_events_pair_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_liquidity_events_slot(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_token_mint_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_mint_events_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_mint_events_executed_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_token_burn_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_burn_events_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_burn_events_executed_at(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_chain_slots(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_chain_transactions(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_chain_transactions_slot(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_chain_instructions(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_chain_instructions_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_chain_instructions_program_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = update_schema_version_metadata(database).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_dex_decoded_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_decoded_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_dex_decoded_events_transaction_instruction_event(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_instruction_observations(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_instruction_observations_key(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_instruction_observations_decoder_discriminator(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_instruction_observations_signature(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_instruction_observations_instruction_name(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_dex_decode_replay_ledger(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_dex_decode_replay_ledger_transaction_scope_version(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_decode_replay_ledger_signature(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_decode_replay_ledger_status(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_dex_event_coverage_entries(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_dex_event_coverage_entries_key(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_event_coverage_entries_decoder(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_event_coverage_entries_proof_status(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_dex_event_coverage_entries_event_family(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_transaction_classifications(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_transaction_classifications_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_transaction_classifications_kind(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_protocol_candidates(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_protocol_candidates_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_protocol_candidates_program_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pool_lifecycle_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_lifecycle_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_lifecycle_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_pool_lifecycle_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_fee_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_fee_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_fee_event_amounts(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_event_amounts_fee_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_event_amounts_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_event_amounts_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_fee_event_amounts_token_mint(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_fee_event_amounts_fee_event_leg(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_reward_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_reward_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_reward_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_reward_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pool_admin_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_admin_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_admin_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_pool_admin_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_orderbook_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_orderbook_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_orderbook_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_orderbook_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_token_account_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_account_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_token_account_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_token_account_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_launch_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_events_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_events_event_kind(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_uix_launch_events_decoded_event_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_launch_surfaces(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_launch_surface_keys(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_surface_keys_surface_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_launch_attributions(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_attributions_surface_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_launch_attributions_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pool_origins(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_origins_dex_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_origins_pair_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_origins_listing_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pool_origins_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_wallets(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_wallet_participations(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_wallet_participations_wallet_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_wallet_participations_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_wallet_participations_transaction_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_trade_events(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_trade_events_pair_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_trade_events_pool_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pair_metrics(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_wallet_holdings(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_wallet_holdings_wallet_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_wallet_holdings_token_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pair_candles(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pair_candles_pair_timeframe(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pair_candles_bucket(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_tbl_pair_analytic_signals(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            let result = create_idx_pair_analytic_signals_pair_id(pool).await;
            if let Err(error) = result {
                return Err(error);
            }
            return Ok(());
        },
    }
}

/// Executes one SQLite schema statement.
async fn execute_sqlite_schema_statement(
    pool: &sqlx::SqlitePool,
    statement_name: &str,
    statement_sql: &'static str,
) -> Result<(), crate::Error> {
    let execute_result = sqlx::query(statement_sql).execute(pool).await;
    match execute_result {
        Ok(_) => return Ok(()),
        Err(error) => {
            return Err(crate::Error::Db(format!(
                "cannot initialize sqlite schema statement '{}': {}",
                statement_name, error
            )));
        },
    }
}

/// Updates the persisted schema version metadata entry.
async fn update_schema_version_metadata(database: &crate::Database) -> Result<(), crate::Error> {
    let schema_version = crate::DbMetadataDto::new(
        "schema_version".to_string(),
        env!("CARGO_PKG_VERSION").to_string(),
    );
    let upsert_result = crate::query_db_metadatas_upsert(database, &schema_version).await;
    match upsert_result {
        Ok(_) => return Ok(()),
        Err(error) => return Err(error),
    }
}

/// Creates `k_sol_db_metadata`.
async fn create_tbl_db_metadata(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_db_metadata",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_db_metadata (
    key TEXT NOT NULL PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates `k_sol_known_http_endpoints`.
async fn create_tbl_known_http_endpoints(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_known_http_endpoints",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_known_http_endpoints (
    name TEXT NOT NULL PRIMARY KEY,
    provider TEXT NOT NULL,
    url TEXT NOT NULL,
    enabled INTEGER NOT NULL,
    roles_json TEXT NOT NULL,
    last_seen_at TEXT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates `k_sol_known_ws_endpoints`.
async fn create_tbl_known_ws_endpoints(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_known_ws_endpoints",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_known_ws_endpoints (
    name TEXT NOT NULL PRIMARY KEY,
    provider TEXT NOT NULL,
    url TEXT NOT NULL,
    enabled INTEGER NOT NULL,
    roles_json TEXT NOT NULL,
    last_seen_at TEXT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates `k_sol_db_runtime_events`.
async fn create_tbl_db_runtime_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_db_runtime_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_db_runtime_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    event_kind TEXT NOT NULL,
    level INTEGER NOT NULL,
    source TEXT NOT NULL,
    message TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_db_runtime_events(created_at)`.
async fn create_idx_db_runtime_events_created_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_db_runtime_events_created_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_db_runtime_events_created_at
ON k_sol_db_runtime_events (created_at)
        "#,
    )
    .await;
}

/// Creates `k_sol_observed_tokens`.
async fn create_tbl_observed_tokens(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_observed_tokens",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_observed_tokens (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    mint TEXT NOT NULL UNIQUE,
    symbol TEXT NULL,
    name TEXT NULL,
    decimals INTEGER NULL,
    token_program TEXT NOT NULL,
    status INTEGER NOT NULL,
    first_seen_at TEXT NOT NULL,
    last_seen_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates unique index on `k_sol_observed_tokens(mint)`.
async fn create_idx_observed_tokens_mint(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_observed_tokens_mint",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS idx_observed_tokens_mint
ON k_sol_observed_tokens (mint)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_observed_tokens(status)`.
async fn create_idx_observed_tokens_status(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_observed_tokens_status",
        r#"
CREATE INDEX IF NOT EXISTS idx_observed_tokens_status
ON k_sol_observed_tokens (status)
        "#,
    )
    .await;
}

/// Creates `k_sol_onchain_observations`.
async fn create_tbl_onchain_observations(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_onchain_observations",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_onchain_observations (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    observation_kind TEXT NOT NULL,
    source_kind INTEGER NOT NULL,
    endpoint_name TEXT NULL,
    object_key TEXT NOT NULL,
    slot INTEGER NULL,
    payload_json TEXT NOT NULL,
    observed_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_onchain_observations(object_key)`.
async fn create_idx_onchain_observations_object_key(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_onchain_observations_object_key",
        r#"
CREATE INDEX IF NOT EXISTS idx_onchain_observations_object_key
ON k_sol_onchain_observations (object_key)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_onchain_observations(observed_at)`.
async fn create_idx_onchain_observations_observed_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_onchain_observations_observed_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_onchain_observations_observed_at
ON k_sol_onchain_observations (observed_at)
        "#,
    )
    .await;
}

/// Creates `k_sol_analysis_signals`.
async fn create_tbl_analysis_signals(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_analysis_signals",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_analysis_signals (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    signal_kind TEXT NOT NULL,
    severity INTEGER NOT NULL,
    object_key TEXT NOT NULL,
    related_observation_id INTEGER NULL,
    score REAL NULL,
    payload_json TEXT NOT NULL,
    created_at TEXT NOT NULL,
    FOREIGN KEY(related_observation_id) REFERENCES k_sol_onchain_observations(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_analysis_signals(object_key)`.
async fn create_idx_analysis_signals_object_key(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_analysis_signals_object_key",
        r#"
CREATE INDEX IF NOT EXISTS idx_analysis_signals_object_key
ON k_sol_analysis_signals (object_key)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_analysis_signals(created_at)`.
async fn create_idx_analysis_signals_created_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_analysis_signals_created_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_analysis_signals_created_at
ON k_sol_analysis_signals (created_at)
        "#,
    )
    .await;
}

/// Creates `k_sol_dexes`.
async fn create_tbl_dexes(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_dexes",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_dexes (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    code TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    program_id TEXT NULL,
    router_program_id TEXT NULL,
    is_enabled INTEGER NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates `k_sol_tokens`.
async fn create_tbl_tokens(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_tokens",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_tokens (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    mint TEXT NOT NULL UNIQUE,
    symbol TEXT NULL,
    name TEXT NULL,
    decimals INTEGER NULL,
    token_program TEXT NOT NULL,
    is_quote_token INTEGER NOT NULL,
    first_seen_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_tokens(token_program)`.
async fn create_idx_tokens_token_program(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_tokens_token_program",
        r#"
CREATE INDEX IF NOT EXISTS idx_tokens_token_program
ON k_sol_tokens (token_program)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_tokens(is_quote_token)`.
async fn create_idx_tokens_is_quote_token(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_tokens_is_quote_token",
        r#"
CREATE INDEX IF NOT EXISTS idx_tokens_is_quote_token
ON k_sol_tokens (is_quote_token)
        "#,
    )
    .await;
}

/// Creates `k_sol_pools`.
async fn create_tbl_pools(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pools",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pools (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    address TEXT NOT NULL UNIQUE,
    pool_kind INTEGER NOT NULL,
    status INTEGER NOT NULL,
    first_seen_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pools(dex_id)`.
async fn create_idx_pools_dex_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pools_dex_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pools_dex_id
ON k_sol_pools (dex_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_pairs`.
async fn create_tbl_pairs(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pairs",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pairs (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL UNIQUE,
    base_token_id INTEGER NOT NULL,
    quote_token_id INTEGER NOT NULL,
    symbol TEXT NULL,
    first_seen_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id),
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(base_token_id) REFERENCES k_sol_tokens(id),
    FOREIGN KEY(quote_token_id) REFERENCES k_sol_tokens(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pairs(dex_id)`.
async fn create_idx_pairs_dex_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pairs_dex_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pairs_dex_id
ON k_sol_pairs (dex_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pairs(base_token_id)`.
async fn create_idx_pairs_base_token_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pairs_base_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pairs_base_token_id
ON k_sol_pairs (base_token_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pairs(quote_token_id)`.
async fn create_idx_pairs_quote_token_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pairs_quote_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pairs_quote_token_id
ON k_sol_pairs (quote_token_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_pool_tokens`.
async fn create_tbl_pool_tokens(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pool_tokens",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pool_tokens (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    pool_id INTEGER NOT NULL,
    token_id INTEGER NOT NULL,
    role INTEGER NOT NULL,
    vault_address TEXT NULL,
    token_order INTEGER NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(token_id) REFERENCES k_sol_tokens(id),
    UNIQUE(pool_id, token_id, role)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_tokens(pool_id)`.
async fn create_idx_pool_tokens_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_tokens_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_tokens_pool_id
ON k_sol_pool_tokens (pool_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_tokens(token_id)`.
async fn create_idx_pool_tokens_token_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_tokens_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_tokens_token_id
ON k_sol_pool_tokens (token_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_pool_listings`.
async fn create_tbl_pool_listings(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pool_listings",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pool_listings (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL UNIQUE,
    pair_id INTEGER NULL,
    source_kind INTEGER NOT NULL,
    source_endpoint_name TEXT NULL,
    detected_at TEXT NOT NULL,
    initial_base_reserve REAL NULL,
    initial_quote_reserve REAL NULL,
    initial_price_quote REAL NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id),
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_listings(detected_at)`.
async fn create_idx_pool_listings_detected_at(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_listings_detected_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_listings_detected_at
ON k_sol_pool_listings (detected_at)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_listings(dex_id)`.
async fn create_idx_pool_listings_dex_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_listings_dex_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_listings_dex_id
ON k_sol_pool_listings (dex_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_swaps`.
async fn create_tbl_swaps(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_swaps",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_swaps (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    instruction_index INTEGER NOT NULL,
    slot INTEGER NULL,
    trader_wallet TEXT NULL,
    base_token_id INTEGER NOT NULL,
    quote_token_id INTEGER NOT NULL,
    base_amount TEXT NOT NULL,
    quote_amount TEXT NOT NULL,
    price_quote TEXT NULL,
    trade_side INTEGER NOT NULL,
    executed_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id),
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id),
    FOREIGN KEY(base_token_id) REFERENCES k_sol_tokens(id),
    FOREIGN KEY(quote_token_id) REFERENCES k_sol_tokens(id),
    UNIQUE(signature, instruction_index)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_swaps(pool_id)`.
async fn create_idx_swaps_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_swaps_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_swaps_pool_id
ON k_sol_swaps (pool_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_swaps(executed_at)`.
async fn create_idx_swaps_executed_at(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_swaps_executed_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_swaps_executed_at
ON k_sol_swaps (executed_at)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_swaps(pair_id)`.
async fn create_idx_swaps_pair_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_swaps_pair_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_swaps_pair_id
ON k_sol_swaps (pair_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_swaps(slot)`.
async fn create_idx_swaps_slot(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_swaps_slot",
        r#"
CREATE INDEX IF NOT EXISTS idx_swaps_slot
ON k_sol_swaps (slot)
        "#,
    )
    .await;
}

/// Creates `k_sol_liquidity_events`.
async fn create_tbl_liquidity_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_liquidity_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_liquidity_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    instruction_index INTEGER NOT NULL,
    slot INTEGER NULL,
    program_id TEXT NULL,
    event_kind INTEGER NOT NULL,
    event_kind_text TEXT NULL,
    actor_wallet TEXT NULL,
    base_token_id INTEGER NOT NULL,
    quote_token_id INTEGER NOT NULL,
    lp_token_id INTEGER NULL,
    base_amount TEXT NOT NULL,
    quote_amount TEXT NOT NULL,
    lp_amount TEXT NULL,
    amounts_are_complete INTEGER NOT NULL DEFAULT 1,
    payload_json TEXT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id),
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id),
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id),
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id),
    FOREIGN KEY(base_token_id) REFERENCES k_sol_tokens(id),
    FOREIGN KEY(quote_token_id) REFERENCES k_sol_tokens(id),
    FOREIGN KEY(lp_token_id) REFERENCES k_sol_tokens(id),
    UNIQUE(signature, instruction_index)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_liquidity_events(pool_id)`.
async fn create_idx_liquidity_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_liquidity_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_liquidity_events_pool_id
ON k_sol_liquidity_events (pool_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_liquidity_events(executed_at)`.
async fn create_idx_liquidity_events_executed_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_liquidity_events_executed_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_liquidity_events_executed_at
ON k_sol_liquidity_events (executed_at)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_liquidity_events(pair_id)`.
async fn create_idx_liquidity_events_pair_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_liquidity_events_pair_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_liquidity_events_pair_id
ON k_sol_liquidity_events (pair_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_liquidity_events(slot)`.
async fn create_idx_liquidity_events_slot(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_liquidity_events_slot",
        r#"
CREATE INDEX IF NOT EXISTS idx_liquidity_events_slot
ON k_sol_liquidity_events (slot)
        "#,
    )
    .await;
}

/// Creates `k_sol_token_mint_events`.
async fn create_tbl_token_mint_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_token_mint_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_token_mint_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    token_id INTEGER NOT NULL,
    signature TEXT NOT NULL,
    instruction_index INTEGER NOT NULL,
    slot INTEGER NULL,
    authority_wallet TEXT NULL,
    destination_wallet TEXT NULL,
    amount TEXT NOT NULL,
    supply_after TEXT NULL,
    executed_at TEXT NOT NULL,
    FOREIGN KEY(token_id) REFERENCES k_sol_tokens(id),
    UNIQUE(signature, instruction_index)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_mint_events(token_id)`.
async fn create_idx_token_mint_events_token_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_mint_events_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_mint_events_token_id
ON k_sol_token_mint_events (token_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_mint_events(executed_at)`.
async fn create_idx_token_mint_events_executed_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_mint_events_executed_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_mint_events_executed_at
ON k_sol_token_mint_events (executed_at)
        "#,
    )
    .await;
}

/// Creates `k_sol_token_burn_events`.
async fn create_tbl_token_burn_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_token_burn_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_token_burn_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    token_id INTEGER NOT NULL,
    signature TEXT NOT NULL,
    instruction_index INTEGER NOT NULL,
    slot INTEGER NULL,
    authority_wallet TEXT NULL,
    source_wallet TEXT NULL,
    amount TEXT NOT NULL,
    supply_after TEXT NULL,
    executed_at TEXT NOT NULL,
    FOREIGN KEY(token_id) REFERENCES k_sol_tokens(id),
    UNIQUE(signature, instruction_index)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_burn_events(token_id)`.
async fn create_idx_token_burn_events_token_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_burn_events_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_burn_events_token_id
ON k_sol_token_burn_events (token_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_burn_events(executed_at)`.
async fn create_idx_token_burn_events_executed_at(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_burn_events_executed_at",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_burn_events_executed_at
ON k_sol_token_burn_events (executed_at)
        "#,
    )
    .await;
}

/// Creates `k_sol_chain_slots`.
async fn create_tbl_chain_slots(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_chain_slots",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_chain_slots (
    slot INTEGER NOT NULL PRIMARY KEY,
    parent_slot INTEGER NULL,
    block_time_unix INTEGER NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates `k_sol_chain_transactions`.
async fn create_tbl_chain_transactions(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_chain_transactions",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_chain_transactions (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    signature TEXT NOT NULL UNIQUE,
    slot INTEGER NULL,
    block_time_unix INTEGER NULL,
    source_endpoint_name TEXT NULL,
    version_text TEXT NULL,
    err_json TEXT NULL,
    meta_json TEXT NULL,
    transaction_json TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(slot) REFERENCES k_sol_chain_slots(slot)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_chain_transactions(slot)`.
async fn create_idx_chain_transactions_slot(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_chain_transactions_slot",
        r#"
CREATE INDEX IF NOT EXISTS idx_chain_transactions_slot
ON k_sol_chain_transactions (slot)
        "#,
    )
    .await;
}

/// Creates `k_sol_chain_instructions`.
async fn create_tbl_chain_instructions(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_chain_instructions",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_chain_instructions (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    parent_instruction_id INTEGER NULL,
    instruction_index INTEGER NOT NULL,
    inner_instruction_index INTEGER NULL,
    program_id TEXT NULL,
    program_name TEXT NULL,
    stack_height INTEGER NULL,
    accounts_json TEXT NOT NULL,
    data_json TEXT NULL,
    parsed_type TEXT NULL,
    parsed_json TEXT NULL,
    created_at TEXT NOT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id),
    FOREIGN KEY(parent_instruction_id) REFERENCES k_sol_chain_instructions(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_chain_instructions(transaction_id)`.
async fn create_idx_chain_instructions_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_chain_instructions_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_chain_instructions_transaction_id
ON k_sol_chain_instructions (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_chain_instructions(program_id)`.
async fn create_idx_chain_instructions_program_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_chain_instructions_program_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_chain_instructions_program_id
ON k_sol_chain_instructions (program_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_instruction_observations`.
async fn create_tbl_instruction_observations(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_instruction_observations",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_instruction_observations (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    observation_key TEXT NOT NULL,
    transaction_id INTEGER NOT NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    block_time INTEGER NULL,
    failed INTEGER NOT NULL,
    instruction_id INTEGER NOT NULL,
    parent_instruction_id INTEGER NULL,
    instruction_index INTEGER NOT NULL,
    inner_instruction_index INTEGER NULL,
    program_id TEXT NOT NULL,
    decoder_code TEXT NULL,
    discriminator_hex TEXT NULL,
    instruction_name TEXT NULL,
    accounts_json TEXT NOT NULL,
    data_json TEXT NULL,
    pool_account TEXT NULL,
    decoded_event_kind TEXT NULL,
    decoded_event_id INTEGER NULL,
    observed_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id),
    FOREIGN KEY(instruction_id) REFERENCES k_sol_chain_instructions(id),
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

/// Creates unique index on instruction observation key.
async fn create_uix_instruction_observations_key(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_instruction_observations_key",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_instruction_observations_key
ON k_sol_instruction_observations (observation_key)
        "#,
    )
    .await;
}

/// Creates lookup index on decoder/discriminator.
async fn create_idx_instruction_observations_decoder_discriminator(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_instruction_observations_decoder_discriminator",
        r#"
CREATE INDEX IF NOT EXISTS idx_instruction_observations_decoder_discriminator
ON k_sol_instruction_observations (decoder_code, discriminator_hex)
        "#,
    )
    .await;
}

/// Creates lookup index on signature.
async fn create_idx_instruction_observations_signature(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_instruction_observations_signature",
        r#"
CREATE INDEX IF NOT EXISTS idx_instruction_observations_signature
ON k_sol_instruction_observations (signature)
        "#,
    )
    .await;
}

/// Creates lookup index on instruction name.
async fn create_idx_instruction_observations_instruction_name(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_instruction_observations_instruction_name",
        r#"
CREATE INDEX IF NOT EXISTS idx_instruction_observations_instruction_name
ON k_sol_instruction_observations (instruction_name)
        "#,
    )
    .await;
}

/// Creates `k_sol_dex_decoded_events`.
async fn create_tbl_dex_decoded_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_dex_decoded_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_dex_decoded_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    instruction_id INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    lp_mint TEXT NULL,
    token_a_mint TEXT NULL,
    token_b_mint TEXT NULL,
    market_account TEXT NULL,
    payload_json TEXT NOT NULL,
    created_at TEXT NOT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id),
    FOREIGN KEY(instruction_id) REFERENCES k_sol_chain_instructions(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_dex_decoded_events(transaction_id)`.
async fn create_idx_dex_decoded_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_decoded_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_decoded_events_transaction_id
ON k_sol_dex_decoded_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates unique index on `(transaction_id, instruction_id, event_kind)`.
async fn create_uix_dex_decoded_events_transaction_instruction_event(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_dex_decoded_events_transaction_instruction_event",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_dex_decoded_events_transaction_instruction_event
ON k_sol_dex_decoded_events (transaction_id, instruction_id, event_kind)
        "#,
    )
    .await;
}

/// Creates `k_sol_dex_decode_replay_ledger`.
async fn create_tbl_dex_decode_replay_ledger(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_dex_decode_replay_ledger",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_dex_decode_replay_ledger (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    signature TEXT NOT NULL,
    decoder_scope TEXT NOT NULL,
    decoder_version TEXT NOT NULL,
    decode_status TEXT NOT NULL,
    certainty TEXT NOT NULL,
    event_count INTEGER NOT NULL,
    distinct_token_mint_count INTEGER NOT NULL,
    force_replay_required INTEGER NOT NULL,
    status_reason TEXT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id)
)
        "#,
    )
    .await;
}

/// Creates unique index on `(transaction_id, decoder_scope, decoder_version)`.
async fn create_uix_dex_decode_replay_ledger_transaction_scope_version(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_dex_decode_replay_ledger_transaction_scope_version",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_dex_decode_replay_ledger_transaction_scope_version
ON k_sol_dex_decode_replay_ledger (transaction_id, decoder_scope, decoder_version)
        "#,
    )
    .await;
}

/// Creates index on `(signature, decoder_scope, decoder_version)`.
async fn create_idx_dex_decode_replay_ledger_signature(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_decode_replay_ledger_signature",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_decode_replay_ledger_signature
ON k_sol_dex_decode_replay_ledger (signature, decoder_scope, decoder_version)
        "#,
    )
    .await;
}

/// Creates index on `(decode_status, certainty, force_replay_required)`.
async fn create_idx_dex_decode_replay_ledger_status(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_decode_replay_ledger_status",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_decode_replay_ledger_status
ON k_sol_dex_decode_replay_ledger (decode_status, certainty, force_replay_required)
        "#,
    )
    .await;
}

/// Creates `k_sol_dex_event_coverage_entries`.
async fn create_tbl_dex_event_coverage_entries(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_dex_event_coverage_entries",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_dex_event_coverage_entries (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    coverage_key TEXT NOT NULL,
    decoder_code TEXT NOT NULL,
    program_id TEXT NULL,
    program_family TEXT NOT NULL,
    surface_kind TEXT NOT NULL,
    source_repo TEXT NULL,
    source_path TEXT NULL,
    entry_kind TEXT NOT NULL,
    entry_name TEXT NOT NULL,
    discriminator_hex TEXT NULL,
    discriminator_len INTEGER NULL,
    event_family TEXT NULL,
    expected_db_target TEXT NULL,
    proof_status TEXT NOT NULL,
    local_event_kind TEXT NULL,
    observed_count INTEGER NOT NULL DEFAULT 0,
    materialized_count INTEGER NOT NULL DEFAULT 0,
    trade_count INTEGER NOT NULL DEFAULT 0,
    first_signature TEXT NULL,
    last_signature TEXT NULL,
    notes TEXT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates unique index on `k_sol_dex_event_coverage_entries(coverage_key)`.
async fn create_uix_dex_event_coverage_entries_key(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_dex_event_coverage_entries_key",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_dex_event_coverage_entries_key
ON k_sol_dex_event_coverage_entries (coverage_key)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_dex_event_coverage_entries(decoder_code)`.
async fn create_idx_dex_event_coverage_entries_decoder(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_event_coverage_entries_decoder",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_event_coverage_entries_decoder
ON k_sol_dex_event_coverage_entries (decoder_code, entry_kind, entry_name)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_dex_event_coverage_entries(proof_status)`.
async fn create_idx_dex_event_coverage_entries_proof_status(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_event_coverage_entries_proof_status",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_event_coverage_entries_proof_status
ON k_sol_dex_event_coverage_entries (proof_status, decoder_code)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_dex_event_coverage_entries(event_family)`.
async fn create_idx_dex_event_coverage_entries_event_family(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_dex_event_coverage_entries_event_family",
        r#"
CREATE INDEX IF NOT EXISTS idx_dex_event_coverage_entries_event_family
ON k_sol_dex_event_coverage_entries (event_family, expected_db_target)
        "#,
    )
    .await;
}

async fn create_tbl_launch_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_launch_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_launch_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NOT NULL UNIQUE,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    actor_wallet TEXT NULL,
    event_role TEXT NOT NULL,
    related_account TEXT NULL,
    related_mint TEXT NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE CASCADE,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id) ON DELETE SET NULL,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id) ON DELETE SET NULL,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_launch_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_events_transaction_id
ON k_sol_launch_events (transaction_id)
        "#,
    )
    .await;
}

async fn create_idx_launch_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_events_pool_id
ON k_sol_launch_events (pool_id)
        "#,
    )
    .await;
}

async fn create_idx_launch_events_event_kind(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_events_event_kind",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_events_event_kind
ON k_sol_launch_events (event_kind)
        "#,
    )
    .await;
}

async fn create_uix_launch_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_launch_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_launch_events_decoded_event_id
ON k_sol_launch_events (decoded_event_id)
        "#,
    )
    .await;
}

async fn create_tbl_launch_surfaces(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_launch_surfaces",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_launch_surfaces (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    code TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    protocol_family TEXT NULL,
    is_enabled INTEGER NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

async fn create_tbl_launch_surface_keys(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_launch_surface_keys",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_launch_surface_keys (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    launch_surface_id INTEGER NOT NULL,
    match_kind TEXT NOT NULL,
    match_value TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(launch_surface_id) REFERENCES k_sol_launch_surfaces(id) ON DELETE CASCADE,
    UNIQUE(match_kind, match_value)
)
        "#,
    )
    .await;
}

async fn create_idx_launch_surface_keys_surface_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_surface_keys_surface_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_surface_keys_surface_id
ON k_sol_launch_surface_keys(launch_surface_id)
        "#,
    )
    .await;
}

async fn create_tbl_launch_attributions(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_launch_attributions",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_launch_attributions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    launch_surface_id INTEGER NOT NULL,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NOT NULL UNIQUE,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    matched_key_id INTEGER NULL,
    protocol_name TEXT NOT NULL,
    match_kind TEXT NOT NULL,
    matched_value TEXT NOT NULL,
    attributed_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(launch_surface_id) REFERENCES k_sol_launch_surfaces(id) ON DELETE CASCADE,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE CASCADE,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id) ON DELETE SET NULL,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE SET NULL,
    FOREIGN KEY(matched_key_id) REFERENCES k_sol_launch_surface_keys(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_launch_attributions_surface_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_attributions_surface_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_attributions_surface_id
ON k_sol_launch_attributions(launch_surface_id)
        "#,
    )
    .await;
}

async fn create_idx_launch_attributions_pool_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_launch_attributions_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_launch_attributions_pool_id
ON k_sol_launch_attributions(pool_id)
        "#,
    )
    .await;
}

async fn create_tbl_pool_origins(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pool_origins",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pool_origins (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL UNIQUE,
    pair_id INTEGER NULL,
    pool_listing_id INTEGER NULL,
    founding_transaction_id INTEGER NOT NULL,
    founding_decoded_event_id INTEGER NOT NULL UNIQUE,
    founding_signature TEXT NOT NULL,
    founding_protocol_name TEXT NOT NULL,
    founding_event_kind TEXT NOT NULL,
    source_kind INTEGER NOT NULL,
    source_endpoint_name TEXT NULL,
    launch_attribution_id INTEGER NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id) ON DELETE CASCADE,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id) ON DELETE CASCADE,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE SET NULL,
    FOREIGN KEY(pool_listing_id) REFERENCES k_sol_pool_listings(id) ON DELETE SET NULL,
    FOREIGN KEY(founding_transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(founding_decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE CASCADE,
    FOREIGN KEY(launch_attribution_id) REFERENCES k_sol_launch_attributions(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_pool_origins_dex_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_origins_dex_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_origins_dex_id
ON k_sol_pool_origins(dex_id)
        "#,
    )
    .await;
}

async fn create_idx_pool_origins_pair_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_origins_pair_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_origins_pair_id
ON k_sol_pool_origins(pair_id)
        "#,
    )
    .await;
}

async fn create_idx_pool_origins_listing_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_origins_listing_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_origins_listing_id
ON k_sol_pool_origins(pool_listing_id)
        "#,
    )
    .await;
}

async fn create_idx_pool_origins_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_origins_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_origins_transaction_id
ON k_sol_pool_origins(founding_transaction_id)
        "#,
    )
    .await;
}

async fn create_tbl_wallets(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_wallets",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_wallets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    address TEXT NOT NULL UNIQUE,
    label TEXT NULL,
    first_seen_at TEXT NOT NULL,
    last_seen_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

async fn create_tbl_wallet_participations(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_wallet_participations",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_wallet_participations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    wallet_id INTEGER NOT NULL,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    role TEXT NOT NULL,
    unique_key TEXT NOT NULL UNIQUE,
    source_kind INTEGER NOT NULL,
    source_endpoint_name TEXT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(wallet_id) REFERENCES k_sol_wallets(id) ON DELETE CASCADE,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE SET NULL,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id) ON DELETE SET NULL,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_wallet_participations_wallet_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_wallet_participations_wallet_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_wallet_participations_wallet_id
ON k_sol_wallet_participations(wallet_id)
        "#,
    )
    .await;
}

async fn create_idx_wallet_participations_pool_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_wallet_participations_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_wallet_participations_pool_id
ON k_sol_wallet_participations(pool_id)
        "#,
    )
    .await;
}

async fn create_idx_wallet_participations_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_wallet_participations_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_wallet_participations_transaction_id
ON k_sol_wallet_participations(transaction_id)
        "#,
    )
    .await;
}

async fn create_tbl_trade_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_trade_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_trade_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    dex_id INTEGER NOT NULL,
    pool_id INTEGER NOT NULL,
    pair_id INTEGER NOT NULL,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NOT NULL UNIQUE,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    trade_side TEXT NOT NULL,
    base_token_id INTEGER NOT NULL,
    quote_token_id INTEGER NOT NULL,
    base_amount_raw TEXT NULL,
    quote_amount_raw TEXT NULL,
    price_quote_per_base REAL NULL,
    source_kind INTEGER NOT NULL,
    source_endpoint_name TEXT NULL,
    payload_json TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id) ON DELETE CASCADE,
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id) ON DELETE CASCADE,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE CASCADE,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE CASCADE,
    FOREIGN KEY(base_token_id) REFERENCES k_sol_tokens(id) ON DELETE CASCADE,
    FOREIGN KEY(quote_token_id) REFERENCES k_sol_tokens(id) ON DELETE CASCADE
)
        "#,
    )
    .await;
}

async fn create_idx_trade_events_pair_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_trade_events_pair_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_trade_events_pair_id
ON k_sol_trade_events(pair_id)
        "#,
    )
    .await;
}

async fn create_idx_trade_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_trade_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_trade_events_pool_id
ON k_sol_trade_events(pool_id)
        "#,
    )
    .await;
}

async fn create_tbl_pair_metrics(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pair_metrics",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pair_metrics (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    pair_id INTEGER NOT NULL UNIQUE,
    first_slot INTEGER NULL,
    last_slot INTEGER NULL,
    first_signature TEXT NULL,
    last_signature TEXT NULL,
    trade_count INTEGER NOT NULL,
    buy_count INTEGER NOT NULL,
    sell_count INTEGER NOT NULL,
    cumulative_base_amount_raw TEXT NULL,
    cumulative_quote_amount_raw TEXT NULL,
    last_price_quote_per_base REAL NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE CASCADE
)
        "#,
    )
    .await;
}

async fn create_tbl_wallet_holdings(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_wallet_holdings",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_wallet_holdings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    wallet_id INTEGER NOT NULL,
    token_id INTEGER NOT NULL,
    first_transaction_id INTEGER NOT NULL,
    last_transaction_id INTEGER NOT NULL,
    last_decoded_event_id INTEGER NULL,
    last_pool_id INTEGER NULL,
    last_pair_id INTEGER NULL,
    last_role TEXT NULL,
    balance_raw TEXT NULL,
    last_slot_observed INTEGER NULL,
    source_kind INTEGER NOT NULL,
    source_endpoint_name TEXT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    UNIQUE(wallet_id, token_id),
    FOREIGN KEY(wallet_id) REFERENCES k_sol_wallets(id) ON DELETE CASCADE,
    FOREIGN KEY(token_id) REFERENCES k_sol_tokens(id) ON DELETE CASCADE,
    FOREIGN KEY(first_transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(last_transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE CASCADE,
    FOREIGN KEY(last_decoded_event_id) REFERENCES k_sol_dex_decoded_events(id) ON DELETE SET NULL,
    FOREIGN KEY(last_pool_id) REFERENCES k_sol_pools(id) ON DELETE SET NULL,
    FOREIGN KEY(last_pair_id) REFERENCES k_sol_pairs(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_wallet_holdings_wallet_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_wallet_holdings_wallet_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_wallet_holdings_wallet_id
ON k_sol_wallet_holdings(wallet_id)
        "#,
    )
    .await;
}

async fn create_idx_wallet_holdings_token_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_wallet_holdings_token_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_wallet_holdings_token_id
ON k_sol_wallet_holdings(token_id)
        "#,
    )
    .await;
}

async fn create_tbl_pair_candles(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pair_candles",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pair_candles (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    pair_id INTEGER NOT NULL,
    timeframe_seconds INTEGER NOT NULL,
    bucket_start_unix INTEGER NOT NULL,
    bucket_end_unix INTEGER NOT NULL,
    open_price_quote_per_base REAL NOT NULL,
    high_price_quote_per_base REAL NOT NULL,
    low_price_quote_per_base REAL NOT NULL,
    close_price_quote_per_base REAL NOT NULL,
    trade_count INTEGER NOT NULL,
    buy_count INTEGER NOT NULL,
    sell_count INTEGER NOT NULL,
    base_volume_raw TEXT NULL,
    quote_volume_raw TEXT NULL,
    first_trade_signature TEXT NULL,
    last_trade_signature TEXT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    UNIQUE(pair_id, timeframe_seconds, bucket_start_unix),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE CASCADE
)
        "#,
    )
    .await;
}

async fn create_idx_pair_candles_pair_timeframe(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pair_candles_pair_timeframe",
        r#"
CREATE INDEX IF NOT EXISTS idx_pair_candles_pair_timeframe
ON k_sol_pair_candles(pair_id, timeframe_seconds)
        "#,
    )
    .await;
}

async fn create_idx_pair_candles_bucket(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pair_candles_bucket",
        r#"
CREATE INDEX IF NOT EXISTS idx_pair_candles_bucket
ON k_sol_pair_candles(bucket_start_unix)
        "#,
    )
    .await;
}

async fn create_tbl_pair_analytic_signals(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pair_analytic_signals",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pair_analytic_signals (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    pair_id INTEGER NOT NULL,
    signal_kind TEXT NOT NULL,
    severity INTEGER NOT NULL,
    timeframe_seconds INTEGER NOT NULL,
    bucket_start_unix INTEGER NOT NULL,
    score REAL NULL,
    signal_value_json TEXT NOT NULL,
    first_transaction_id INTEGER NULL,
    last_transaction_id INTEGER NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    UNIQUE(pair_id, signal_kind, timeframe_seconds, bucket_start_unix),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id) ON DELETE CASCADE,
    FOREIGN KEY(first_transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE SET NULL,
    FOREIGN KEY(last_transaction_id) REFERENCES k_sol_chain_transactions(id) ON DELETE SET NULL
)
        "#,
    )
    .await;
}

async fn create_idx_pair_analytic_signals_pair_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pair_analytic_signals_pair_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pair_analytic_signals_pair_id
ON k_sol_pair_analytic_signals(pair_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_transaction_classifications`.
async fn create_tbl_transaction_classifications(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_transaction_classifications",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_transaction_classifications (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    classification_kind TEXT NOT NULL,
    primary_protocol TEXT NULL,
    primary_program_id TEXT NULL,
    confidence_level INTEGER NOT NULL,
    reason TEXT NOT NULL,
    evidence_json TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates unique index on `k_sol_transaction_classifications(transaction_id)`.
async fn create_uix_transaction_classifications_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_transaction_classifications_transaction_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_transaction_classifications_transaction_id
ON k_sol_transaction_classifications (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_transaction_classifications(classification_kind)`.
async fn create_idx_transaction_classifications_kind(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_transaction_classifications_kind",
        r#"
CREATE INDEX IF NOT EXISTS idx_transaction_classifications_kind
ON k_sol_transaction_classifications (classification_kind)
        "#,
    )
    .await;
}

/// Creates `k_sol_protocol_candidates`.
async fn create_tbl_protocol_candidates(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_protocol_candidates",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_protocol_candidates (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    instruction_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    program_id TEXT NOT NULL,
    program_name_hint TEXT NULL,
    candidate_protocol TEXT NULL,
    candidate_surface TEXT NULL,
    reason TEXT NOT NULL,
    evidence_json TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_protocol_candidates(transaction_id)`.
async fn create_idx_protocol_candidates_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_protocol_candidates_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_protocol_candidates_transaction_id
ON k_sol_protocol_candidates (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_protocol_candidates(program_id)`.
async fn create_idx_protocol_candidates_program_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_protocol_candidates_program_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_protocol_candidates_program_id
ON k_sol_protocol_candidates (program_id)
        "#,
    )
    .await;
}

/// Creates `k_sol_pool_lifecycle_events`.
async fn create_tbl_pool_lifecycle_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pool_lifecycle_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pool_lifecycle_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    token_a_mint TEXT NULL,
    token_b_mint TEXT NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_lifecycle_events(transaction_id)`.
async fn create_idx_pool_lifecycle_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_lifecycle_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_lifecycle_events_transaction_id
ON k_sol_pool_lifecycle_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_lifecycle_events(pool_id)`.
async fn create_idx_pool_lifecycle_events_pool_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_lifecycle_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_lifecycle_events_pool_id
ON k_sol_pool_lifecycle_events (pool_id)
        "#,
    )
    .await;
}

/// Creates partial unique index on `k_sol_pool_lifecycle_events(decoded_event_id)`.
async fn create_uix_pool_lifecycle_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_pool_lifecycle_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_pool_lifecycle_events_decoded_event_id
ON k_sol_pool_lifecycle_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}

/// Creates `k_sol_fee_events`.
async fn create_tbl_fee_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_fee_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_fee_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    actor_wallet TEXT NULL,
    fee_token_mint TEXT NULL,
    fee_amount_raw TEXT NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_events(transaction_id)`.
async fn create_idx_fee_events_transaction_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_events_transaction_id
ON k_sol_fee_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_events(pool_id)`.
async fn create_idx_fee_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_events_pool_id
ON k_sol_fee_events (pool_id)
        "#,
    )
    .await;
}

/// Creates partial unique index on `k_sol_fee_events(decoded_event_id)`.
async fn create_uix_fee_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_fee_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_fee_events_decoded_event_id
ON k_sol_fee_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}

/// Creates `k_sol_fee_event_amounts`.
async fn create_tbl_fee_event_amounts(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_fee_event_amounts",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_fee_event_amounts (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    fee_event_id INTEGER NOT NULL,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    leg_index INTEGER NOT NULL,
    fee_component_kind TEXT NOT NULL,
    token_mint TEXT NOT NULL,
    amount_raw TEXT NOT NULL,
    source_account TEXT NULL,
    destination_account TEXT NULL,
    amount_source TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_event_amounts(fee_event_id)`.
async fn create_idx_fee_event_amounts_fee_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_event_amounts_fee_event_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_event_amounts_fee_event_id
ON k_sol_fee_event_amounts (fee_event_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_event_amounts(transaction_id)`.
async fn create_idx_fee_event_amounts_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_event_amounts_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_event_amounts_transaction_id
ON k_sol_fee_event_amounts (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_event_amounts(decoded_event_id)`.
async fn create_idx_fee_event_amounts_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_event_amounts_decoded_event_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_event_amounts_decoded_event_id
ON k_sol_fee_event_amounts (decoded_event_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_fee_event_amounts(token_mint)`.
async fn create_idx_fee_event_amounts_token_mint(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_fee_event_amounts_token_mint",
        r#"
CREATE INDEX IF NOT EXISTS idx_fee_event_amounts_token_mint
ON k_sol_fee_event_amounts (token_mint)
        "#,
    )
    .await;
}

/// Creates unique index on one fee amount leg per parent fee event and leg index.
async fn create_uix_fee_event_amounts_fee_event_leg(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_fee_event_amounts_fee_event_leg",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_fee_event_amounts_fee_event_leg
ON k_sol_fee_event_amounts (fee_event_id, leg_index)
        "#,
    )
    .await;
}

/// Creates `k_sol_reward_events`.
async fn create_tbl_reward_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_reward_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_reward_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    actor_wallet TEXT NULL,
    reward_token_mint TEXT NULL,
    reward_amount_raw TEXT NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_reward_events(transaction_id)`.
async fn create_idx_reward_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_reward_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_reward_events_transaction_id
ON k_sol_reward_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_reward_events(pool_id)`.
async fn create_idx_reward_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_reward_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_reward_events_pool_id
ON k_sol_reward_events (pool_id)
        "#,
    )
    .await;
}

/// Creates partial unique index on `k_sol_reward_events(decoded_event_id)`.
async fn create_uix_reward_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_reward_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_reward_events_decoded_event_id
ON k_sol_reward_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}

/// Creates `k_sol_pool_admin_events`.
async fn create_tbl_pool_admin_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_pool_admin_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_pool_admin_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    pool_account TEXT NULL,
    actor_wallet TEXT NULL,
    admin_action TEXT NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_admin_events(transaction_id)`.
async fn create_idx_pool_admin_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_admin_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_admin_events_transaction_id
ON k_sol_pool_admin_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_pool_admin_events(pool_id)`.
async fn create_idx_pool_admin_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_pool_admin_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_pool_admin_events_pool_id
ON k_sol_pool_admin_events (pool_id)
        "#,
    )
    .await;
}

/// Creates partial unique index on `k_sol_pool_admin_events(decoded_event_id)`.
async fn create_uix_pool_admin_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_pool_admin_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_pool_admin_events_decoded_event_id
ON k_sol_pool_admin_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}

/// Creates `k_sol_orderbook_events`.
async fn create_tbl_orderbook_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_orderbook_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_orderbook_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NOT NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NOT NULL,
    event_kind TEXT NOT NULL,
    order_action TEXT NOT NULL,
    pool_account TEXT NULL,
    market_account TEXT NULL,
    actor_wallet TEXT NULL,
    order_account TEXT NULL,
    base_token_mint TEXT NULL,
    quote_token_mint TEXT NULL,
    amount_raw TEXT NULL,
    amount_min_raw TEXT NULL,
    tick_index INTEGER NULL,
    zero_for_one INTEGER NULL,
    payload_json TEXT NOT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NOT NULL
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_orderbook_events(transaction_id)`.
async fn create_idx_orderbook_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_orderbook_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_orderbook_events_transaction_id
ON k_sol_orderbook_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_orderbook_events(pool_id)`.
async fn create_idx_orderbook_events_pool_id(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_orderbook_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_orderbook_events_pool_id
ON k_sol_orderbook_events (pool_id)
        "#,
    )
    .await;
}

/// Creates partial unique index on `k_sol_orderbook_events(decoded_event_id)`.
async fn create_uix_orderbook_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_orderbook_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_orderbook_events_decoded_event_id
ON k_sol_orderbook_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}

/// Creates `k_sol_token_account_events`.
async fn create_tbl_token_account_events(pool: &sqlx::SqlitePool) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_tbl_token_account_events",
        r#"
CREATE TABLE IF NOT EXISTS k_sol_token_account_events (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    transaction_id INTEGER NULL,
    decoded_event_id INTEGER NULL,
    dex_id INTEGER NULL,
    pool_id INTEGER NULL,
    pair_id INTEGER NULL,
    signature TEXT NOT NULL,
    instruction_index INTEGER NOT NULL,
    slot INTEGER NULL,
    protocol_name TEXT NOT NULL,
    program_id TEXT NULL,
    event_kind TEXT NOT NULL,
    token_account TEXT NULL,
    token_mint TEXT NULL,
    owner_wallet TEXT NULL,
    account_action TEXT NULL,
    payload_json TEXT NULL,
    executed_at TEXT NOT NULL,
    created_at TEXT NULL,
    FOREIGN KEY(transaction_id) REFERENCES k_sol_chain_transactions(id),
    FOREIGN KEY(decoded_event_id) REFERENCES k_sol_dex_decoded_events(id),
    FOREIGN KEY(dex_id) REFERENCES k_sol_dexes(id),
    FOREIGN KEY(pool_id) REFERENCES k_sol_pools(id),
    FOREIGN KEY(pair_id) REFERENCES k_sol_pairs(id)
)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_account_events(transaction_id)`.
async fn create_idx_token_account_events_transaction_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_account_events_transaction_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_account_events_transaction_id
ON k_sol_token_account_events (transaction_id)
        "#,
    )
    .await;
}

/// Creates index on `k_sol_token_account_events(pool_id)`.
async fn create_idx_token_account_events_pool_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_idx_token_account_events_pool_id",
        r#"
CREATE INDEX IF NOT EXISTS idx_token_account_events_pool_id
ON k_sol_token_account_events (pool_id)
        "#,
    )
    .await;
}

/// Creates unique index on `k_sol_token_account_events(decoded_event_id)`.
async fn create_uix_token_account_events_decoded_event_id(
    pool: &sqlx::SqlitePool,
) -> Result<(), crate::Error> {
    return execute_sqlite_schema_statement(
        pool,
        "create_uix_token_account_events_decoded_event_id",
        r#"
CREATE UNIQUE INDEX IF NOT EXISTS uix_token_account_events_decoded_event_id
ON k_sol_token_account_events (decoded_event_id)
WHERE decoded_event_id IS NOT NULL
        "#,
    )
    .await;
}
