//! Pure-logic tests for `SeekingAlphaSite`.
//! No browser launched — live tests live in `sa_live.rs`.

use tail_fin_common::{AuthFailureKind, FailureIndicators, Site};
use tail_fin_sa::SeekingAlphaSite;

#[test]
fn identity() {
    let s = SeekingAlphaSite;
    assert_eq!(s.id(), "seekingalpha");
    assert_eq!(s.display_name(), "Seeking Alpha");
    assert_eq!(s.refresh_url(), "https://seekingalpha.com/");
    assert_eq!(s.cookie_domain_patterns(), &["*.seekingalpha.com"]);
}

#[test]
fn refresh_interval_is_at_least_60s() {
    let s = SeekingAlphaSite;
    assert!(s.refresh_interval_min() >= std::time::Duration::from_secs(60));
}

#[test]
fn detect_401_maps_to_cookie_expired() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(401),
        ..Default::default()
    };
    assert!(matches!(
        s.detect_auth_failure(&ind),
        Some(AuthFailureKind::CookieExpired)
    ));
}

#[test]
fn detect_429_maps_to_rate_limited_with_long_backoff() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(429),
        ..Default::default()
    };
    match s.detect_auth_failure(&ind) {
        Some(AuthFailureKind::RateLimited { retry_after }) => {
            // SA rate limits need long cooldowns — at least 5 min.
            assert!(retry_after >= std::time::Duration::from_secs(300));
        }
        other => panic!("expected RateLimited, got {other:?}"),
    }
}

#[test]
fn detect_503_maps_to_cloudflare_antibot() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(503),
        ..Default::default()
    };
    match s.detect_auth_failure(&ind) {
        Some(AuthFailureKind::AntibotBlock { challenge_type }) => {
            assert!(challenge_type.contains("cloudflare"));
        }
        other => panic!("expected AntibotBlock, got {other:?}"),
    }
}

#[test]
fn detect_perimeterx_body_marker() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(200),
        body_preview: "<html>... px-captcha ...</html>".into(),
        ..Default::default()
    };
    match s.detect_auth_failure(&ind) {
        Some(AuthFailureKind::AntibotBlock { challenge_type }) => {
            assert_eq!(challenge_type, "perimeterx");
        }
        other => panic!("expected AntibotBlock perimeterx, got {other:?}"),
    }
}

#[test]
fn detect_access_denied_body() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(200),
        body_preview: "Access to this page has been denied.".into(),
        ..Default::default()
    };
    assert!(matches!(
        s.detect_auth_failure(&ind),
        Some(AuthFailureKind::AntibotBlock { .. })
    ));
}

#[test]
fn detect_returns_none_for_healthy_200() {
    let s = SeekingAlphaSite;
    let ind = FailureIndicators {
        status: Some(200),
        body_preview: "<html>stock analysis</html>".into(),
        ..Default::default()
    };
    assert!(s.detect_auth_failure(&ind).is_none());
}
