//! Live integration tests that hit the real SEC EDGAR API.
//!
//! These are `#[ignore]` by default. Run them with:
//!
//!     cargo test -- --ignored
//!
//! Or run only the live tests:
//!
//!     cargo test --test live -- --ignored

use edgar_rs::{Cik, ClientBuilder, SearchOptions};

const USER_AGENT: &str = "edgar-rs-tests/0.1.0 test@example.com";

/// Apple Inc. CIK
const APPLE_CIK: u64 = 320193;

fn live_client() -> edgar_rs::Client {
    ClientBuilder::new(USER_AGENT).build().unwrap()
}

#[tokio::test]
#[ignore]
async fn live_get_tickers() {
    let client = live_client();
    let tickers = client.get_tickers().await.unwrap();

    // There should be thousands of tickers
    assert!(
        tickers.len() > 1000,
        "expected >1000 tickers, got {}",
        tickers.len()
    );

    // Apple should be in there
    let apple_cik = APPLE_CIK.to_string();
    assert!(tickers.contains_key(&apple_cik), "Apple CIK not found");
    let apple = &tickers[&apple_cik];
    assert_eq!(apple.ticker, "AAPL");
}

#[tokio::test]
#[ignore]
async fn live_get_submission() {
    let client = live_client();
    let sub = client.get_submission(Cik::new(APPLE_CIK)).await.unwrap();

    // SEC returns CIK zero-padded to 10 digits
    assert!(sub.cik.ends_with(&APPLE_CIK.to_string()));
    assert!(
        sub.name.to_uppercase().contains("APPLE"),
        "unexpected name: {}",
        sub.name
    );
    assert!(sub.tickers.contains(&"AAPL".to_string()));
    assert!(!sub.filings.recent.accession_number.is_empty());
}

#[tokio::test]
#[ignore]
async fn live_get_submission_not_found() {
    let client = live_client();
    // CIK 9999999999 should not exist
    let err = client
        .get_submission(Cik::new(9999999999))
        .await
        .unwrap_err();
    assert!(err.is_not_found());
}

#[tokio::test]
#[ignore]
async fn live_get_company_concept() {
    let client = live_client();
    let concept = client
        .get_company_concept(Cik::new(APPLE_CIK), "us-gaap", "Revenues")
        .await
        .unwrap();

    assert_eq!(concept.cik, APPLE_CIK);
    assert_eq!(concept.taxonomy, "us-gaap");
    assert_eq!(concept.tag, "Revenues");
    assert!(concept.units.contains_key("USD"));
    assert!(!concept.units["USD"].is_empty());
}

#[tokio::test]
#[ignore]
async fn live_get_company_facts() {
    let client = live_client();
    let facts = client.get_company_facts(Cik::new(APPLE_CIK)).await.unwrap();

    assert_eq!(facts.cik, APPLE_CIK);
    assert!(facts.facts.contains_key("us-gaap"));
    assert!(facts.facts.contains_key("dei"));

    // Should have many concepts
    let gaap = &facts.facts["us-gaap"];
    assert!(
        gaap.len() > 10,
        "expected >10 us-gaap concepts, got {}",
        gaap.len()
    );
}

#[tokio::test]
#[ignore]
async fn live_get_frame() {
    let client = live_client();
    let frame = client
        .get_frame("us-gaap", "AccountsPayableCurrent", "USD", "CY2023Q4I")
        .await
        .unwrap();

    assert_eq!(frame.taxonomy, "us-gaap");
    assert_eq!(frame.tag, "AccountsPayableCurrent");
    assert_eq!(frame.uom, "USD");
    assert!(
        !frame.data.is_empty(),
        "expected frame data to be non-empty"
    );
    assert!(frame.pts > 0);
}

#[tokio::test]
#[ignore]
async fn live_get_document() {
    // First get a real accession number from Apple's submissions
    let client = live_client();
    let sub = client.get_submission(Cik::new(APPLE_CIK)).await.unwrap();

    let recent = &sub.filings.recent;
    assert!(
        !recent.accession_number.is_empty(),
        "no recent filings found"
    );

    let accession = &recent.accession_number[0];
    let primary_doc = &recent.primary_document[0];

    let doc = client
        .get_document(Cik::new(APPLE_CIK), accession, primary_doc)
        .await
        .unwrap();

    // Document should be non-empty
    assert!(!doc.is_empty(), "document body is empty");
    assert!(
        doc.len() > 100,
        "document suspiciously short: {} bytes",
        doc.len()
    );
}

#[tokio::test]
#[ignore]
async fn live_search_basic() {
    let client = live_client();
    let result = client.search("Apple", None).await.unwrap();

    assert!(result.total > 0, "expected search results for 'Apple'");
    assert!(!result.hits.is_empty());
    assert!(!result.hits[0].id.is_empty());
    assert!(result.hits[0].score > 0.0);
}

#[tokio::test]
#[ignore]
async fn live_search_with_form_filter() {
    let client = live_client();
    let opts = SearchOptions {
        forms: vec!["10-K".into()],
        ..Default::default()
    };
    let result = client.search("Apple", Some(&opts)).await.unwrap();

    assert!(result.total > 0);
    for hit in &result.hits {
        assert!(
            hit.form.starts_with("10-K"),
            "expected 10-K form, got {}",
            hit.form
        );
    }
}

#[tokio::test]
#[ignore]
async fn live_search_with_date_range() {
    let client = live_client();
    let opts = SearchOptions {
        date_start: Some("2023-01-01".into()),
        date_end: Some("2023-12-31".into()),
        ..Default::default()
    };
    let result = client.search("Apple", Some(&opts)).await.unwrap();

    assert!(result.total > 0);
    for hit in &result.hits {
        assert!(
            hit.file_date.as_str() >= "2023-01-01" && hit.file_date.as_str() <= "2023-12-31",
            "file_date {} outside range",
            hit.file_date
        );
    }
}

#[tokio::test]
#[ignore]
async fn live_search_empty_results() {
    let client = live_client();
    let result = client
        .search("xyzzy_nonexistent_query_12345", None)
        .await
        .unwrap();

    assert_eq!(result.total, 0);
    assert!(result.hits.is_empty());
}
