//! Tests for session types and query builders.

use chrono::Utc;

use super::*;
use crate::model::Order;

#[test]
fn test_session_is_active() {
    let mut session = Session::default();
    assert!(session.is_active());

    session.last_ended_at = Some(Utc::now());
    assert!(!session.is_active());
}

#[test]
fn test_session_total_tokens() {
    let session = Session {
        tokens_input: 200,
        tokens_output: 500,
        tokens_cache_read: 200,
        tokens_cache_write: 100,
        ..Default::default()
    };
    assert_eq!(session.total_tokens(), 1000);
}

#[test]
fn test_session_unified_fields() {
    let session = Session {
        message_count: 3,
        tool_call_count: 10,
        api_request_count: 2,
        ..Default::default()
    };
    // v14 schema uses unified fields directly (no source-specific columns)
    assert_eq!(session.message_count, 3);
    assert_eq!(session.tool_call_count, 10);
    assert_eq!(session.api_request_count, 2);
}

#[test]
fn test_session_default_values() {
    // Default session should have zero counts
    let session = Session::default();
    assert_eq!(session.tokens_input, 0);
    assert_eq!(session.message_count, 0);
    assert_eq!(session.api_request_count, 0);
}

#[test]
fn test_session_query_builder() {
    let query = SessionQuery::new()
        .active_only()
        .with_framework("claude")
        .with_limit(10)
        .with_order_by(SessionOrder::LastActivity);

    assert!(query.active_only);
    assert_eq!(query.framework, Some("claude".to_string()));
    assert_eq!(query.limit, Some(10));
    assert_eq!(query.order_by, SessionOrder::LastActivity);
    assert_eq!(query.direction, Order::Desc); // Default is descending
}

#[test]
fn test_session_query_github_filters() {
    let query = SessionQuery::new()
        .with_github_repo("paradigmxyz/mi6")
        .with_github_issue(236)
        .with_github_pr(237);

    assert_eq!(query.github_repo, Some("paradigmxyz/mi6".to_string()));
    assert_eq!(query.github_issue, Some(236));
    assert_eq!(query.github_pr, Some(237));
}

#[test]
fn test_session_query_ascending() {
    let query = SessionQuery::new().ascending();
    assert_eq!(query.direction, Order::Asc);

    let query = query.descending();
    assert_eq!(query.direction, Order::Desc);
}

#[test]
fn test_session_query_with_direction() {
    // Test with_direction method for consistency with EventQuery
    let query = SessionQuery::new().with_direction(Order::Asc);
    assert_eq!(query.direction, Order::Asc);

    let query = SessionQuery::new().with_direction(Order::Desc);
    assert_eq!(query.direction, Order::Desc);
}

#[test]
fn test_session_is_process_running_with_own_pid() {
    let session = Session {
        pid: Some(std::process::id() as i32),
        ..Default::default()
    };
    assert!(session.is_process_running());
}

#[test]
fn test_session_is_process_running_with_dead_pid() {
    let session = Session {
        pid: Some(999_999_999),
        ..Default::default()
    };
    assert!(!session.is_process_running());
}

#[test]
fn test_session_is_process_running_without_pid() {
    let session = Session::default();
    assert!(!session.is_process_running());
}

#[test]
fn test_session_status_display() {
    assert_eq!(SessionStatus::Idle.to_string(), "idle");
    assert_eq!(SessionStatus::Busy.to_string(), "busy");
    assert_eq!(SessionStatus::Wait.to_string(), "wait");
    assert_eq!(SessionStatus::Done.to_string(), "done");
}

#[test]
fn test_session_status_from_str() {
    assert_eq!(
        "idle".parse::<SessionStatus>().unwrap(),
        SessionStatus::Idle
    );
    assert_eq!(
        "busy".parse::<SessionStatus>().unwrap(),
        SessionStatus::Busy
    );
    assert_eq!(
        "wait".parse::<SessionStatus>().unwrap(),
        SessionStatus::Wait
    );
    assert_eq!(
        "done".parse::<SessionStatus>().unwrap(),
        SessionStatus::Done
    );
}

#[test]
fn test_session_status_from_str_case_insensitive() {
    assert_eq!(
        "IDLE".parse::<SessionStatus>().unwrap(),
        SessionStatus::Idle
    );
    assert_eq!(
        "Busy".parse::<SessionStatus>().unwrap(),
        SessionStatus::Busy
    );
    assert_eq!(
        "WAIT".parse::<SessionStatus>().unwrap(),
        SessionStatus::Wait
    );
    assert_eq!(
        "Done".parse::<SessionStatus>().unwrap(),
        SessionStatus::Done
    );
}

#[test]
fn test_session_status_from_str_invalid() {
    assert!("invalid".parse::<SessionStatus>().is_err());
    assert!("".parse::<SessionStatus>().is_err());
    assert!("running".parse::<SessionStatus>().is_err());
}

#[test]
fn test_session_status_roundtrip() {
    for status in [
        SessionStatus::Idle,
        SessionStatus::Busy,
        SessionStatus::Wait,
        SessionStatus::Done,
    ] {
        let s = status.to_string();
        let parsed: SessionStatus = s.parse().unwrap();
        assert_eq!(status, parsed);
    }
}

#[test]
fn test_session_metrics_source() {
    // Transcript is highest priority
    let session = Session {
        transcript_available: true,
        n_otel_events: 5,
        n_hook_events: 10,
        ..Default::default()
    };
    assert_eq!(session.metrics_source(), "transcript");

    // OTEL is second priority when transcript not available
    let session = Session {
        transcript_available: false,
        n_otel_events: 5,
        n_hook_events: 10,
        ..Default::default()
    };
    assert_eq!(session.metrics_source(), "otel");

    // Hook is fallback
    let session = Session {
        transcript_available: false,
        n_otel_events: 0,
        n_hook_events: 10,
        ..Default::default()
    };
    assert_eq!(session.metrics_source(), "hook");
}

#[test]
fn test_session_should_update_metrics() {
    // When transcript is available, OTEL and hook should NOT update metrics
    let session = Session {
        transcript_available: true,
        ..Default::default()
    };
    assert!(!session.should_otel_update_tokens());
    assert!(!session.should_hook_update_counts());

    // When transcript is not available, updates should be allowed
    let session = Session {
        transcript_available: false,
        ..Default::default()
    };
    assert!(session.should_otel_update_tokens());
    assert!(session.should_hook_update_counts());
}
