//! Fetch and evaluate a token report summary from rugcheck.xyz
//!
//! Uses the /v1/tokens/{id}/report/summary endpoint to check LP lock status
//! and identified risks without requiring authentication.
//!
//! Usage:
//! ```bash
//! cargo run --example rugcheck_report -- <TOKEN_ADDRESS> [--verbose]
//! # e.g.
//! cargo run --example rugcheck_report -- So11111111111111111111111111111111111111112 --verbose
//! ```

use anyhow::{Context, Result};
use colored::*;
use serde::Deserialize;
use serde_json;

const RUGCHECK_URL: &str = "https://api.rugcheck.xyz/v1/tokens/{}/report/summary";
const MIN_LP_LOCKED_PCT: f64 = 75.0;
const MAX_RISK_SCORE: u64 = 501;

// ── API response types ────────────────────────────────────────────────────────

#[derive(Debug, Deserialize)]
struct TokenCheckSummary {
    score: Option<u64>,
    #[serde(rename = "score_normalised")]
    score_normalised: Option<u64>,
    #[serde(rename = "lpLockedPct")]
    lp_locked_pct: Option<f64>,
    #[serde(rename = "tokenType")]
    token_type: Option<String>,
    #[serde(rename = "tokenProgram")]
    token_program: Option<String>,
    #[serde(default)]
    risks: Vec<Risk>,
}

#[derive(Debug, Deserialize)]
struct Risk {
    name: Option<String>,
    level: Option<String>,
    description: Option<String>,
    score: Option<i64>,
    value: Option<String>,
}

// ── Fetch ─────────────────────────────────────────────────────────────────────

async fn fetch_raw(client: &reqwest::Client, token_address: &str) -> Result<String> {
    let url = RUGCHECK_URL.replace("{}", token_address);
    client
        .get(&url)
        .send()
        .await
        .context("HTTP request to rugcheck.xyz failed")?
        .error_for_status()
        .context("rugcheck.xyz returned an error status")?
        .text()
        .await
        .context("Failed to read rugcheck.xyz response body")
}

// ── Entry point ───────────────────────────────────────────────────────────────

#[tokio::main]
async fn main() -> Result<()> {
    let mut token_address = "So11111111111111111111111111111111111111112".to_string();
    let mut verbose = false;

    for arg in std::env::args().skip(1) {
        match arg.as_str() {
            "--verbose" | "-v" => verbose = true,
            _ => token_address = arg,
        }
    }

    println!(
        "{}",
        "=== Rugcheck.xyz Token Report Summary ==="
            .bright_cyan()
            .bold()
    );
    println!("Token: {}\n", token_address.bright_white());

    let client = reqwest::Client::new();
    let raw = fetch_raw(&client, &token_address).await?;

    if verbose {
        let pretty: serde_json::Value =
            serde_json::from_str(&raw).context("Response is not valid JSON")?;
        println!(
            "{}\n{}\n{}",
            "=== RAW API RESPONSE ===".bright_cyan().bold(),
            serde_json::to_string_pretty(&pretty)?,
            "=== END RESPONSE ===".bright_cyan().bold(),
        );
        println!();
    }

    let summary: TokenCheckSummary =
        serde_json::from_str(&raw).context("Failed to deserialize rugcheck.xyz response")?;

    // ── Token info ────────────────────────────────────────────────────────────
    if let Some(token_type) = &summary.token_type {
        println!("{} {}", "Type:".bold(), token_type.bright_white());
    }
    if let Some(program) = &summary.token_program {
        println!("{} {}", "Program:".bold(), program.dimmed());
    }
    println!();

    // ── Risk scores ───────────────────────────────────────────────────────────
    if let Some(score) = summary.score {
        let score_str = format!("{}", score);
        let colored_score = if score < 200 {
            score_str.green()
        } else if score < MAX_RISK_SCORE {
            score_str.yellow()
        } else {
            score_str.bright_red().bold()
        };
        println!("{} {}", "Risk Score:".bold(), colored_score);
    }
    if let Some(norm) = summary.score_normalised {
        println!(
            "{} {}",
            "Normalised Score:".bold(),
            norm.to_string().dimmed()
        );
    }
    println!();

    // ── LP lock check ─────────────────────────────────────────────────────────
    let lp_locked_pct = summary.lp_locked_pct.unwrap_or(0.0);
    let lp_ok = lp_locked_pct >= MIN_LP_LOCKED_PCT;
    let lp_str = format!("{:.2}%", lp_locked_pct);
    if lp_ok {
        println!("{} LP locked: {}", "✓".green(), lp_str.green());
    } else {
        println!(
            "{} LP locked: {} (minimum: {:.0}%)",
            "✗".red(),
            lp_str.red(),
            MIN_LP_LOCKED_PCT
        );
    }
    println!();

    // ── Risks ─────────────────────────────────────────────────────────────────
    if summary.risks.is_empty() {
        println!("{}", "No risks identified.".green());
    } else {
        println!("{}", "Identified Risks:".bold());
        for risk in &summary.risks {
            let name = risk.name.as_deref().unwrap_or("Unknown");
            let level = risk.level.as_deref().unwrap_or("info");
            let desc = risk.description.as_deref().unwrap_or("");
            let value = risk.value.as_deref().unwrap_or("");
            let score = risk.score.unwrap_or(0);

            let level_colored = match level.to_uppercase().as_str() {
                "DANGER" => format!("[{}]", level).bright_red().bold(),
                "WARN" | "WARNING" => format!("[{}]", level).yellow().bold(),
                _ => format!("[{}]", level).dimmed().bold(),
            };

            let name_colored = match level.to_uppercase().as_str() {
                "DANGER" => name.bright_red().to_string(),
                "WARN" | "WARNING" => name.yellow().to_string(),
                _ => name.normal().to_string(),
            };

            print!("  {} {} (score: {})", level_colored, name_colored, score);
            if !value.is_empty() {
                print!(" — {}", value.dimmed());
            }
            println!();
            if !desc.is_empty() {
                println!("     {}", desc.dimmed());
            }
        }
    }

    // ── Summary ───────────────────────────────────────────────────────────────
    println!();
    let risk_score_ok = summary.score.map_or(false, |s| s < MAX_RISK_SCORE);
    let has_danger = summary.risks.iter().any(|r| {
        r.level
            .as_deref()
            .map(|l| l.to_uppercase() == "DANGER")
            .unwrap_or(false)
    });

    if lp_ok && risk_score_ok && !has_danger {
        println!(
            "{}",
            "RESULT: Token passed all checks.".bright_green().bold()
        );
    } else {
        println!(
            "{}",
            "RESULT: Token failed one or more checks."
                .bright_red()
                .bold()
        );
    }

    Ok(())
}
