//! Tests for release domain entity

use chrono::Utc;
use governor_core::domain::release::{CrateReleasePlan, Release, ReleasePlan, ReleaseStatus};
use governor_core::domain::version::SemanticVersion;

#[test]
fn test_release_new() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let release = Release::new("test-crate".to_string(), version.clone(), Utc::now());
    assert_eq!(release.crate_name, "test-crate");
    assert_eq!(release.version(), &version);
    assert!(release.is_stable());
    assert!(!release.published);
    assert!(!release.is_prerelease);
    assert!(release.tag.is_none());
    assert!(release.commit_hash.is_none());
    assert!(release.notes.is_none());
    assert!(release.downloads.is_none());
}

#[test]
fn test_release_version() {
    let version = SemanticVersion::parse("2.5.0").unwrap();
    let release = Release::new("my-crate".to_string(), version, Utc::now());
    assert_eq!(release.version().to_string(), "2.5.0");
}

#[test]
fn test_release_is_stable() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let release = Release::new("stable-crate".to_string(), version, Utc::now());
    assert!(release.is_stable());
}

#[test]
fn test_release_is_prerelease() {
    let version = SemanticVersion::parse("1.0.0-beta.1").unwrap();
    let release = Release::new("beta-crate".to_string(), version, Utc::now());
    assert!(release.is_prerelease);
    assert!(!release.is_stable());
}

#[test]
fn test_release_is_alpha() {
    let version = SemanticVersion::parse("0.1.0-alpha").unwrap();
    let release = Release::new("alpha-crate".to_string(), version, Utc::now());
    assert!(release.is_prerelease);
}

#[test]
fn test_release_tag_name_default() {
    let version = SemanticVersion::parse("1.2.3").unwrap();
    let release = Release::new("test".to_string(), version, Utc::now());
    assert_eq!(release.tag_name(), "v1.2.3");
}

#[test]
fn test_release_tag_name_custom() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let release =
        Release::new("test".to_string(), version, Utc::now()).with_tag("custom-v1.0.0".to_string());
    assert_eq!(release.tag_name(), "custom-v1.0.0");
}

#[test]
fn test_release_mark_published() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut release = Release::new("test".to_string(), version, Utc::now());
    assert!(!release.published);
    release.mark_published();
    assert!(release.published);
}

#[test]
fn test_release_with_tag() {
    let version = SemanticVersion::parse("2.0.0").unwrap();
    let release =
        Release::new("test".to_string(), version, Utc::now()).with_tag("v2.0.0".to_string());
    assert_eq!(release.tag, Some("v2.0.0".to_string()));
}

#[test]
fn test_release_with_commit_hash() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let release = Release::new("test".to_string(), version, Utc::now())
        .with_commit_hash("abc123def".to_string());
    assert_eq!(release.commit_hash, Some("abc123def".to_string()));
}

#[test]
fn test_release_with_notes() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let notes = "First stable release".to_string();
    let release = Release::new("test".to_string(), version, Utc::now()).with_notes(notes.clone());
    assert_eq!(release.notes, Some(notes));
}

#[test]
fn test_release_status_equality() {
    assert_eq!(ReleaseStatus::Planned, ReleaseStatus::Planned);
    assert_ne!(ReleaseStatus::Planned, ReleaseStatus::InProgress);
    assert_ne!(ReleaseStatus::Completed, ReleaseStatus::Failed);
}

#[test]
fn test_release_plan_new() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let plan = ReleasePlan::new(version.clone());
    assert_eq!(plan.version, version);
    assert!(plan.crates.is_empty());
    assert_eq!(plan.status, ReleaseStatus::Planned);
    assert!(plan.release_notes.is_none());
    assert!(plan.created_at <= Utc::now());
}

#[test]
fn test_release_plan_add_crate() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version.clone());
    let crate_plan = CrateReleasePlan::new(
        "crate1".to_string(),
        SemanticVersion::parse("0.1.0").unwrap(),
        version,
    );
    plan.add_crate(crate_plan);
    assert_eq!(plan.crates.len(), 1);
}

#[test]
fn test_release_plan_publishable_crates() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version.clone());

    let crate1 = CrateReleasePlan::new(
        "crate1".to_string(),
        SemanticVersion::parse("0.1.0").unwrap(),
        version.clone(),
    )
    .with_publish();
    let crate2 = CrateReleasePlan::new(
        "crate2".to_string(),
        SemanticVersion::parse("0.2.0").unwrap(),
        version,
    );

    plan.add_crate(crate1);
    plan.add_crate(crate2);
    assert_eq!(plan.publishable_crates().len(), 1);
    assert_eq!(plan.publishable_crates()[0].name, "crate1");
}

#[test]
fn test_release_plan_publish_order() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version.clone());

    let crate1 = CrateReleasePlan::new(
        "crate1".to_string(),
        SemanticVersion::parse("0.1.0").unwrap(),
        version.clone(),
    )
    .with_publish()
    .with_order(2);
    let crate2 = CrateReleasePlan::new(
        "crate2".to_string(),
        SemanticVersion::parse("0.2.0").unwrap(),
        version,
    )
    .with_publish()
    .with_order(1);

    plan.add_crate(crate1);
    plan.add_crate(crate2);

    let order = plan.publish_order();
    assert_eq!(order.len(), 2);
    assert_eq!(order[0].publish_order, 1);
    assert_eq!(order[1].publish_order, 2);
}

#[test]
fn test_release_plan_all_published() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version.clone());

    let crate1 = CrateReleasePlan::new(
        "crate1".to_string(),
        SemanticVersion::parse("0.1.0").unwrap(),
        version.clone(),
    )
    .with_publish();
    let crate2 = CrateReleasePlan::new(
        "crate2".to_string(),
        SemanticVersion::parse("0.2.0").unwrap(),
        version,
    )
    .with_publish();

    plan.add_crate(crate1);
    plan.add_crate(crate2);

    assert!(!plan.all_published());
    plan.crates[0].mark_published();
    assert!(!plan.all_published());
    plan.crates[1].mark_published();
    assert!(plan.all_published());
}

#[test]
fn test_release_plan_mark_in_progress() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version);
    assert_eq!(plan.status, ReleaseStatus::Planned);
    plan.mark_in_progress();
    assert_eq!(plan.status, ReleaseStatus::InProgress);
}

#[test]
fn test_release_plan_mark_completed() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version);
    plan.mark_in_progress();
    plan.mark_completed();
    assert_eq!(plan.status, ReleaseStatus::Completed);
}

#[test]
fn test_release_plan_mark_failed() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version);
    plan.mark_in_progress();
    plan.mark_failed();
    assert_eq!(plan.status, ReleaseStatus::Failed);
}

#[test]
fn test_crate_release_plan_new() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let plan = CrateReleasePlan::new("test-crate".to_string(), current.clone(), target.clone());
    assert_eq!(plan.name, "test-crate");
    assert_eq!(plan.current_version, current);
    assert_eq!(plan.target_version, target);
    assert!(!plan.should_publish);
    assert!(!plan.published);
    assert_eq!(plan.publish_order, 0);
    assert!(plan.dependencies.is_empty());
    assert_eq!(plan.estimated_time_secs, 45);
}

#[test]
fn test_crate_release_plan_with_publish() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let plan = CrateReleasePlan::new("test".to_string(), current, target).with_publish();
    assert!(plan.should_publish);
}

#[test]
fn test_crate_release_plan_with_order() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let plan = CrateReleasePlan::new("test".to_string(), current, target).with_order(5);
    assert_eq!(plan.publish_order, 5);
}

#[test]
fn test_crate_release_plan_with_dependency() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let plan = CrateReleasePlan::new("test".to_string(), current, target)
        .with_dependency("dep1".to_string());
    assert_eq!(plan.dependencies.len(), 1);
    assert_eq!(plan.dependencies[0], "dep1");
}

#[test]
fn test_crate_release_plan_with_multiple_dependencies() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let plan = CrateReleasePlan::new("test".to_string(), current, target)
        .with_dependency("dep1".to_string())
        .with_dependency("dep2".to_string())
        .with_dependency("dep3".to_string());
    assert_eq!(plan.dependencies.len(), 3);
}

#[test]
fn test_crate_release_plan_mark_published() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = CrateReleasePlan::new("test".to_string(), current, target);
    assert!(!plan.published);
    plan.mark_published();
    assert!(plan.published);
}

#[test]
fn test_crate_release_plan_crates_io_url() {
    let current = SemanticVersion::parse("0.1.0").unwrap();
    let target = SemanticVersion::parse("2.5.0").unwrap();
    let plan = CrateReleasePlan::new("my-crate".to_string(), current, target);
    assert_eq!(
        plan.crates_io_url(),
        "https://crates.io/crates/my-crate/2.5.0"
    );
}

#[test]
fn test_release_plan_with_release_notes() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let mut plan = ReleasePlan::new(version);
    plan.release_notes = Some("First release".to_string());
    assert_eq!(plan.release_notes, Some("First release".to_string()));
}

#[test]
fn test_release_plan_empty_publish_order() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let plan = ReleasePlan::new(version);
    let order = plan.publish_order();
    assert!(order.is_empty());
}

#[test]
fn test_release_plan_all_published_when_empty() {
    let version = SemanticVersion::parse("1.0.0").unwrap();
    let plan = ReleasePlan::new(version);
    assert!(plan.all_published());
}
