//! Tests for diff/trend tracking

use chrono::Utc;
use dep_age::diff::{compute_diff, format_diff, DiffChange, PreviousRun};
use dep_age::{DepAgeSummary, DepResult, Registry, Status};

fn make_summary(results: Vec<DepResult>) -> DepAgeSummary {
    let fresh = results.iter().filter(|r| r.status == Status::Fresh).count();
    let aging = results.iter().filter(|r| r.status == Status::Aging).count();
    let stale = results.iter().filter(|r| r.status == Status::Stale).count();
    let ancient = results
        .iter()
        .filter(|r| r.status == Status::Ancient)
        .count();
    let errors = results
        .iter()
        .filter(|r| matches!(r.status, Status::Error(_)))
        .count();
    DepAgeSummary {
        total: results.len(),
        fresh,
        aging,
        stale,
        ancient,
        errors,
        oldest: None,
        checked_at: Utc::now(),
        results,
    }
}

#[test]
fn test_diff_newly_stale() {
    let prev = PreviousRun {
        manifest_path: "Cargo.toml".to_string(),
        checked_at: Utc::now(),
        results: vec![dep_age::diff::PreviousResult {
            name: "old-crate".to_string(),
            version_spec: "1.0".to_string(),
            status: "fresh".to_string(),
            days_since_publish: Some(30),
        }],
    };

    let curr = make_summary(vec![DepResult {
        name: "old-crate".to_string(),
        version_spec: "1.0".to_string(),
        latest_version: "1.0".to_string(),
        published_at: Some(Utc::now()),
        days_since_publish: Some(400),
        status: Status::Stale,
        registry: Registry::Crates,
    }]);

    let diffs = compute_diff(&curr, &prev);
    assert_eq!(diffs.len(), 1);
    assert_eq!(diffs[0].change, DiffChange::NewlyStale);
    assert_eq!(diffs[0].package, "old-crate");
}

#[test]
fn test_diff_improved() {
    let prev = PreviousRun {
        manifest_path: "Cargo.toml".to_string(),
        checked_at: Utc::now(),
        results: vec![dep_age::diff::PreviousResult {
            name: "old-crate".to_string(),
            version_spec: "1.0".to_string(),
            status: "stale".to_string(),
            days_since_publish: Some(800),
        }],
    };

    let curr = make_summary(vec![DepResult {
        name: "old-crate".to_string(),
        version_spec: "2.0".to_string(),
        latest_version: "2.0".to_string(),
        published_at: Some(Utc::now()),
        days_since_publish: Some(10),
        status: Status::Fresh,
        registry: Registry::Crates,
    }]);

    let diffs = compute_diff(&curr, &prev);
    assert_eq!(diffs.len(), 1);
    assert_eq!(diffs[0].change, DiffChange::Improved);
}

#[test]
fn test_diff_more_stale() {
    let prev = PreviousRun {
        manifest_path: "Cargo.toml".to_string(),
        checked_at: Utc::now(),
        results: vec![dep_age::diff::PreviousResult {
            name: "old-crate".to_string(),
            version_spec: "1.0".to_string(),
            status: "stale".to_string(),
            days_since_publish: Some(800),
        }],
    };

    let curr = make_summary(vec![DepResult {
        name: "old-crate".to_string(),
        version_spec: "1.0".to_string(),
        latest_version: "1.0".to_string(),
        published_at: Some(Utc::now()),
        days_since_publish: Some(900),
        status: Status::Stale,
        registry: Registry::Crates,
    }]);

    let diffs = compute_diff(&curr, &prev);
    assert_eq!(diffs.len(), 1);
    assert_eq!(diffs[0].change, DiffChange::MoreStale);
}

#[test]
fn test_diff_new_package() {
    let prev = PreviousRun {
        manifest_path: "Cargo.toml".to_string(),
        checked_at: Utc::now(),
        results: vec![],
    };

    let curr = make_summary(vec![DepResult {
        name: "new-crate".to_string(),
        version_spec: "1.0".to_string(),
        latest_version: "1.0".to_string(),
        published_at: Some(Utc::now()),
        days_since_publish: Some(5),
        status: Status::Fresh,
        registry: Registry::Crates,
    }]);

    let diffs = compute_diff(&curr, &prev);
    assert_eq!(diffs.len(), 1);
    assert_eq!(diffs[0].change, DiffChange::New);
}

#[test]
fn test_diff_unchanged() {
    let prev = PreviousRun {
        manifest_path: "Cargo.toml".to_string(),
        checked_at: Utc::now(),
        results: vec![dep_age::diff::PreviousResult {
            name: "same-crate".to_string(),
            version_spec: "1.0".to_string(),
            status: "fresh".to_string(),
            days_since_publish: Some(30),
        }],
    };

    let curr = make_summary(vec![DepResult {
        name: "same-crate".to_string(),
        version_spec: "1.0".to_string(),
        latest_version: "1.0".to_string(),
        published_at: Some(Utc::now()),
        days_since_publish: Some(31),
        status: Status::Fresh,
        registry: Registry::Crates,
    }]);

    let diffs = compute_diff(&curr, &prev);
    assert!(diffs.is_empty());
}

#[test]
fn test_format_diff_empty() {
    let output = format_diff(&[]);
    assert_eq!(output, "No changes since last run.");
}

#[test]
fn test_format_diff_mixed() {
    let diffs = vec![
        dep_age::diff::DiffResult {
            package: "old-crate".to_string(),
            change: DiffChange::NewlyStale,
            previous_status: Some("fresh".to_string()),
            current_status: "stale".to_string(),
        },
        dep_age::diff::DiffResult {
            package: "new-crate".to_string(),
            change: DiffChange::New,
            previous_status: None,
            current_status: "fresh".to_string(),
        },
    ];

    let output = format_diff(&diffs);
    assert!(output.contains("Newly stale"));
    assert!(output.contains("old-crate"));
    assert!(output.contains("New packages"));
    assert!(output.contains("new-crate"));
}
