/// Launch Platform Detection and Scoring Profiles
///
/// Different token launch platforms have different trust models and mechanics.
/// A token on a Pump.fun bonding curve has 100% of supply in the protocol
/// contract — this looks like extreme concentration but is not a rug risk.
/// Platform-aware scoring exempts checks that would produce false positives
/// for known, audited protocols while keeping all checks for unknown deployers.
use birdeye_client::defi::types::BirdeyeTokenSecurity;

/// Known Solana token launch platforms
#[derive(Debug, Clone, PartialEq)]
pub enum LaunchPlatform {
    /// Pump.fun — the largest Solana meme token launchpad.
    /// Detection: mint address ends with "pump" (enforced vanity suffix).
    PumpFun,

    /// Bonk Launchpad — community-backed launchpad by the BONK team.
    /// Detection: creator_owner matches known program address(es).
    BonkLaunchpad,

    /// Moonshot — DexScreener's token launchpad.
    /// Detection: creator_owner matches known program address(es).
    Moonshot,

    /// LetsBonk — competing Pump.fun-style launchpad.
    /// Detection: creator_owner matches known program address(es).
    LetsBonk,

    /// Unknown or custom deployer — strictest rules applied.
    Unknown,
}

impl LaunchPlatform {
    pub fn display_name(&self) -> &'static str {
        match self {
            LaunchPlatform::PumpFun => "Pump.fun",
            LaunchPlatform::BonkLaunchpad => "Bonk Launchpad",
            LaunchPlatform::Moonshot => "Moonshot (DexScreener)",
            LaunchPlatform::LetsBonk => "LetsBonk",
            LaunchPlatform::Unknown => "Unknown",
        }
    }
}

/// Trust level classification for a launch platform
#[derive(Debug, Clone, PartialEq)]
pub enum TrustLevel {
    /// Well-established, widely-used, and audited protocol
    Audited,
    /// Community-backed protocol with a track record
    Community,
    /// Custom deployer or unknown origin — apply strictest rules
    Unknown,
}

impl TrustLevel {
    pub fn display_name(&self) -> &'static str {
        match self {
            TrustLevel::Audited => "Audited",
            TrustLevel::Community => "Community",
            TrustLevel::Unknown => "Unknown",
        }
    }
}

/// Platform-specific scoring rules and context.
///
/// Each known launcher gets a profile that tells the scoring engine which
/// checks to skip or adjust and what informational notes to surface.
#[derive(Debug, Clone)]
pub struct PlatformProfile {
    pub platform: LaunchPlatform,
    pub trust_level: TrustLevel,

    /// Skip holder concentration hard stops.
    ///
    /// On bonding curve platforms the protocol contract holds the unsold
    /// supply — this looks like extreme concentration but is not a rug risk.
    /// The real distribution only becomes meaningful after graduation.
    pub skip_holder_hard_stops: bool,

    /// Override the holder distribution score with a neutral value (0–20).
    ///
    /// When supply is protocol-controlled rather than freely distributed,
    /// the standard concentration scoring doesn't reflect actual risk.
    pub holder_score_override: Option<f64>,

    /// Informational notes surfaced as positive signals in the output
    pub notes: Vec<&'static str>,
}

impl PlatformProfile {
    fn pumpfun() -> Self {
        Self {
            platform: LaunchPlatform::PumpFun,
            trust_level: TrustLevel::Audited,
            skip_holder_hard_stops: true,
            holder_score_override: Some(10.0), // neutral 10/20 — bonding curve supply
            notes: vec![
                "✓ Pump.fun: audited launchpad with programmatic bonding curve",
                "ℹ Holder concentration reflects bonding curve contract supply — not whale risk",
            ],
        }
    }

    fn bonk_launchpad() -> Self {
        Self {
            platform: LaunchPlatform::BonkLaunchpad,
            trust_level: TrustLevel::Community,
            skip_holder_hard_stops: false,
            holder_score_override: None, // neutral 10/20 — bonding curve supply
            notes: vec![
                "✓ Bonk Launchpad: community-backed protocol with bonding curve mechanics",
                "ℹ Holder concentration reflects bonding curve contract supply — not whale risk",
            ],
        }
    }

    fn moonshot() -> Self {
        Self {
            platform: LaunchPlatform::Moonshot,
            trust_level: TrustLevel::Community,
            skip_holder_hard_stops: false,
            holder_score_override: None, // neutral 10/20 — bonding curve supply
            notes: vec![
                "✓ Moonshot (DexScreener): established launchpad with bonding curve mechanics",
                "ℹ Holder concentration reflects bonding curve contract supply — not whale risk",
            ],
        }
    }

    fn letsbonk() -> Self {
        Self {
            platform: LaunchPlatform::LetsBonk,
            trust_level: TrustLevel::Community,
            skip_holder_hard_stops: true,
            holder_score_override: Some(10.0), // neutral 10/20 — bonding curve supply
            notes: vec![
                "✓ LetsBonk: community launchpad with bonding curve mechanics",
                "ℹ Holder concentration reflects bonding curve contract supply — not whale risk",
            ],
        }
    }

    /// Profile for unknown/custom deployers — strictest rules applied
    pub fn unknown() -> Self {
        Self {
            platform: LaunchPlatform::Unknown,
            trust_level: TrustLevel::Unknown,
            skip_holder_hard_stops: false,
            holder_score_override: None,
            notes: vec![],
        }
    }
}

// ── Known platform program addresses ─────────────────────────────────────────

/// Bonk Launchpad known program address(es)
/// TODO: Add confirmed address(es) from https://launch.bonk.gg
const BONK_LAUNCHPAD_PROGRAMS: &[&str] = &[];

/// Moonshot (DexScreener) known program address(es)
/// TODO: Add confirmed address(es) from https://dexscreener.com/moonshot
const MOONSHOT_PROGRAMS: &[&str] = &[];

/// LetsBonk known program address(es)
/// TODO: Add confirmed address(es)
const LETSBONK_PROGRAMS: &[&str] = &[];

// ── Detection ────────────────────────────────────────────────────────────────

/// Detect which launch platform created this token and return its scoring profile.
///
/// Detection priority (first match wins):
/// 1. Pump.fun — mint ends with `"pump"` (vanity suffix enforced by the protocol)
/// 2. Bonk Launchpad — creator_owner matches known program addresses
/// 3. Moonshot — creator_owner matches known program addresses
/// 4. LetsBonk — creator_owner matches known program addresses
/// 5. Unknown — fallback, strictest rules applied
pub fn detect_platform(mint: &str, security: &BirdeyeTokenSecurity) -> PlatformProfile {
    let creator = security.creator_address.as_deref().unwrap_or("");
    let creator_owner = security.creator_owner_address.as_deref().unwrap_or("");

    // Pump.fun: all mints have an enforced "pump" vanity suffix
    if mint.ends_with("pump") {
        return PlatformProfile::pumpfun();
    }

    // Address-based detection — only active once program addresses are populated
    if !BONK_LAUNCHPAD_PROGRAMS.is_empty()
        && (BONK_LAUNCHPAD_PROGRAMS.contains(&creator)
            || BONK_LAUNCHPAD_PROGRAMS.contains(&creator_owner))
    {
        return PlatformProfile::bonk_launchpad();
    }

    if !MOONSHOT_PROGRAMS.is_empty()
        && (MOONSHOT_PROGRAMS.contains(&creator) || MOONSHOT_PROGRAMS.contains(&creator_owner))
    {
        return PlatformProfile::moonshot();
    }

    if !LETSBONK_PROGRAMS.is_empty()
        && (LETSBONK_PROGRAMS.contains(&creator) || LETSBONK_PROGRAMS.contains(&creator_owner))
    {
        return PlatformProfile::letsbonk();
    }

    PlatformProfile::unknown()
}

#[cfg(test)]
mod tests {
    use super::*;

    // ── detect_platform ───────────────────────────────────────────────────────

    #[test]
    fn test_pumpfun_detected_by_pump_suffix() {
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("J1YNFvLWJ7EQ3vrv2KPTQsEMNo3FjsQxZoaUz4y1pump", &security);
        assert_eq!(profile.platform, LaunchPlatform::PumpFun);
        assert_eq!(profile.trust_level, TrustLevel::Audited);
    }

    #[test]
    fn test_pumpfun_short_mint_with_pump_suffix() {
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("pump", &security);
        assert_eq!(profile.platform, LaunchPlatform::PumpFun);
    }

    #[test]
    fn test_pumpfun_suffix_is_case_sensitive() {
        // Pump.fun uses lowercase "pump" — uppercase must not match
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("TokenAddressEndingWithPUMP", &security);
        assert_eq!(profile.platform, LaunchPlatform::Unknown);
    }

    #[test]
    fn test_pump_in_middle_of_mint_is_not_pumpfun() {
        // "pump" must appear at the end, not anywhere in the middle
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("pumpTokenSomethingElse", &security);
        assert_eq!(profile.platform, LaunchPlatform::Unknown);
    }

    #[test]
    fn test_unknown_platform_fallback() {
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("So11111111111111111111111111111111111111112", &security);
        assert_eq!(profile.platform, LaunchPlatform::Unknown);
        assert_eq!(profile.trust_level, TrustLevel::Unknown);
    }

    // ── PlatformProfile properties ────────────────────────────────────────────

    #[test]
    fn test_pumpfun_profile_skips_holder_hard_stops() {
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("anyaddressendingwithpump", &security);
        assert!(profile.skip_holder_hard_stops);
        assert!(profile.holder_score_override.is_some());
    }

    #[test]
    fn test_unknown_profile_applies_all_checks() {
        let profile = PlatformProfile::unknown();
        assert!(!profile.skip_holder_hard_stops);
        assert!(profile.holder_score_override.is_none());
        assert!(profile.notes.is_empty());
    }

    #[test]
    fn test_pumpfun_profile_has_informational_notes() {
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("mintpump", &security);
        assert!(!profile.notes.is_empty());
    }

    #[test]
    fn test_pumpfun_holder_score_override_is_neutral() {
        // 10/20 = 50% — neutral, not penalizing but not rewarding
        let security = BirdeyeTokenSecurity::default();
        let profile = detect_platform("mintpump", &security);
        assert_eq!(profile.holder_score_override, Some(10.0));
    }

    // ── display names ─────────────────────────────────────────────────────────

    #[test]
    fn test_platform_display_names() {
        assert_eq!(LaunchPlatform::PumpFun.display_name(), "Pump.fun");
        assert_eq!(
            LaunchPlatform::BonkLaunchpad.display_name(),
            "Bonk Launchpad"
        );
        assert_eq!(
            LaunchPlatform::Moonshot.display_name(),
            "Moonshot (DexScreener)"
        );
        assert_eq!(LaunchPlatform::LetsBonk.display_name(), "LetsBonk");
        assert_eq!(LaunchPlatform::Unknown.display_name(), "Unknown");
    }

    #[test]
    fn test_trust_level_display_names() {
        assert_eq!(TrustLevel::Audited.display_name(), "Audited");
        assert_eq!(TrustLevel::Community.display_name(), "Community");
        assert_eq!(TrustLevel::Unknown.display_name(), "Unknown");
    }
}
