// file: kb_lib/src/local_pipeline_replay.rs

//! Local pipeline replay from already persisted raw transaction data.
//!
//! This service does not fetch historical transactions from Solana RPC. It
//! reuses rows already present in `k_sol_chain_transactions` and replays the
//! deterministic local pipeline over their signatures.

/// Configuration for a local pipeline replay pass.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalPipelineReplayConfig {
    /// Maximum number of persisted transactions to replay.
    pub limit: std::option::Option<i64>,
    /// Whether token metadata backfill should run after local replay.
    pub refresh_missing_token_metadata: bool,
    /// Maximum number of missing token metadata rows to resolve.
    pub token_metadata_limit: std::option::Option<i64>,
}

impl Default for LocalPipelineReplayConfig {
    fn default() -> Self {
        return Self {
            limit: Some(10_000),
            refresh_missing_token_metadata: false,
            token_metadata_limit: Some(250),
        };
    }
}

/// Summary of a local pipeline replay pass.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalPipelineReplayResult {
    /// Number of transaction signatures selected for replay.
    pub selected_transaction_count: usize,
    /// Number of transactions replayed without a fatal per-signature error.
    pub replayed_transaction_count: usize,
    /// Number of transactions that produced a decode error.
    pub decode_error_count: usize,
    /// Number of transactions that produced a detect error.
    pub detect_error_count: usize,
    /// Number of transactions that produced a trade aggregation error.
    pub trade_aggregation_error_count: usize,
    /// Number of transactions that produced a candle aggregation error.
    pub pair_candle_error_count: usize,
    /// Number of transactions that produced an analytic signal error.
    pub analytic_signal_error_count: usize,
    /// Total decoded events returned by replayed decode calls.
    pub decoded_event_count: usize,
    /// Total detection results returned by replayed detect calls.
    pub detection_count: usize,
    /// Total trade aggregation results returned by replayed aggregation calls.
    pub trade_event_count: usize,
    /// Total candle upsert results returned by replayed candle calls.
    ///
    /// This is a replay write/result counter, not the number of distinct rows
    /// currently persisted in `k_sol_pair_candles`. Use local diagnostics for the
    /// persisted row count.
    pub pair_candle_upsert_count: usize,
    /// Total analytic signal upsert results returned by replayed analytic calls.
    ///
    /// This is a replay write/result counter, not the number of distinct rows
    /// currently persisted in the analytic signal table.
    pub analytic_signal_upsert_count: usize,
    /// Total transaction classification rows upserted during replay.
    pub transaction_classification_count: usize,
    /// Number of transactions that produced a classification error.
    pub transaction_classification_error_count: usize,
    /// Number of token metadata rows updated after replay.
    pub token_metadata_updated_count: usize,
    /// Number of pair symbols updated after replay.
    pub pair_symbol_updated_count: usize,
    /// Number of errors outside per-signature replay.
    pub global_error_count: usize,
}

/// Local pipeline replay service.
#[derive(Debug, Clone)]
pub struct LocalPipelineReplayService {
    database: std::sync::Arc<crate::Database>,
    http_pool: std::option::Option<std::sync::Arc<crate::HttpEndpointPool>>,
    http_role: std::string::String,
}

impl LocalPipelineReplayService {
    /// Creates a new local-only pipeline replay service.
    pub fn new(database: std::sync::Arc<crate::Database>) -> Self {
        return Self {
            database,
            http_pool: None,
            http_role: "local_metadata".to_string(),
        };
    }

    /// Creates a new pipeline replay service able to refresh token metadata over Solana HTTP RPC.
    pub fn new_with_http_pool(
        http_pool: std::sync::Arc<crate::HttpEndpointPool>,
        database: std::sync::Arc<crate::Database>,
        http_role: std::string::String,
    ) -> Self {
        return Self {
            database,
            http_pool: Some(http_pool),
            http_role,
        };
    }

    /// Replays the local pipeline from persisted raw chain transaction rows.
    pub async fn replay_local_pipeline(
        &self,
        config: &crate::LocalPipelineReplayConfig,
    ) -> Result<crate::LocalPipelineReplayResult, crate::Error> {
        let signatures_result = crate::query_chain_transactions_list_signatures_for_replay(
            self.database.as_ref(),
            config.limit,
        )
        .await;
        let signatures = match signatures_result {
            Ok(signatures) => signatures,
            Err(error) => return Err(error),
        };
        let dex_decode = crate::DexDecodeService::new(self.database.clone());
        let dex_detect = crate::DexDetectService::new(self.database.clone());
        let trade_aggregation = crate::TradeAggregationService::new(self.database.clone());
        let pair_candle_aggregation =
            crate::PairCandleAggregationService::new(self.database.clone());
        let pair_analytic_signal = crate::PairAnalyticSignalService::new(self.database.clone());
        let transaction_classification =
            crate::TransactionClassificationService::new(self.database.clone());
        let mut result = LocalPipelineReplayResult {
            selected_transaction_count: signatures.len(),
            ..Default::default()
        };
        for signature in signatures {
            tracing::debug!(
                signature = %signature,
                "replaying local pipeline for persisted transaction"
            );
            let decode_result =
                dex_decode.decode_transaction_by_signature(signature.as_str()).await;
            match decode_result {
                Ok(decoded_events) => {
                    result.decoded_event_count += decoded_events.len();
                },
                Err(error) => {
                    result.decode_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay decode step failed"
                    );
                    continue;
                },
            }
            let detect_result =
                dex_detect.detect_transaction_by_signature(signature.as_str()).await;
            match detect_result {
                Ok(detections) => {
                    result.detection_count += detections.len();
                },
                Err(error) => {
                    result.detect_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay detect step failed; continuing with aggregation"
                    );
                },
            }
            let trade_result =
                trade_aggregation.record_transaction_by_signature(signature.as_str()).await;
            match trade_result {
                Ok(trade_results) => {
                    result.trade_event_count += trade_results.len();
                },
                Err(error) => {
                    result.trade_aggregation_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay trade aggregation step failed"
                    );
                    continue;
                },
            }
            let candle_result = pair_candle_aggregation
                .record_transaction_by_signature(signature.as_str())
                .await;
            match candle_result {
                Ok(candle_results) => {
                    result.pair_candle_upsert_count += candle_results.len();
                },
                Err(error) => {
                    result.pair_candle_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay candle aggregation step failed"
                    );
                },
            }
            let analytic_result =
                pair_analytic_signal.record_transaction_by_signature(signature.as_str()).await;
            match analytic_result {
                Ok(analytic_results) => {
                    result.analytic_signal_upsert_count += analytic_results.len();
                },
                Err(error) => {
                    result.analytic_signal_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay analytic signal step failed"
                    );
                },
            }
            let classification_result = transaction_classification
                .classify_transaction_by_signature(signature.as_str())
                .await;
            match classification_result {
                Ok(_) => {
                    result.transaction_classification_count += 1;
                },
                Err(error) => {
                    result.transaction_classification_error_count += 1;
                    tracing::warn!(
                        signature = %signature,
                        error = %error,
                        "local pipeline replay transaction classification step failed"
                    );
                },
            }
            result.replayed_transaction_count += 1;
        }
        if config.refresh_missing_token_metadata {
            let metadata_service = match &self.http_pool {
                Some(http_pool) => crate::TokenMetadataBackfillService::new(
                    http_pool.clone(),
                    self.database.clone(),
                    self.http_role.clone(),
                ),
                None => crate::TokenMetadataBackfillService::new_local(self.database.clone()),
            };
            let metadata_result = metadata_service
                .backfill_missing_token_metadata(config.token_metadata_limit)
                .await;
            match metadata_result {
                Ok(metadata_result) => {
                    result.token_metadata_updated_count += metadata_result.updated_token_count;
                },
                Err(error) => {
                    result.global_error_count += 1;
                    tracing::warn!(
                        error = %error,
                        "token metadata refresh failed after local pipeline replay"
                    );
                },
            }
        }
        return Ok(result);
    }
}

/// Replays the local pipeline from persisted raw chain transaction rows.
pub async fn replay_local_pipeline(
    database: std::sync::Arc<crate::Database>,
    config: &crate::LocalPipelineReplayConfig,
) -> Result<crate::LocalPipelineReplayResult, crate::Error> {
    let service = crate::LocalPipelineReplayService::new(database);
    return service.replay_local_pipeline(config).await;
}
