// file: kb_lib/src/token_metadata.rs

//! Token metadata resolution and backfill.
//!
//! This module enriches already discovered token mints with stable metadata.
//! It intentionally stays independent from DEX-specific decoding so every DEX
//! benefits from the same metadata path.

const NATIVE_SOL_MINT_ALIAS: &str = "SOL";
const NATIVE_SOL_SYMBOL: &str = "SOL";
const NATIVE_SOL_NAME: &str = "Solana";
const WRAPPED_SOL_SYMBOL: &str = "WSOL";
const WRAPPED_SOL_NAME: &str = "Wrapped SOL";
const METAPLEX_TOKEN_METADATA_PROGRAM_ID: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";

#[derive(Debug, Clone, Copy)]
struct KnownLocalTokenMetadata {
    mint: &'static str,
    symbol: &'static str,
    name: &'static str,
    decimals: u8,
    token_program: &'static str,
    is_quote_token: std::option::Option<bool>,
}

const KNOWN_LOCAL_TOKEN_METADATA: &[KnownLocalTokenMetadata] = &[
    KnownLocalTokenMetadata {
        mint: crate::WSOL_MINT_ID,
        symbol: WRAPPED_SOL_SYMBOL,
        name: WRAPPED_SOL_NAME,
        decimals: 9,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: Some(true),
    },
    KnownLocalTokenMetadata {
        mint: crate::USDC_MINT_ID,
        symbol: "USDC",
        name: "USD Coin",
        decimals: 6,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: Some(true),
    },
    KnownLocalTokenMetadata {
        mint: crate::USDT_MINT_ID,
        symbol: "USDT",
        name: "Tether USD",
        decimals: 6,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: Some(true),
    },
    KnownLocalTokenMetadata {
        mint: crate::JUP_MINT_ID,
        symbol: "JUP",
        name: "Jupiter",
        decimals: 6,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: None,
    },
    KnownLocalTokenMetadata {
        mint: crate::RAY_MINT_ID,
        symbol: "RAY",
        name: "Raydium",
        decimals: 6,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: None,
    },
    KnownLocalTokenMetadata {
        mint: crate::BONK_MINT_ID,
        symbol: "BONK",
        name: "Bonk",
        decimals: 5,
        token_program: crate::SPL_TOKEN_PROGRAM_ID,
        is_quote_token: None,
    },
];

/// Summary produced by a token metadata backfill pass.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenMetadataBackfillResult {
    /// Number of tokens considered by this pass.
    pub total_token_count: usize,
    /// Number of tokens for which a resolution attempt was made.
    pub attempted_token_count: usize,
    /// Number of tokens enriched from local deterministic metadata.
    pub local_metadata_count: usize,
    /// Number of tokens enriched from Pump.fun decoded payloads.
    pub pump_fun_payload_metadata_count: usize,
    /// Number of tokens enriched from the SPL mint account.
    pub mint_account_metadata_count: usize,
    /// Number of tokens enriched from the Metaplex metadata account.
    pub metaplex_metadata_count: usize,
    /// Number of token rows updated.
    pub updated_token_count: usize,
    /// Number of token rows already complete or not resolvable without error.
    pub skipped_token_count: usize,
    /// Number of non-fatal per-token errors.
    pub error_count: usize,
    /// Number of pair display symbols updated after metadata enrichment.
    pub pair_symbol_updated_count: usize,
    /// Number of pair display symbols already correct after metadata enrichment.
    pub pair_symbol_skipped_count: usize,
    /// Number of pair display symbols skipped because a related token row was missing.
    pub pair_symbol_missing_token_count: usize,
}

impl TokenMetadataBackfillResult {
    fn merge(&mut self, other: &TokenMetadataBackfillResult) {
        self.total_token_count += other.total_token_count;
        self.attempted_token_count += other.attempted_token_count;
        self.local_metadata_count += other.local_metadata_count;
        self.pump_fun_payload_metadata_count += other.pump_fun_payload_metadata_count;
        self.mint_account_metadata_count += other.mint_account_metadata_count;
        self.metaplex_metadata_count += other.metaplex_metadata_count;
        self.updated_token_count += other.updated_token_count;
        self.skipped_token_count += other.skipped_token_count;
        self.error_count += other.error_count;
        self.pair_symbol_updated_count += other.pair_symbol_updated_count;
        self.pair_symbol_skipped_count += other.pair_symbol_skipped_count;
        self.pair_symbol_missing_token_count += other.pair_symbol_missing_token_count;
    }
}

/// Service that enriches persisted token rows with mint and display metadata.
#[derive(Debug, Clone)]
pub struct TokenMetadataBackfillService {
    database: std::sync::Arc<crate::Database>,
    http_pool: std::option::Option<std::sync::Arc<crate::HttpEndpointPool>>,
    http_role: std::string::String,
}

impl TokenMetadataBackfillService {
    /// Creates a metadata backfill service backed by Solana HTTP RPC.
    pub fn new(
        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,
        };
    }

    /// Creates a metadata backfill service that can only apply local and DB-derived metadata.
    pub fn new_local(database: std::sync::Arc<crate::Database>) -> Self {
        return Self {
            database,
            http_pool: None,
            http_role: "local_metadata".to_string(),
        };
    }

    /// Enriches all tokens whose metadata is incomplete.
    pub async fn backfill_missing_token_metadata(
        &self,
        limit: std::option::Option<i64>,
    ) -> Result<crate::TokenMetadataBackfillResult, crate::Error> {
        let tokens_result =
            crate::query_tokens_list_missing_metadata(self.database.as_ref(), limit).await;
        let tokens = match tokens_result {
            Ok(tokens) => tokens,
            Err(error) => return Err(error),
        };
        let mut result = crate::TokenMetadataBackfillResult::default();
        for token in tokens {
            let token_result = self.backfill_token_metadata_by_mint(token.mint.as_str()).await;
            match token_result {
                Ok(token_result) => result.merge(&token_result),
                Err(error) => {
                    result.total_token_count += 1;
                    result.attempted_token_count += 1;
                    result.error_count += 1;
                    tracing::warn!(
                        mint = %token.mint,
                        error = %error,
                        "token metadata backfill failed for one mint"
                    );
                },
            }
        }
        let pair_symbol_result = crate::refresh_pair_symbols(self.database.as_ref()).await;
        match pair_symbol_result {
            Ok(pair_symbol_result) => {
                result.pair_symbol_updated_count += pair_symbol_result.updated_pair_count;
                result.pair_symbol_skipped_count += pair_symbol_result.skipped_pair_count;
                result.pair_symbol_missing_token_count += pair_symbol_result.missing_token_count;
            },
            Err(error) => {
                result.error_count += 1;
                tracing::warn!(
                    error = %error,
                    "pair symbol refresh failed after token metadata backfill"
                );
            },
        }
        return Ok(result);
    }

    /// Enriches one token mint with local, DEX-payload, mint-account, and Metaplex metadata.
    pub async fn backfill_token_metadata_by_mint(
        &self,
        mint: &str,
    ) -> Result<crate::TokenMetadataBackfillResult, crate::Error> {
        let trimmed_mint = mint.trim();
        if trimmed_mint.is_empty() {
            return Err(crate::Error::Config("token metadata mint must not be empty".to_string()));
        }
        let mut result = crate::TokenMetadataBackfillResult {
            total_token_count: 1,
            attempted_token_count: 1,
            local_metadata_count: 0,
            pump_fun_payload_metadata_count: 0,
            mint_account_metadata_count: 0,
            metaplex_metadata_count: 0,
            updated_token_count: 0,
            skipped_token_count: 0,
            error_count: 0,
            pair_symbol_updated_count: 0,
            pair_symbol_skipped_count: 0,
            pair_symbol_missing_token_count: 0,
        };
        let token_result =
            crate::query_tokens_get_by_mint(self.database.as_ref(), trimmed_mint).await;
        let token_option = match token_result {
            Ok(token_option) => token_option,
            Err(error) => return Err(error),
        };
        let token_existed = token_option.is_some();
        let mut token = match token_option {
            Some(token) => token,
            None => build_empty_token_dto(trimmed_mint),
        };
        let original_token = token.clone();
        let mut resolved = ResolvedTokenMetadata::default();
        let local_metadata_option = resolve_local_token_metadata(trimmed_mint);
        if let Some(local_metadata) = local_metadata_option {
            result.local_metadata_count += 1;
            resolved.merge(local_metadata);
        }
        let pump_fun_metadata_result =
            resolve_pump_fun_metadata_from_decoded_events(self.database.as_ref(), trimmed_mint)
                .await;
        match pump_fun_metadata_result {
            Ok(pump_fun_metadata_option) => {
                if let Some(pump_fun_metadata) = pump_fun_metadata_option {
                    result.pump_fun_payload_metadata_count += 1;
                    resolved.merge(pump_fun_metadata);
                }
            },
            Err(error) => {
                result.error_count += 1;
                tracing::warn!(
                    mint = %trimmed_mint,
                    error = %error,
                    "pump.fun metadata payload resolution failed"
                );
            },
        }
        if let Some(http_pool) = &self.http_pool {
            let mint_account_result = fetch_mint_account_metadata(
                http_pool.as_ref(),
                self.http_role.as_str(),
                trimmed_mint,
            )
            .await;
            match mint_account_result {
                Ok(mint_account_option) => {
                    if let Some(mint_account) = mint_account_option {
                        result.mint_account_metadata_count += 1;
                        resolved.merge(mint_account);
                    }
                },
                Err(error) => {
                    result.error_count += 1;
                    tracing::warn!(
                        mint = %trimmed_mint,
                        error = %error,
                        "mint account metadata resolution failed"
                    );
                },
            }
            if should_try_metaplex_metadata(&token, &resolved) {
                let metaplex_result = fetch_metaplex_metadata(
                    http_pool.as_ref(),
                    self.http_role.as_str(),
                    trimmed_mint,
                )
                .await;
                match metaplex_result {
                    Ok(metaplex_option) => {
                        if let Some(metaplex_metadata) = metaplex_option {
                            result.metaplex_metadata_count += 1;
                            resolved.merge(metaplex_metadata);
                        }
                    },
                    Err(error) => {
                        result.error_count += 1;
                        tracing::warn!(
                            mint = %trimmed_mint,
                            error = %error,
                            "metaplex metadata resolution failed"
                        );
                    },
                }
            }
        }
        let changed = apply_resolved_metadata_to_token(&mut token, &resolved);
        if !token_existed && !changed {
            result.skipped_token_count += 1;
            return Ok(result);
        }
        if changed || token.id.is_none() {
            token.updated_at = chrono::Utc::now();
            let upsert_result = crate::query_tokens_upsert(self.database.as_ref(), &token).await;
            match upsert_result {
                Ok(_) => {
                    if changed || original_token.id.is_none() {
                        result.updated_token_count += 1;
                    } else {
                        result.skipped_token_count += 1;
                    }
                },
                Err(error) => return Err(error),
            }
        } else {
            result.skipped_token_count += 1;
        }
        return Ok(result);
    }
}

#[derive(Debug, Clone, Default)]
struct ResolvedTokenMetadata {
    symbol: std::option::Option<std::string::String>,
    name: std::option::Option<std::string::String>,
    decimals: std::option::Option<u8>,
    token_program: std::option::Option<std::string::String>,
    is_quote_token: std::option::Option<bool>,
}

impl ResolvedTokenMetadata {
    fn merge(&mut self, other: ResolvedTokenMetadata) {
        if option_string_is_missing(self.symbol.as_deref()) && other.symbol.is_some() {
            self.symbol = other.symbol;
        }
        if option_string_is_missing(self.name.as_deref()) && other.name.is_some() {
            self.name = other.name;
        }
        if self.decimals.is_none() && other.decimals.is_some() {
            self.decimals = other.decimals;
        }
        if option_string_is_missing(self.token_program.as_deref()) && other.token_program.is_some()
        {
            self.token_program = other.token_program;
        }
        if self.is_quote_token.is_none() && other.is_quote_token.is_some() {
            self.is_quote_token = other.is_quote_token;
        }
    }
}

fn build_empty_token_dto(mint: &str) -> crate::TokenDto {
    return crate::TokenDto::new(
        mint.to_string(),
        None,
        None,
        None,
        crate::SPL_TOKEN_PROGRAM_ID.to_string(),
        false,
    );
}

fn resolve_local_token_metadata(mint: &str) -> std::option::Option<ResolvedTokenMetadata> {
    if mint == NATIVE_SOL_MINT_ALIAS {
        return Some(ResolvedTokenMetadata {
            symbol: Some(NATIVE_SOL_SYMBOL.to_string()),
            name: Some(NATIVE_SOL_NAME.to_string()),
            decimals: Some(9),
            token_program: Some(crate::SYSTEM_PROGRAM_ID.to_string()),
            is_quote_token: Some(true),
        });
    }
    for known_token in KNOWN_LOCAL_TOKEN_METADATA {
        if mint == known_token.mint {
            return Some(ResolvedTokenMetadata {
                symbol: Some(known_token.symbol.to_string()),
                name: Some(known_token.name.to_string()),
                decimals: Some(known_token.decimals),
                token_program: Some(known_token.token_program.to_string()),
                is_quote_token: known_token.is_quote_token,
            });
        }
    }
    return None;
}

async fn resolve_pump_fun_metadata_from_decoded_events(
    database: &crate::Database,
    mint: &str,
) -> Result<std::option::Option<ResolvedTokenMetadata>, crate::Error> {
    let payload_result =
        crate::query_dex_decoded_events_get_latest_pump_fun_create_payload_by_mint(database, mint)
            .await;
    let payload_option = match payload_result {
        Ok(payload_option) => payload_option,
        Err(error) => return Err(error),
    };
    let payload_text = match payload_option {
        Some(payload_text) => payload_text,
        None => return Ok(None),
    };
    let payload_result = serde_json::from_str::<serde_json::Value>(payload_text.as_str());
    let payload = match payload_result {
        Ok(payload) => payload,
        Err(error) => {
            return Err(crate::Error::Json(format!(
                "cannot parse pump.fun metadata payload for mint '{}': {}",
                mint, error
            )));
        },
    };
    return Ok(extract_pump_fun_metadata_from_payload(&payload));
}

fn extract_pump_fun_metadata_from_payload(
    payload: &serde_json::Value,
) -> std::option::Option<ResolvedTokenMetadata> {
    let symbol = extract_string_by_candidate_keys(payload, &["symbol", "tokenSymbol"]);
    let name = extract_string_by_candidate_keys(payload, &["name", "tokenName"]);
    let decimals = extract_u8_by_candidate_keys(payload, &["decimals", "tokenDecimals"]);
    if symbol.is_none() && name.is_none() && decimals.is_none() {
        return None;
    }
    return Some(ResolvedTokenMetadata {
        symbol,
        name,
        decimals,
        token_program: None,
        is_quote_token: None,
    });
}

fn should_try_metaplex_metadata(token: &crate::TokenDto, resolved: &ResolvedTokenMetadata) -> bool {
    let missing_symbol = option_string_is_missing(token.symbol.as_deref())
        && option_string_is_missing(resolved.symbol.as_deref());
    let missing_name = option_string_is_missing(token.name.as_deref())
        && option_string_is_missing(resolved.name.as_deref());
    return missing_symbol || missing_name;
}

fn apply_resolved_metadata_to_token(
    token: &mut crate::TokenDto,
    resolved: &ResolvedTokenMetadata,
) -> bool {
    let mut changed = false;
    if option_string_is_missing(token.symbol.as_deref()) {
        if let Some(symbol) = &resolved.symbol {
            if !symbol.trim().is_empty() {
                token.symbol = Some(symbol.trim().to_string());
                changed = true;
            }
        }
    }
    if option_string_is_missing(token.name.as_deref()) {
        if let Some(name) = &resolved.name {
            if !name.trim().is_empty() {
                token.name = Some(name.trim().to_string());
                changed = true;
            }
        }
    }
    if token.decimals.is_none() {
        if let Some(decimals) = resolved.decimals {
            token.decimals = Some(decimals);
            changed = true;
        }
    }
    if let Some(token_program) = &resolved.token_program {
        if !token_program.trim().is_empty() && token.token_program != *token_program {
            token.token_program = token_program.clone();
            changed = true;
        }
    }
    if let Some(is_quote_token) = resolved.is_quote_token {
        if token.is_quote_token != is_quote_token {
            token.is_quote_token = is_quote_token;
            changed = true;
        }
    }
    return changed;
}

async fn fetch_mint_account_metadata(
    http_pool: &crate::HttpEndpointPool,
    http_role: &str,
    mint: &str,
) -> Result<std::option::Option<ResolvedTokenMetadata>, crate::Error> {
    let config = serde_json::json!({
        "encoding": "jsonParsed",
        "commitment": "confirmed"
    });
    let account_result = http_pool
        .get_account_info_raw_for_role(http_role, mint.to_string(), Some(config))
        .await;
    let account_value = match account_result {
        Ok(account_value) => account_value,
        Err(error) => return Err(error),
    };
    let value_option = account_value.get("value");
    let value = match value_option {
        Some(value) => value,
        None => return Ok(None),
    };
    if value.is_null() {
        return Ok(None);
    }
    let owner = value
        .get("owner")
        .and_then(serde_json::Value::as_str)
        .map(|value| return value.to_string());
    let decimals = value
        .get("data")
        .and_then(|data| return data.get("parsed"))
        .and_then(|parsed| return parsed.get("info"))
        .and_then(|info| return info.get("decimals"))
        .and_then(serde_json::Value::as_u64)
        .and_then(u64_to_u8);
    let token_2022_metadata = extract_token_2022_metadata_from_mint_account_value(value);
    let mut resolved = ResolvedTokenMetadata {
        symbol: None,
        name: None,
        decimals,
        token_program: owner,
        is_quote_token: None,
    };
    if let Some(token_2022_metadata) = token_2022_metadata {
        resolved.merge(token_2022_metadata);
    }
    if resolved.symbol.is_none()
        && resolved.name.is_none()
        && resolved.decimals.is_none()
        && resolved.token_program.is_none()
    {
        return Ok(None);
    }
    return Ok(Some(resolved));
}

fn extract_token_2022_metadata_from_mint_account_value(
    value: &serde_json::Value,
) -> std::option::Option<ResolvedTokenMetadata> {
    let extensions_option = value
        .get("data")
        .and_then(|data| return data.get("parsed"))
        .and_then(|parsed| return parsed.get("info"))
        .and_then(|info| return info.get("extensions"))
        .and_then(serde_json::Value::as_array);
    let extensions = match extensions_option {
        Some(extensions) => extensions,
        None => return None,
    };
    for extension in extensions {
        let extension_name_option = extension.get("extension").and_then(serde_json::Value::as_str);
        let extension_name = match extension_name_option {
            Some(extension_name) => extension_name,
            None => continue,
        };
        if extension_name != "tokenMetadata" {
            continue;
        }
        let state_option = extension
            .get("state")
            .or_else(|| return extension.get("metadata"))
            .or_else(|| return extension.get("value"));
        let state = match state_option {
            Some(state) => state,
            None => continue,
        };
        let symbol = extract_string_by_candidate_keys(state, &["symbol", "tokenSymbol"]);
        let name = extract_string_by_candidate_keys(state, &["name", "tokenName"]);
        if symbol.is_none() && name.is_none() {
            continue;
        }
        return Some(ResolvedTokenMetadata {
            symbol,
            name,
            decimals: None,
            token_program: None,
            is_quote_token: None,
        });
    }
    return None;
}

async fn fetch_metaplex_metadata(
    http_pool: &crate::HttpEndpointPool,
    http_role: &str,
    mint: &str,
) -> Result<std::option::Option<ResolvedTokenMetadata>, crate::Error> {
    let metadata_address_result = derive_metaplex_metadata_address(mint);
    let metadata_address = match metadata_address_result {
        Ok(metadata_address) => metadata_address,
        Err(error) => return Err(error),
    };
    let config = serde_json::json!({
        "encoding": "base64",
        "commitment": "confirmed"
    });
    let account_result = http_pool
        .get_account_info_raw_for_role(http_role, metadata_address, Some(config))
        .await;
    let account_value = match account_result {
        Ok(account_value) => account_value,
        Err(error) => return Err(error),
    };
    let value_option = account_value.get("value");
    let value = match value_option {
        Some(value) => value,
        None => return Ok(None),
    };
    if value.is_null() {
        return Ok(None);
    }
    let data_text_option = extract_base64_account_data(value);
    let data_text = match data_text_option {
        Some(data_text) => data_text,
        None => return Ok(None),
    };
    let bytes_result = decode_base64_standard(data_text.as_str());
    let bytes = match bytes_result {
        Ok(bytes) => bytes,
        Err(error) => return Err(error),
    };
    return Ok(parse_metaplex_token_metadata_account(&bytes));
}

fn derive_metaplex_metadata_address(mint: &str) -> Result<std::string::String, crate::Error> {
    let program_id_result = <solana_sdk::pubkey::Pubkey as std::str::FromStr>::from_str(
        METAPLEX_TOKEN_METADATA_PROGRAM_ID,
    );
    let program_id = match program_id_result {
        Ok(program_id) => program_id,
        Err(error) => {
            return Err(crate::Error::Config(format!(
                "invalid metaplex metadata program id '{}': {}",
                METAPLEX_TOKEN_METADATA_PROGRAM_ID, error
            )));
        },
    };
    let mint_pubkey_result = <solana_sdk::pubkey::Pubkey as std::str::FromStr>::from_str(mint);
    let mint_pubkey = match mint_pubkey_result {
        Ok(mint_pubkey) => mint_pubkey,
        Err(error) => {
            return Err(crate::Error::Config(format!("invalid token mint '{}': {}", mint, error)));
        },
    };
    let (metadata_address, _) = solana_sdk::pubkey::Pubkey::find_program_address(
        &[b"metadata", program_id.as_ref(), mint_pubkey.as_ref()],
        &program_id,
    );
    return Ok(metadata_address.to_string());
}

fn extract_base64_account_data(
    value: &serde_json::Value,
) -> std::option::Option<std::string::String> {
    let data = match value.get("data") {
        Some(data) => data,
        None => return None,
    };
    if let Some(data_array) = data.as_array() {
        let first = match data_array.first() {
            Some(first) => first,
            None => return None,
        };
        return first.as_str().map(|value| return value.to_string());
    }
    return data.as_str().map(|value| return value.to_string());
}

fn parse_metaplex_token_metadata_account(
    bytes: &[u8],
) -> std::option::Option<ResolvedTokenMetadata> {
    if bytes.len() < 69 {
        return None;
    }
    let mut offset = 65usize;
    let name_option = read_borsh_string(bytes, &mut offset);
    let symbol_option = read_borsh_string(bytes, &mut offset);
    let _uri_option = read_borsh_string(bytes, &mut offset);
    let name = match name_option {
        Some(name) => clean_metaplex_string(name.as_str()),
        None => None,
    };
    let symbol = match symbol_option {
        Some(symbol) => clean_metaplex_string(symbol.as_str()),
        None => None,
    };
    if name.is_none() && symbol.is_none() {
        return None;
    }
    return Some(ResolvedTokenMetadata {
        symbol,
        name,
        decimals: None,
        token_program: None,
        is_quote_token: None,
    });
}

fn read_borsh_string(bytes: &[u8], offset: &mut usize) -> std::option::Option<std::string::String> {
    if bytes.len() < *offset + 4 {
        return None;
    }
    let length = u32::from_le_bytes([
        bytes[*offset],
        bytes[*offset + 1],
        bytes[*offset + 2],
        bytes[*offset + 3],
    ]) as usize;
    *offset += 4;
    if bytes.len() < *offset + length {
        return None;
    }
    let data = &bytes[*offset..*offset + length];
    *offset += length;
    let text_result = std::string::String::from_utf8(data.to_vec());
    match text_result {
        Ok(text) => return Some(text),
        Err(_) => return None,
    }
}

fn clean_metaplex_string(value: &str) -> std::option::Option<std::string::String> {
    let cleaned = value.trim_matches(char::from(0)).trim().to_string();
    if cleaned.is_empty() { return None } else { return Some(cleaned) }
}

fn decode_base64_standard(text: &str) -> Result<std::vec::Vec<u8>, crate::Error> {
    use base64::Engine;
    let decoded_result = base64::engine::general_purpose::STANDARD.decode(text);
    match decoded_result {
        Ok(decoded) => return Ok(decoded),
        Err(error) => {
            return Err(crate::Error::Json(format!(
                "cannot decode standard base64 payload: {}",
                error
            )));
        },
    }
}

fn extract_string_by_candidate_keys(
    payload: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<std::string::String> {
    for key in candidate_keys {
        let value_option = payload.get(*key);
        let value = match value_option {
            Some(value) => value,
            None => continue,
        };
        if let Some(text) = value.as_str() {
            let trimmed = text.trim();
            if !trimmed.is_empty() {
                return Some(trimmed.to_string());
            }
            continue;
        }
        if value.is_number() || value.is_boolean() {
            return Some(value.to_string());
        }
    }
    return None;
}

fn extract_u8_by_candidate_keys(
    payload: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<u8> {
    for key in candidate_keys {
        let value_option = payload.get(*key);
        let value = match value_option {
            Some(value) => value,
            None => continue,
        };
        if let Some(number) = value.as_u64() {
            let converted = u64_to_u8(number);
            if converted.is_some() {
                return converted;
            }
        }
        if let Some(text) = value.as_str() {
            let parsed_result = text.trim().parse::<u64>();
            let parsed = match parsed_result {
                Ok(parsed) => parsed,
                Err(_) => continue,
            };
            let converted = u64_to_u8(parsed);
            if converted.is_some() {
                return converted;
            }
        }
    }
    return None;
}

fn option_string_is_missing(value: std::option::Option<&str>) -> bool {
    match value {
        Some(value) => return value.trim().is_empty(),
        None => return true,
    }
}

fn u64_to_u8(value: u64) -> std::option::Option<u8> {
    let converted_result = u8::try_from(value);
    match converted_result {
        Ok(converted) => return Some(converted),
        Err(_) => return None,
    }
}

#[cfg(test)]
mod tests {
    fn build_test_metadata_account(name: &str, symbol: &str, uri: &str) -> std::vec::Vec<u8> {
        let mut bytes = std::vec::Vec::new();
        bytes.push(4_u8);
        bytes.extend_from_slice(&[1_u8; 32]);
        bytes.extend_from_slice(&[2_u8; 32]);
        push_borsh_string(&mut bytes, name);
        push_borsh_string(&mut bytes, symbol);
        push_borsh_string(&mut bytes, uri);
        return bytes;
    }

    fn push_borsh_string(bytes: &mut std::vec::Vec<u8>, value: &str) {
        let length = value.len() as u32;
        bytes.extend_from_slice(&length.to_le_bytes());
        bytes.extend_from_slice(value.as_bytes());
    }

    async fn make_database() -> std::sync::Arc<crate::Database> {
        let tempdir_result = tempfile::tempdir();
        let tempdir = match tempdir_result {
            Ok(tempdir) => tempdir,
            Err(error) => panic!("tempdir must succeed: {}", error),
        };
        let database_path = tempdir.path().join("token_metadata.sqlite3");
        let config = crate::DatabaseConfig {
            enabled: true,
            backend: crate::DatabaseBackend::Sqlite,
            sqlite: crate::SqliteDatabaseConfig {
                path: database_path.to_string_lossy().to_string(),
                create_if_missing: true,
                busy_timeout_ms: 5000,
                max_connections: 1,
                auto_initialize_schema: true,
                use_wal: true,
            },
        };
        let database_result = crate::Database::connect_and_initialize(&config).await;
        let database = match database_result {
            Ok(database) => database,
            Err(error) => panic!("database init must succeed: {}", error),
        };
        return std::sync::Arc::new(database);
    }

    #[test]
    fn local_metadata_resolves_wsol() {
        let mint = crate::WSOL_MINT_ID.to_string();
        let metadata_option = super::resolve_local_token_metadata(mint.as_str());
        let metadata = match metadata_option {
            Some(metadata) => metadata,
            None => panic!("WSOL metadata must resolve"),
        };
        assert_eq!(metadata.symbol.as_deref(), Some("WSOL"));
        assert_eq!(metadata.name.as_deref(), Some("Wrapped SOL"));
        assert_eq!(metadata.decimals, Some(9));
        assert_eq!(metadata.is_quote_token, Some(true));
    }

    #[test]
    fn local_metadata_resolves_known_ecosystem_tokens_without_forcing_quote_flag() {
        let cases = [
            (crate::JUP_MINT_ID, "JUP", "Jupiter", 6_u8),
            (crate::RAY_MINT_ID, "RAY", "Raydium", 6_u8),
            (crate::BONK_MINT_ID, "BONK", "Bonk", 5_u8),
        ];
        for (mint, expected_symbol, expected_name, expected_decimals) in cases {
            let metadata_option = super::resolve_local_token_metadata(mint);
            let metadata = match metadata_option {
                Some(metadata) => metadata,
                None => panic!("known ecosystem token metadata must resolve"),
            };
            assert_eq!(metadata.symbol.as_deref(), Some(expected_symbol));
            assert_eq!(metadata.name.as_deref(), Some(expected_name));
            assert_eq!(metadata.decimals, Some(expected_decimals));
            assert_eq!(metadata.is_quote_token, None);
        }
    }

    #[test]
    fn pump_fun_payload_metadata_extracts_name_and_symbol() {
        let payload = serde_json::json!({
            "mint": "PumpMint111",
            "symbol": "PUMPX",
            "name": "Pump Example",
            "uri": "https://example.invalid/pump.json"
        });
        let metadata_option = super::extract_pump_fun_metadata_from_payload(&payload);
        let metadata = match metadata_option {
            Some(metadata) => metadata,
            None => panic!("pump.fun metadata must parse"),
        };
        assert_eq!(metadata.symbol.as_deref(), Some("PUMPX"));
        assert_eq!(metadata.name.as_deref(), Some("Pump Example"));
    }

    #[test]
    fn token_2022_inline_metadata_extracts_name_and_symbol() {
        let account = serde_json::json!({
            "data": {
                "parsed": {
                    "info": {
                        "extensions": [
                            {
                                "extension": "tokenMetadata",
                                "state": {
                                    "symbol": "T22",
                                    "name": "Token 2022 Example"
                                }
                            }
                        ]
                    }
                }
            }
        });
        let metadata_option = super::extract_token_2022_metadata_from_mint_account_value(&account);
        let metadata = match metadata_option {
            Some(metadata) => metadata,
            None => panic!("token-2022 metadata must parse"),
        };
        assert_eq!(metadata.symbol.as_deref(), Some("T22"));
        assert_eq!(metadata.name.as_deref(), Some("Token 2022 Example"));
    }

    #[test]
    fn base64_decoder_decodes_standard_payload() {
        let decoded_result = super::decode_base64_standard("AQIDBA==");
        let decoded = match decoded_result {
            Ok(decoded) => decoded,
            Err(error) => panic!("base64 decode must succeed: {}", error),
        };
        assert_eq!(decoded, vec![1_u8, 2_u8, 3_u8, 4_u8]);
    }

    #[test]
    fn metaplex_parser_extracts_name_and_symbol() {
        let bytes = build_test_metadata_account(
            "Example Token\0\0\0",
            "EXM\0\0",
            "https://example.invalid/token.json",
        );
        let metadata_option = super::parse_metaplex_token_metadata_account(&bytes);
        let metadata = match metadata_option {
            Some(metadata) => metadata,
            None => panic!("metadata must parse"),
        };
        assert_eq!(metadata.name.as_deref(), Some("Example Token"));
        assert_eq!(metadata.symbol.as_deref(), Some("EXM"));
    }

    #[tokio::test]
    async fn local_backfill_updates_wsol_without_http() {
        let database = make_database().await;
        let token = crate::TokenDto::new(
            crate::WSOL_MINT_ID.to_string(),
            None,
            None,
            None,
            crate::SPL_TOKEN_PROGRAM_ID.to_string(),
            false,
        );
        let upsert_result = crate::query_tokens_upsert(database.as_ref(), &token).await;
        if let Err(error) = upsert_result {
            panic!("token upsert must succeed: {}", error);
        }
        let service = crate::TokenMetadataBackfillService::new_local(database.clone());
        let result = service.backfill_missing_token_metadata(Some(10)).await;
        let result = match result {
            Ok(result) => result,
            Err(error) => panic!("metadata backfill must succeed: {}", error),
        };
        assert_eq!(result.updated_token_count, 1);
        let fetched_result = crate::query_tokens_get_by_mint(
            database.as_ref(),
            crate::WSOL_MINT_ID.to_string().as_str(),
        )
        .await;
        let fetched_option = match fetched_result {
            Ok(fetched_option) => fetched_option,
            Err(error) => panic!("token fetch must succeed: {}", error),
        };
        let fetched = match fetched_option {
            Some(fetched) => fetched,
            None => panic!("token must exist"),
        };
        assert_eq!(fetched.symbol.as_deref(), Some("WSOL"));
        assert_eq!(fetched.name.as_deref(), Some("Wrapped SOL"));
        assert_eq!(fetched.decimals, Some(9));
        assert!(fetched.is_quote_token);
    }

    #[tokio::test]
    async fn local_backfill_updates_stable_quotes_without_http() {
        let database = make_database().await;
        let usdc = crate::TokenDto::new(
            crate::USDC_MINT_ID.to_string(),
            None,
            None,
            None,
            crate::SPL_TOKEN_PROGRAM_ID.to_string(),
            false,
        );
        let usdt = crate::TokenDto::new(
            crate::USDT_MINT_ID.to_string(),
            None,
            None,
            None,
            crate::SPL_TOKEN_PROGRAM_ID.to_string(),
            false,
        );
        let usdc_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdc).await;
        if let Err(error) = usdc_upsert_result {
            panic!("usdc token upsert must succeed: {}", error);
        }
        let usdt_upsert_result = crate::query_tokens_upsert(database.as_ref(), &usdt).await;
        if let Err(error) = usdt_upsert_result {
            panic!("usdt token upsert must succeed: {}", error);
        }
        let service = crate::TokenMetadataBackfillService::new_local(database.clone());
        let result = service.backfill_missing_token_metadata(Some(10)).await;
        let result = match result {
            Ok(result) => result,
            Err(error) => panic!("metadata backfill must succeed: {}", error),
        };
        assert_eq!(result.updated_token_count, 2);

        let usdc_result =
            crate::query_tokens_get_by_mint(database.as_ref(), crate::USDC_MINT_ID).await;
        let usdc_option = match usdc_result {
            Ok(usdc_option) => usdc_option,
            Err(error) => panic!("usdc token fetch must succeed: {}", error),
        };
        let usdc = match usdc_option {
            Some(usdc) => usdc,
            None => panic!("usdc token must exist"),
        };
        assert_eq!(usdc.symbol.as_deref(), Some("USDC"));
        assert_eq!(usdc.name.as_deref(), Some("USD Coin"));
        assert_eq!(usdc.decimals, Some(6));
        assert!(usdc.is_quote_token);

        let usdt_result =
            crate::query_tokens_get_by_mint(database.as_ref(), crate::USDT_MINT_ID).await;
        let usdt_option = match usdt_result {
            Ok(usdt_option) => usdt_option,
            Err(error) => panic!("usdt token fetch must succeed: {}", error),
        };
        let usdt = match usdt_option {
            Some(usdt) => usdt,
            None => panic!("usdt token must exist"),
        };
        assert_eq!(usdt.symbol.as_deref(), Some("USDT"));
        assert_eq!(usdt.name.as_deref(), Some("Tether USD"));
        assert_eq!(usdt.decimals, Some(6));
        assert!(usdt.is_quote_token);
    }

    #[tokio::test]
    async fn local_backfill_updates_known_ecosystem_tokens_without_http() {
        let database = make_database().await;
        let token_mints = [crate::JUP_MINT_ID, crate::RAY_MINT_ID, crate::BONK_MINT_ID];
        for mint in token_mints {
            let token = crate::TokenDto::new(
                mint.to_string(),
                None,
                None,
                None,
                crate::SPL_TOKEN_PROGRAM_ID.to_string(),
                false,
            );
            let upsert_result = crate::query_tokens_upsert(database.as_ref(), &token).await;
            if let Err(error) = upsert_result {
                panic!("known token upsert must succeed: {}", error);
            }
        }
        let service = crate::TokenMetadataBackfillService::new_local(database.clone());
        let result = service.backfill_missing_token_metadata(Some(10)).await;
        let result = match result {
            Ok(result) => result,
            Err(error) => panic!("metadata backfill must succeed: {}", error),
        };
        assert_eq!(result.updated_token_count, 3);

        let jup_result =
            crate::query_tokens_get_by_mint(database.as_ref(), crate::JUP_MINT_ID).await;
        let jup_option = match jup_result {
            Ok(jup_option) => jup_option,
            Err(error) => panic!("jup token fetch must succeed: {}", error),
        };
        let jup = match jup_option {
            Some(jup) => jup,
            None => panic!("jup token must exist"),
        };
        assert_eq!(jup.symbol.as_deref(), Some("JUP"));
        assert_eq!(jup.name.as_deref(), Some("Jupiter"));
        assert_eq!(jup.decimals, Some(6));
        assert!(!jup.is_quote_token);

        let ray_result =
            crate::query_tokens_get_by_mint(database.as_ref(), crate::RAY_MINT_ID).await;
        let ray_option = match ray_result {
            Ok(ray_option) => ray_option,
            Err(error) => panic!("ray token fetch must succeed: {}", error),
        };
        let ray = match ray_option {
            Some(ray) => ray,
            None => panic!("ray token must exist"),
        };
        assert_eq!(ray.symbol.as_deref(), Some("RAY"));
        assert_eq!(ray.name.as_deref(), Some("Raydium"));
        assert_eq!(ray.decimals, Some(6));
        assert!(!ray.is_quote_token);

        let bonk_result =
            crate::query_tokens_get_by_mint(database.as_ref(), crate::BONK_MINT_ID).await;
        let bonk_option = match bonk_result {
            Ok(bonk_option) => bonk_option,
            Err(error) => panic!("bonk token fetch must succeed: {}", error),
        };
        let bonk = match bonk_option {
            Some(bonk) => bonk,
            None => panic!("bonk token must exist"),
        };
        assert_eq!(bonk.symbol.as_deref(), Some("BONK"));
        assert_eq!(bonk.name.as_deref(), Some("Bonk"));
        assert_eq!(bonk.decimals, Some(5));
        assert!(!bonk.is_quote_token);
    }

    #[tokio::test]
    async fn local_backfill_does_not_overwrite_existing_display_metadata() {
        let database = make_database().await;
        let token = crate::TokenDto::new(
            crate::WSOL_MINT_ID.to_string(),
            Some("SOL".to_string()),
            Some("Custom Sol".to_string()),
            None,
            crate::SPL_TOKEN_PROGRAM_ID.to_string(),
            false,
        );
        let upsert_result = crate::query_tokens_upsert(database.as_ref(), &token).await;
        if let Err(error) = upsert_result {
            panic!("token upsert must succeed: {}", error);
        }
        let service = crate::TokenMetadataBackfillService::new_local(database.clone());
        let result = service
            .backfill_token_metadata_by_mint(crate::WSOL_MINT_ID.to_string().as_str())
            .await;
        if let Err(error) = result {
            panic!("metadata backfill must succeed: {}", error);
        }
        let fetched_result = crate::query_tokens_get_by_mint(
            database.as_ref(),
            crate::WSOL_MINT_ID.to_string().as_str(),
        )
        .await;
        let fetched_option = match fetched_result {
            Ok(fetched_option) => fetched_option,
            Err(error) => panic!("token fetch must succeed: {}", error),
        };
        let fetched = match fetched_option {
            Some(fetched) => fetched,
            None => panic!("token must exist"),
        };
        assert_eq!(fetched.symbol.as_deref(), Some("SOL"));
        assert_eq!(fetched.name.as_deref(), Some("Custom Sol"));
        assert_eq!(fetched.decimals, Some(9));
        assert!(fetched.is_quote_token);
    }
}
