//! Example: Polling and verification flow
//!
//! This example demonstrates the polling loop and presentation verification
//! flow in detail, including error handling and trust registry validation.
//!
//! # Running
//!
//! ```bash
//! cargo run --example poll_and_verify
//! ```
//!
//! # Flow
//!
//! 1. Manual polling loop with status handling
//! 2. SD-JWT presentation verification
//! 3. Trust registry validation
//! 4. Credential type validation against issuer's permitted types

use std::time::{Duration, Instant};

use lastid_sdk::{BaseCredentialPolicy, ClientBuilder, LastIDError, RequestId, RequestStatus};

/// Custom polling loop with detailed status handling
///
/// This demonstrates a manual polling loop similar to what the SDK does
/// internally. Note: FR-010 requires timeouts to return
/// Err(LastIDError::Timeout), not Ok(RequestStatus::Timeout).
async fn poll_with_logging(
    client: &lastid_sdk::LastIDClient,
    request_id: &RequestId,
    timeout: Duration,
) -> Result<RequestStatus, LastIDError> {
    let start = Instant::now();
    let mut poll_count = 0;
    let mut interval = Duration::from_secs(2);

    println!("Starting manual polling loop...");
    println!("  Timeout: {} seconds", timeout.as_secs());
    println!("  Initial interval: {} seconds", interval.as_secs());
    println!();

    loop {
        poll_count += 1;
        let elapsed = start.elapsed();

        // Check timeout - FR-010: return Err(LastIDError::Timeout), not
        // Ok(RequestStatus::Timeout)
        if elapsed >= timeout {
            println!("  [TIMEOUT] Polling exceeded {} seconds", timeout.as_secs());
            return Err(LastIDError::Timeout(elapsed.as_secs()));
        }

        // Poll status
        println!(
            "  [Poll #{}] Checking status (elapsed: {:.1}s)...",
            poll_count,
            elapsed.as_secs_f32()
        );

        let status = client.poll_request(request_id).await?;

        match &status {
            RequestStatus::Pending { created_at, .. } => {
                println!("    -> Pending (created: {})", created_at);
            }
            RequestStatus::Fulfilled { fulfilled_at, .. } => {
                println!("    -> Fulfilled at: {}", fulfilled_at);
                return Ok(status);
            }
            RequestStatus::Denied {
                reason, denied_at, ..
            } => {
                println!("    -> Denied at {}: {}", denied_at, reason);
                return Ok(status);
            }
            RequestStatus::Expired { expired_at, .. } => {
                println!("    -> Expired at: {}", expired_at);
                return Ok(status);
            }
            RequestStatus::NotFound { message, .. } => {
                println!("    -> Not found: {}", message);
                return Ok(status);
            }
            _ => {
                // Handle future variants that may be added
                println!("    -> Unknown status");
                return Ok(status);
            }
        }

        // Wait before next poll with exponential backoff
        println!("    Waiting {} seconds...", interval.as_secs());
        tokio::time::sleep(interval).await;

        // Increase interval (capped at 10 seconds)
        interval = Duration::from_millis((interval.as_millis() * 3 / 2).min(10_000) as u64);
    }
}

/// Verify a presentation with detailed output
async fn verify_with_details(
    client: &lastid_sdk::LastIDClient,
    presentation: &str,
) -> Result<(), LastIDError> {
    println!("\n=== Verifying Presentation ===\n");

    // Parse the presentation
    println!("Step 1: Parsing SD-JWT presentation...");
    let vp = lastid_sdk::VerifiablePresentation::parse(presentation)?;
    println!("  ✓ Successfully parsed SD-JWT");
    println!("  Issuer: {}", vp.issuer_did()?);
    println!("  Subject: {}", vp.subject_did()?);
    println!("  Type: {}", vp.credential_type()?);
    println!("  Disclosures: {}", vp.disclosures().len());

    // Validate the issuer
    println!("\nStep 2: Validating issuer against trust registry...");
    let issuer_did = vp.issuer_did()?;
    let issuer_info = client.validate_issuer(issuer_did).await?;

    println!("  ✓ Issuer found in trust registry");
    println!("  Organization: {}", issuer_info.organization_name);
    println!("  Status: {:?}", issuer_info.status);
    println!("  Permitted types: {:?}", issuer_info.permitted_types);

    // Check if credential type is permitted
    let credential_type = vp.credential_type()?;
    if issuer_info.can_issue(credential_type) {
        println!(
            "  ✓ Issuer is authorized to issue '{}' credentials",
            credential_type
        );
    } else {
        println!(
            "  ✗ Issuer is NOT authorized to issue '{}' credentials",
            credential_type
        );
        return Err(LastIDError::invalid_credential(format!(
            "Issuer cannot issue '{}' credentials",
            credential_type
        )));
    }

    // Full verification via client
    println!("\nStep 3: Full credential verification...");
    let verified = client.verify_presentation(presentation).await?;

    println!("  ✓ Signature valid");
    println!("  ✓ Not expired");
    println!("  ✓ Issuer trusted");

    println!("\n=== Verified Credential ===\n");
    println!("  Subject DID: {}", verified.subject_did);
    println!("  Issuer DID: {}", verified.issuer_did);
    println!("  Type: {}", verified.credential_type);
    println!("  Issued: {}", verified.issued_at);
    println!("  Expires: {}", verified.expires_at);
    println!("  Issuer Status: {:?}", verified.issuer_status);

    // Display claims
    println!("\n  Claims:");
    let claims = vp.reconstruct_claims();
    if let Some(obj) = claims.as_object() {
        for (key, value) in obj {
            // Skip internal SD-JWT fields
            if !key.starts_with('_')
                && key != "iss"
                && key != "sub"
                && key != "iat"
                && key != "exp"
                && key != "vct"
            {
                println!("    {}: {}", key, value);
            }
        }
    }

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), LastIDError> {
    // Initialize tracing (optional)
    #[cfg(feature = "tracing")]
    {
        use tracing_subscriber::EnvFilter;
        tracing_subscriber::fmt()
            .with_env_filter(
                EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
            )
            .init();
    }

    println!("=== LastID SDK: Poll and Verify Example ===\n");

    // Initialize client
    println!("Initializing client...");
    let client = ClientBuilder::new()
        .with_auto_config()
        .map_err(|e| {
            eprintln!("Error: {}", e);
            eprintln!("\nHint: Create lastid.toml with:");
            eprintln!("  idp_endpoint = \"https://human.dev.lastid.co\"");
            e
        })?
        .build()?;

    println!("  Endpoint: {}", client.config().idp_endpoint);

    // Request credential
    println!("\nRequesting base credential...");
    let policy = BaseCredentialPolicy::new().with_callback("https://example.com/callback");

    let response = client.request_credential(policy).await?;
    println!("  Request ID: {}", response.request_id);
    println!("  Request URI (for QR code): {}", response.request_uri());
    println!("  Expires in: {}s", response.expires_in);

    // Custom polling loop
    println!("\n=== Manual Polling Loop ===\n");

    // poll_with_logging returns Err(LastIDError::Timeout) on timeout (FR-010)
    let status =
        match poll_with_logging(&client, response.request_id(), Duration::from_secs(120)).await {
            Ok(status) => status,
            Err(LastIDError::Timeout(elapsed_seconds)) => {
                println!("\n Polling timed out after {} seconds", elapsed_seconds);
                return Ok(());
            }
            Err(e) => return Err(e),
        };

    // Handle result
    match status {
        RequestStatus::Fulfilled { presentation, .. } => {
            // Verify the presentation
            verify_with_details(&client, &presentation).await?;
            println!("\n SUCCESS: Credential verified!");
        }
        RequestStatus::Denied { reason, .. } => {
            println!("\n Request denied: {}", reason);
        }
        RequestStatus::Expired { .. } => {
            println!("\n Request expired");
        }
        RequestStatus::NotFound { message, .. } => {
            println!("\n Request not found: {}", message);
        }
        RequestStatus::Pending { .. } => {
            // Shouldn't happen after our polling loop
            println!("\n? Unexpected pending status");
        }
        _ => {
            // Handle future variants that may be added
            println!("\n? Unknown status (SDK may need updating)");
        }
    }

    Ok(())
}
