//! Pharma data ingestion CLI for StemeDB.
//!
//! This binary orchestrates extraction from FDA drug labels and ingestion
//! into StemeDB. It can optionally add mock conflict data to demonstrate
//! the SkepticLens conflict detection.
//!
//! # Usage
//!
//! ```bash
//! # Basic FDA ingestion
//! cargo run --bin pharma-ingest -- --drugs semaglutide,tirzepatide
//!
//! # With mock conflicts for demo
//! cargo run --bin pharma-ingest -- --drugs semaglutide --with-conflicts
//!
//! # Query skeptic after ingestion
//! curl "http://localhost:18180/v1/skeptic?subject=Semaglutide:Type2Diabetes&predicate=hba1c_change_percent"
//! ```

// CLI binary - println is the appropriate way to produce user-facing output
#![allow(clippy::print_stdout)]

use clap::Parser;
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
use stemedb_core::types::{ObjectValue, SourceClass};
use stemedb_ontology::{
    client::StemeClient,
    pharma::extractors::{FdaLabelExtractor, MedicalClaim, MedicalExtractor, SourceInput},
};
use tracing::{error, info, warn};

/// Pharma data ingestion CLI for StemeDB.
///
/// Extracts claims from FDA drug labels and ingests them into StemeDB.
/// Supports mock conflict data for demonstrating SkepticLens.
#[derive(Parser, Debug)]
#[command(name = "pharma-ingest")]
#[command(version, about, long_about = None)]
struct Args {
    /// StemeDB API URL
    #[arg(long, env = "STEMEDB_URL", default_value = "http://localhost:18180")]
    stemedb_url: String,

    /// Drugs to ingest (comma-separated)
    #[arg(long, default_value = "semaglutide")]
    drugs: String,

    /// Include mock conflict data for demo.
    ///
    /// Adds synthetic clinical trial data that disagrees with FDA label
    /// values to demonstrate the SkepticLens conflict detection.
    #[arg(long)]
    with_conflicts: bool,

    /// Skip FDA API calls (only use mock data).
    ///
    /// Useful for testing without hitting the live FDA API.
    #[arg(long)]
    mock_only: bool,

    /// Enable verbose output.
    #[arg(long, short)]
    verbose: bool,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();

    // Initialize tracing
    let filter = if args.verbose { "debug" } else { "info" };
    tracing_subscriber::fmt().with_env_filter(filter).init();

    info!("Starting pharma-ingest");
    info!(stemedb_url = %args.stemedb_url, drugs = %args.drugs, with_conflicts = args.with_conflicts);

    let client = StemeClient::new(&args.stemedb_url);

    // Check server health
    if !client.health_check().await {
        error!("StemeDB server not reachable at {}", args.stemedb_url);
        println!("\nError: StemeDB server not reachable at {}", args.stemedb_url);
        println!("Start the server with: cargo run --bin stemedb-api");
        return Ok(());
    }

    // Generate signing key (in production, load from secure storage)
    let signing_key = SigningKey::generate(&mut OsRng);
    let agent_id = signing_key.verifying_key().to_bytes();
    let hlc = uhlc::HLCBuilder::new().build();

    println!("\n=== Pharma Ingest ===");
    println!("Agent ID: {}...", &hex::encode(agent_id)[..16]);
    println!("Server: {}", args.stemedb_url);
    println!();

    let mut total_ingested = 0usize;
    let mut total_errors = 0usize;

    // Extract and ingest FDA claims (unless mock-only mode)
    if !args.mock_only {
        let extractor = FdaLabelExtractor::new();

        for drug in args.drugs.split(',') {
            let drug = drug.trim();
            println!("--- Ingesting {} (FDA) ---", drug);

            match extractor.extract(&SourceInput::DrugName(drug.to_string())).await {
                Ok(claims) => {
                    info!(drug = drug, claims_count = claims.len(), "Extracted FDA claims");
                    for claim in claims {
                        let assertion = claim.to_assertion(&signing_key, agent_id, &hlc);
                        match client.assert(&assertion).await {
                            Ok(hash) => {
                                total_ingested += 1;
                                println!(
                                    "  [Regulatory] {} = {:?} -> {}...",
                                    claim.predicate,
                                    format_value(&claim.value),
                                    &hash[..16]
                                );
                            }
                            Err(e) => {
                                total_errors += 1;
                                warn!(error = %e, predicate = %claim.predicate, "Failed to ingest");
                                println!("  [ERROR] {} -> {}", claim.predicate, e);
                            }
                        }
                    }
                }
                Err(e) => {
                    warn!(drug = drug, error = %e, "Failed to extract FDA claims");
                    println!("  [ERROR] Failed to extract: {}", e);
                }
            }
            println!();
        }
    }

    // Add mock conflicts if requested
    if args.with_conflicts {
        println!("--- Adding Mock Conflicts ---");
        let (ingested, errors) = ingest_mock_conflicts(&client, &signing_key, agent_id, &hlc).await;
        total_ingested += ingested;
        total_errors += errors;
        println!();
    }

    // Summary
    println!("=== Summary ===");
    println!("Total ingested: {}", total_ingested);
    println!("Total errors: {}", total_errors);

    // Demo skeptic query
    if total_ingested > 0 && args.with_conflicts {
        println!("\n=== Skeptic Query Demo ===");
        demo_skeptic_query(&client).await;
    }

    Ok(())
}

/// Ingest mock conflict data to demonstrate SkepticLens.
///
/// Creates synthetic clinical trial data that disagrees with FDA label values.
async fn ingest_mock_conflicts(
    client: &StemeClient,
    signing_key: &SigningKey,
    agent_id: [u8; 32],
    hlc: &uhlc::HLC,
) -> (usize, usize) {
    let mock_claims = vec![
        // Mock Trial A: Higher HbA1c reduction than FDA label (-1.5% vs ~-1.2%)
        MedicalClaim::new(
            "Semaglutide:Type2Diabetes",
            "hba1c_change_percent",
            ObjectValue::Number(-1.5),
        )
        .with_confidence(0.90)
        .with_source_class(SourceClass::Clinical)
        .with_source_url("https://clinicaltrials.gov/study/NCT_MOCK_001")
        .with_source_section("Primary Endpoint")
        .with_quote("Mean HbA1c change from baseline: -1.5% (95% CI: -1.7 to -1.3)"),
        // Mock Trial B: Lower HbA1c reduction (-0.8%)
        MedicalClaim::new(
            "Semaglutide:Type2Diabetes",
            "hba1c_change_percent",
            ObjectValue::Number(-0.8),
        )
        .with_confidence(0.85)
        .with_source_class(SourceClass::Clinical)
        .with_source_url("https://clinicaltrials.gov/study/NCT_MOCK_002")
        .with_source_section("Secondary Endpoint")
        .with_quote("Mean HbA1c change from baseline: -0.8% in intention-to-treat population"),
        // Anecdotal: Patient report of minimal effect (Tier 5 - low weight)
        MedicalClaim::new(
            "Semaglutide:Type2Diabetes",
            "hba1c_change_percent",
            ObjectValue::Number(-0.3),
        )
        .with_confidence(0.50)
        .with_source_class(SourceClass::Anecdotal)
        .with_source_url("https://reddit.com/r/diabetes/comments/mock_thread")
        .with_source_section("User Report")
        .with_quote("My A1C only dropped by 0.3 points after 3 months"),
        // Mock Trial: Different nausea rate than FDA label
        MedicalClaim::new("Semaglutide", "nausea_rate", ObjectValue::Number(0.25))
            .with_confidence(0.88)
            .with_source_class(SourceClass::Clinical)
            .with_source_url("https://clinicaltrials.gov/study/NCT_MOCK_003")
            .with_source_section("Adverse Events")
            .with_quote("Nausea reported in 25% of participants (vs 12% placebo)"),
        // Mock Observational: Real-world nausea rate
        MedicalClaim::new("Semaglutide", "nausea_rate", ObjectValue::Number(0.38))
            .with_confidence(0.75)
            .with_source_class(SourceClass::Observational)
            .with_source_url("https://example.com/real-world-evidence/semaglutide")
            .with_source_section("Safety Analysis")
            .with_quote("Nausea occurred in 38% of patients in real-world setting"),
        // Weight loss disagreement
        MedicalClaim::new(
            "Semaglutide:ChronicWeightManagement",
            "weight_loss_percent",
            ObjectValue::Number(-18.5),
        )
        .with_confidence(0.92)
        .with_source_class(SourceClass::Clinical)
        .with_source_url("https://clinicaltrials.gov/study/NCT_MOCK_004")
        .with_source_section("Primary Endpoint")
        .with_quote("Mean weight loss: -18.5% at 68 weeks"),
        MedicalClaim::new(
            "Semaglutide:ChronicWeightManagement",
            "weight_loss_percent",
            ObjectValue::Number(-12.0),
        )
        .with_confidence(0.70)
        .with_source_class(SourceClass::Community)
        .with_source_url("https://patientslikeme.com/treatments/semaglutide/reviews")
        .with_source_section("Patient Reports Aggregated")
        .with_quote("Average reported weight loss: 12% among community members"),
    ];

    let mut ingested = 0usize;
    let mut errors = 0usize;

    for claim in mock_claims {
        let tier = format!("{:?}", claim.source_class);
        let assertion = claim.to_assertion(signing_key, agent_id, hlc);

        match client.assert(&assertion).await {
            Ok(hash) => {
                ingested += 1;
                println!(
                    "  [{}] {} {} = {:?} -> {}...",
                    tier,
                    claim.subject,
                    claim.predicate,
                    format_value(&claim.value),
                    &hash[..16]
                );
            }
            Err(e) => {
                errors += 1;
                println!("  [ERROR] {} {} -> {}", claim.subject, claim.predicate, e);
            }
        }
    }

    (ingested, errors)
}

/// Demo the skeptic query endpoint.
async fn demo_skeptic_query(client: &StemeClient) {
    let queries = [
        ("Semaglutide:Type2Diabetes", "hba1c_change_percent"),
        ("Semaglutide", "nausea_rate"),
        ("Semaglutide:ChronicWeightManagement", "weight_loss_percent"),
    ];

    for (subject, predicate) in queries {
        println!("\nQuery: {} / {}", subject, predicate);

        match client.skeptic(subject, predicate).await {
            Ok(response) => {
                println!("  Status: {:?}", response.status);
                println!("  Conflict Score: {:.2}", response.conflict_score);
                println!("  Candidates: {}", response.candidates_count);
                println!("  Claims:");
                for (i, claim) in response.claims.iter().enumerate() {
                    println!(
                        "    {}. {:?} (weight: {:.2}, count: {})",
                        i + 1,
                        claim.value,
                        claim.weight_share,
                        claim.assertion_count
                    );
                }
            }
            Err(e) => {
                println!("  Error: {}", e);
            }
        }
    }
}

/// Format an ObjectValue for display.
fn format_value(value: &ObjectValue) -> String {
    match value {
        ObjectValue::Text(s) => {
            if s.len() > 40 {
                format!("\"{}...\"", &s[..37])
            } else {
                format!("\"{}\"", s)
            }
        }
        ObjectValue::Number(n) => format!("{}", n),
        ObjectValue::Boolean(b) => format!("{}", b),
        ObjectValue::Reference(r) => format!("@{}", r),
    }
}
