//! Integration tests for Worker repository
//!
//! Tests cover CRUD operations, specialized queries, constraints,
//! enum handling, timestamps, heartbeat updates, and edge cases.

use attune_common::models::enums::{WorkerStatus, WorkerType};
use attune_common::repositories::runtime::{
    CreateRuntimeInput, CreateWorkerInput, RuntimeRepository, UpdateWorkerInput, WorkerRepository,
};
use attune_common::repositories::{Create, Delete, FindById, List, Update};

use serde_json::json;
use sqlx::PgPool;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicU64, Ordering};

mod helpers;
use helpers::create_test_pool;

// Global counter for unique IDs across all tests
static GLOBAL_COUNTER: AtomicU64 = AtomicU64::new(0);

/// Test fixture for creating unique worker data
struct WorkerFixture {
    sequence: AtomicU64,
    test_id: String,
}

impl WorkerFixture {
    fn new(test_name: &str) -> Self {
        let global_count = GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst);
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();

        // Create unique test ID from test name, timestamp, and global counter
        let mut hasher = DefaultHasher::new();
        test_name.hash(&mut hasher);
        timestamp.hash(&mut hasher);
        global_count.hash(&mut hasher);
        let hash = hasher.finish();

        let test_id = format!("test_{}_{:x}", global_count, hash);

        Self {
            sequence: AtomicU64::new(0),
            test_id,
        }
    }

    fn unique_name(&self, prefix: &str) -> String {
        let seq = self.sequence.fetch_add(1, Ordering::SeqCst);
        format!("{}_{}_worker_{}", prefix, self.test_id, seq)
    }

    fn create_input(&self, name_suffix: &str, worker_type: WorkerType) -> CreateWorkerInput {
        CreateWorkerInput {
            name: self.unique_name(name_suffix),
            worker_type,
            runtime: None,
            host: Some("localhost".to_string()),
            port: Some(8080),
            status: Some(WorkerStatus::Active),
            capabilities: Some(json!({
                "cpu": "x86_64",
                "memory": "8GB",
                "python": ["3.9", "3.10", "3.11"],
                "node": ["16", "18", "20"]
            })),
            meta: Some(json!({
                "region": "us-west-2",
                "environment": "test"
            })),
        }
    }

    fn create_minimal_input(&self, name_suffix: &str) -> CreateWorkerInput {
        CreateWorkerInput {
            name: self.unique_name(name_suffix),
            worker_type: WorkerType::Local,
            runtime: None,
            host: None,
            port: None,
            status: None,
            capabilities: None,
            meta: None,
        }
    }
}

async fn setup_db() -> PgPool {
    create_test_pool()
        .await
        .expect("Failed to create test pool")
}

// ============================================================================
// Basic CRUD Tests
// ============================================================================

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_worker() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("create_worker");
    let input = fixture.create_input("basic", WorkerType::Local);

    let worker = WorkerRepository::create(&pool, input.clone())
        .await
        .expect("Failed to create worker");

    assert!(worker.id > 0);
    assert_eq!(worker.name, input.name);
    assert_eq!(worker.worker_type, input.worker_type);
    assert_eq!(worker.runtime, input.runtime);
    assert_eq!(worker.host, input.host);
    assert_eq!(worker.port, input.port);
    assert_eq!(worker.status, input.status);
    assert_eq!(worker.capabilities, input.capabilities);
    assert_eq!(worker.meta, input.meta);
    assert_eq!(worker.last_heartbeat, None);
    assert!(worker.created > chrono::Utc::now() - chrono::Duration::seconds(5));
    assert!(worker.updated > chrono::Utc::now() - chrono::Duration::seconds(5));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_worker_minimal() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("create_worker_minimal");
    let input = fixture.create_minimal_input("minimal");

    let worker = WorkerRepository::create(&pool, input.clone())
        .await
        .expect("Failed to create minimal worker");

    assert!(worker.id > 0);
    assert_eq!(worker.name, input.name);
    assert_eq!(worker.worker_type, WorkerType::Local);
    assert_eq!(worker.host, None);
    assert_eq!(worker.port, None);
    assert_eq!(worker.status, None);
    assert_eq!(worker.capabilities, None);
    assert_eq!(worker.meta, None);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_worker_by_id() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_id");
    let input = fixture.create_input("findable", WorkerType::Remote);

    let created = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let found = WorkerRepository::find_by_id(&pool, created.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found");

    assert_eq!(found.id, created.id);
    assert_eq!(found.name, created.name);
    assert_eq!(found.worker_type, created.worker_type);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_worker_by_id_not_found() {
    let pool = setup_db().await;

    let result = WorkerRepository::find_by_id(&pool, 999999999)
        .await
        .expect("Query should succeed");

    assert!(result.is_none());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_worker_by_name() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_name");
    let input = fixture.create_input("nametest", WorkerType::Container);

    let created = WorkerRepository::create(&pool, input.clone())
        .await
        .expect("Failed to create worker");

    let found = WorkerRepository::find_by_name(&pool, &input.name)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found");

    assert_eq!(found.id, created.id);
    assert_eq!(found.name, created.name);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_worker_by_name_not_found() {
    let pool = setup_db().await;

    let result = WorkerRepository::find_by_name(&pool, "nonexistent_worker_999999")
        .await
        .expect("Query should succeed");

    assert!(result.is_none());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_workers() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("list_workers");

    let input1 = fixture.create_input("list1", WorkerType::Local);
    let input2 = fixture.create_input("list2", WorkerType::Remote);

    let created1 = WorkerRepository::create(&pool, input1)
        .await
        .expect("Failed to create worker 1");
    let created2 = WorkerRepository::create(&pool, input2)
        .await
        .expect("Failed to create worker 2");

    let list = WorkerRepository::list(&pool)
        .await
        .expect("Failed to list workers");

    assert!(list.len() >= 2);
    assert!(list.iter().any(|w| w.id == created1.id));
    assert!(list.iter().any(|w| w.id == created2.id));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_worker() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("update_worker");
    let input = fixture.create_input("update", WorkerType::Local);

    let created = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let update_input = UpdateWorkerInput {
        name: Some("updated_worker_name".to_string()),
        status: Some(WorkerStatus::Busy),
        capabilities: Some(json!({
            "updated": true
        })),
        meta: Some(json!({
            "version": "2.0"
        })),
        host: Some("updated-host".to_string()),
        port: Some(9090),
    };

    let updated = WorkerRepository::update(&pool, created.id, update_input.clone())
        .await
        .expect("Failed to update worker");

    assert_eq!(updated.id, created.id);
    assert_eq!(updated.name, update_input.name.unwrap());
    assert_eq!(updated.status, update_input.status);
    assert_eq!(updated.capabilities, update_input.capabilities);
    assert_eq!(updated.meta, update_input.meta);
    assert_eq!(updated.host, update_input.host);
    assert_eq!(updated.port, update_input.port);
    assert!(updated.updated > created.updated);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_worker_partial() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("update_partial");
    let input = fixture.create_input("partial", WorkerType::Remote);

    let created = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let update_input = UpdateWorkerInput {
        status: Some(WorkerStatus::Inactive),
        name: None,
        capabilities: None,
        meta: None,
        host: None,
        port: None,
    };

    let updated = WorkerRepository::update(&pool, created.id, update_input.clone())
        .await
        .expect("Failed to update worker");

    assert_eq!(updated.status, update_input.status);
    assert_eq!(updated.name, created.name);
    assert_eq!(updated.capabilities, created.capabilities);
    assert_eq!(updated.meta, created.meta);
    assert_eq!(updated.host, created.host);
    assert_eq!(updated.port, created.port);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_worker_empty() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("update_empty");
    let input = fixture.create_input("empty", WorkerType::Container);

    let created = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let update_input = UpdateWorkerInput::default();

    let result = WorkerRepository::update(&pool, created.id, update_input)
        .await
        .expect("Failed to update worker");

    // Should return existing entity unchanged
    assert_eq!(result.id, created.id);
    assert_eq!(result.name, created.name);
    assert_eq!(result.status, created.status);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_delete_worker() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("delete_worker");
    let input = fixture.create_input("delete", WorkerType::Local);

    let created = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let deleted = WorkerRepository::delete(&pool, created.id)
        .await
        .expect("Failed to delete worker");

    assert!(deleted);

    let found = WorkerRepository::find_by_id(&pool, created.id)
        .await
        .expect("Query should succeed");

    assert!(found.is_none());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_delete_worker_not_found() {
    let pool = setup_db().await;

    let deleted = WorkerRepository::delete(&pool, 999999999)
        .await
        .expect("Delete should succeed");

    assert!(!deleted);
}

// ============================================================================
// Specialized Query Tests
// ============================================================================

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_by_status_active() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_status_active");

    let mut input1 = fixture.create_input("active1", WorkerType::Local);
    input1.status = Some(WorkerStatus::Active);

    let mut input2 = fixture.create_input("active2", WorkerType::Remote);
    input2.status = Some(WorkerStatus::Active);

    let mut input3 = fixture.create_input("busy", WorkerType::Container);
    input3.status = Some(WorkerStatus::Busy);

    let created1 = WorkerRepository::create(&pool, input1)
        .await
        .expect("Failed to create active worker 1");
    let created2 = WorkerRepository::create(&pool, input2)
        .await
        .expect("Failed to create active worker 2");
    let _created3 = WorkerRepository::create(&pool, input3)
        .await
        .expect("Failed to create busy worker");

    let active_workers = WorkerRepository::find_by_status(&pool, WorkerStatus::Active)
        .await
        .expect("Failed to find by status");

    assert!(active_workers.iter().any(|w| w.id == created1.id));
    assert!(active_workers.iter().any(|w| w.id == created2.id));
    assert!(active_workers
        .iter()
        .all(|w| w.status == Some(WorkerStatus::Active)));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_by_status_all_statuses() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_status_all");

    let statuses = vec![
        WorkerStatus::Active,
        WorkerStatus::Inactive,
        WorkerStatus::Busy,
        WorkerStatus::Error,
    ];

    for status in &statuses {
        let mut input = fixture.create_input(&format!("{:?}", status), WorkerType::Local);
        input.status = Some(*status);

        let created = WorkerRepository::create(&pool, input)
            .await
            .expect("Failed to create worker");

        let found = WorkerRepository::find_by_status(&pool, *status)
            .await
            .expect("Failed to find by status");

        assert!(found.iter().any(|w| w.id == created.id));
    }
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_by_type_local() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_type_local");

    let input1 = fixture.create_input("local1", WorkerType::Local);
    let input2 = fixture.create_input("local2", WorkerType::Local);
    let input3 = fixture.create_input("remote", WorkerType::Remote);

    let created1 = WorkerRepository::create(&pool, input1)
        .await
        .expect("Failed to create local worker 1");
    let created2 = WorkerRepository::create(&pool, input2)
        .await
        .expect("Failed to create local worker 2");
    let _created3 = WorkerRepository::create(&pool, input3)
        .await
        .expect("Failed to create remote worker");

    let local_workers = WorkerRepository::find_by_type(&pool, WorkerType::Local)
        .await
        .expect("Failed to find by type");

    assert!(local_workers.iter().any(|w| w.id == created1.id));
    assert!(local_workers.iter().any(|w| w.id == created2.id));
    assert!(local_workers
        .iter()
        .all(|w| w.worker_type == WorkerType::Local));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_by_type_all_types() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("find_by_type_all");

    let types = vec![WorkerType::Local, WorkerType::Remote, WorkerType::Container];

    for worker_type in &types {
        let input = fixture.create_input(&format!("{:?}", worker_type), *worker_type);

        let created = WorkerRepository::create(&pool, input)
            .await
            .expect("Failed to create worker");

        let found = WorkerRepository::find_by_type(&pool, *worker_type)
            .await
            .expect("Failed to find by type");

        assert!(found.iter().any(|w| w.id == created.id));
        assert!(found.iter().all(|w| w.worker_type == *worker_type));
    }
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_heartbeat() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("update_heartbeat");
    let input = fixture.create_input("heartbeat", WorkerType::Local);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.last_heartbeat, None);

    let before = chrono::Utc::now();
    WorkerRepository::update_heartbeat(&pool, worker.id)
        .await
        .expect("Failed to update heartbeat");
    let after = chrono::Utc::now();

    let updated = WorkerRepository::find_by_id(&pool, worker.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found");

    assert!(updated.last_heartbeat.is_some());
    let heartbeat = updated.last_heartbeat.unwrap();
    assert!(heartbeat >= before);
    assert!(heartbeat <= after);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_heartbeat_multiple_times() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("heartbeat_multiple");
    let input = fixture.create_input("multi", WorkerType::Remote);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    WorkerRepository::update_heartbeat(&pool, worker.id)
        .await
        .expect("Failed to update heartbeat 1");

    let first = WorkerRepository::find_by_id(&pool, worker.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found")
        .last_heartbeat
        .unwrap();

    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    WorkerRepository::update_heartbeat(&pool, worker.id)
        .await
        .expect("Failed to update heartbeat 2");

    let second = WorkerRepository::find_by_id(&pool, worker.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found")
        .last_heartbeat
        .unwrap();

    assert!(second > first);
}

// ============================================================================
// Runtime Association Tests
// ============================================================================

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_with_runtime() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("with_runtime");

    // Create a runtime first
    let runtime_input = CreateRuntimeInput {
        r#ref: format!("{}.test_runtime", fixture.test_id),
        pack: None,
        pack_ref: None,
        description: Some("Test runtime".to_string()),
        name: "test_runtime".to_string(),
        aliases: vec![],
        distributions: json!({}),
        installation: None,
        execution_config: json!({
            "interpreter": {
                "binary": "/bin/bash",
                "args": [],
                "file_extension": ".sh"
            }
        }),
        auto_detected: false,
        detection_config: json!({}),
    };

    let runtime = RuntimeRepository::create(&pool, runtime_input)
        .await
        .expect("Failed to create runtime");

    // Create worker with runtime association
    let mut input = fixture.create_input("with_rt", WorkerType::Local);
    input.runtime = Some(runtime.id);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.runtime, Some(runtime.id));

    let found = WorkerRepository::find_by_id(&pool, worker.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found");

    assert_eq!(found.runtime, Some(runtime.id));
}

// ============================================================================
// Enum Tests
// ============================================================================

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_type_local() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("type_local");
    let input = fixture.create_input("local", WorkerType::Local);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.worker_type, WorkerType::Local);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_type_remote() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("type_remote");
    let input = fixture.create_input("remote", WorkerType::Remote);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.worker_type, WorkerType::Remote);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_type_container() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("type_container");
    let input = fixture.create_input("container", WorkerType::Container);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.worker_type, WorkerType::Container);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_status_active() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("status_active");
    let mut input = fixture.create_input("active", WorkerType::Local);
    input.status = Some(WorkerStatus::Active);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, Some(WorkerStatus::Active));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_status_inactive() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("status_inactive");
    let mut input = fixture.create_input("inactive", WorkerType::Local);
    input.status = Some(WorkerStatus::Inactive);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, Some(WorkerStatus::Inactive));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_status_busy() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("status_busy");
    let mut input = fixture.create_input("busy", WorkerType::Local);
    input.status = Some(WorkerStatus::Busy);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, Some(WorkerStatus::Busy));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_worker_status_error() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("status_error");
    let mut input = fixture.create_input("error", WorkerType::Local);
    input.status = Some(WorkerStatus::Error);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, Some(WorkerStatus::Error));
}

// ============================================================================
// Edge Cases and Constraints
// ============================================================================

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_duplicate_name_allowed() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("duplicate_name");

    // Use a fixed name for both workers
    let name = format!("{}_duplicate", fixture.test_id);

    let mut input1 = fixture.create_input("dup1", WorkerType::Local);
    input1.name = name.clone();

    let mut input2 = fixture.create_input("dup2", WorkerType::Remote);
    input2.name = name.clone();

    let worker1 = WorkerRepository::create(&pool, input1)
        .await
        .expect("Failed to create first worker");

    let worker2 = WorkerRepository::create(&pool, input2)
        .await
        .expect("Failed to create second worker with same name");

    assert_eq!(worker1.name, worker2.name);
    assert_ne!(worker1.id, worker2.id);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_json_fields() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("json_fields");
    let input = fixture.create_input("json", WorkerType::Container);

    let worker = WorkerRepository::create(&pool, input.clone())
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.capabilities, input.capabilities);
    assert_eq!(worker.meta, input.meta);

    // Verify JSON structure
    let caps = worker.capabilities.unwrap();
    assert_eq!(caps["cpu"], json!("x86_64"));
    assert_eq!(caps["memory"], json!("8GB"));
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_null_json_fields() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("null_json");
    let input = fixture.create_minimal_input("nulljson");

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.capabilities, None);
    assert_eq!(worker.meta, None);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_null_status() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("null_status");
    let mut input = fixture.create_input("nostatus", WorkerType::Local);
    input.status = None;

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, None);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_ordering() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("list_ordering");

    let mut input1 = fixture.create_input("z", WorkerType::Local);
    input1.name = format!("{}_zzz_worker", fixture.test_id);

    let mut input2 = fixture.create_input("a", WorkerType::Remote);
    input2.name = format!("{}_aaa_worker", fixture.test_id);

    let mut input3 = fixture.create_input("m", WorkerType::Container);
    input3.name = format!("{}_mmm_worker", fixture.test_id);

    WorkerRepository::create(&pool, input1)
        .await
        .expect("Failed to create worker 1");
    WorkerRepository::create(&pool, input2)
        .await
        .expect("Failed to create worker 2");
    WorkerRepository::create(&pool, input3)
        .await
        .expect("Failed to create worker 3");

    let list = WorkerRepository::list(&pool)
        .await
        .expect("Failed to list workers");

    // Find our test workers in the list
    let test_workers: Vec<_> = list
        .iter()
        .filter(|w| w.name.contains(&fixture.test_id))
        .collect();

    assert_eq!(test_workers.len(), 3);

    // Verify they are sorted by name
    for i in 0..test_workers.len() - 1 {
        assert!(test_workers[i].name <= test_workers[i + 1].name);
    }
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_timestamps() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("timestamps");
    let input = fixture.create_input("time", WorkerType::Local);

    let before = chrono::Utc::now();
    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");
    let after = chrono::Utc::now();

    assert!(worker.created >= before);
    assert!(worker.created <= after);
    assert!(worker.updated >= before);
    assert!(worker.updated <= after);
    assert_eq!(worker.created, worker.updated);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_changes_timestamp() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("timestamp_update");
    let input = fixture.create_input("ts", WorkerType::Remote);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    let update_input = UpdateWorkerInput {
        status: Some(WorkerStatus::Busy),
        ..Default::default()
    };

    let updated = WorkerRepository::update(&pool, worker.id, update_input)
        .await
        .expect("Failed to update worker");

    assert_eq!(updated.created, worker.created);
    assert!(updated.updated > worker.updated);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_heartbeat_updates_timestamp() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("heartbeat_updates");
    let input = fixture.create_input("hb", WorkerType::Container);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    let original_updated = worker.updated;

    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    WorkerRepository::update_heartbeat(&pool, worker.id)
        .await
        .expect("Failed to update heartbeat");

    let after_heartbeat = WorkerRepository::find_by_id(&pool, worker.id)
        .await
        .expect("Failed to find worker")
        .expect("Worker not found");

    // Heartbeat should update both last_heartbeat and updated timestamp (due to trigger)
    assert!(after_heartbeat.last_heartbeat.is_some());
    assert!(after_heartbeat.updated > original_updated);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_port_range() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("port_range");

    // Test various port numbers
    let ports = vec![1, 80, 443, 8080, 65535];

    for port in ports {
        let mut input = fixture.create_input(&format!("port{}", port), WorkerType::Local);
        input.port = Some(port);

        let worker = WorkerRepository::create(&pool, input)
            .await
            .unwrap_or_else(|_| panic!("Failed to create worker with port {}", port));

        assert_eq!(worker.port, Some(port));
    }
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_status_lifecycle() {
    let pool = setup_db().await;
    let fixture = WorkerFixture::new("status_lifecycle");
    let mut input = fixture.create_input("lifecycle", WorkerType::Local);
    input.status = Some(WorkerStatus::Inactive);

    let worker = WorkerRepository::create(&pool, input)
        .await
        .expect("Failed to create worker");

    assert_eq!(worker.status, Some(WorkerStatus::Inactive));

    // Transition to Active
    let update1 = UpdateWorkerInput {
        status: Some(WorkerStatus::Active),
        ..Default::default()
    };
    let worker = WorkerRepository::update(&pool, worker.id, update1)
        .await
        .expect("Failed to update to Active");
    assert_eq!(worker.status, Some(WorkerStatus::Active));

    // Transition to Busy
    let update2 = UpdateWorkerInput {
        status: Some(WorkerStatus::Busy),
        ..Default::default()
    };
    let worker = WorkerRepository::update(&pool, worker.id, update2)
        .await
        .expect("Failed to update to Busy");
    assert_eq!(worker.status, Some(WorkerStatus::Busy));

    // Transition to Error
    let update3 = UpdateWorkerInput {
        status: Some(WorkerStatus::Error),
        ..Default::default()
    };
    let worker = WorkerRepository::update(&pool, worker.id, update3)
        .await
        .expect("Failed to update to Error");
    assert_eq!(worker.status, Some(WorkerStatus::Error));

    // Back to Inactive
    let update4 = UpdateWorkerInput {
        status: Some(WorkerStatus::Inactive),
        ..Default::default()
    };
    let worker = WorkerRepository::update(&pool, worker.id, update4)
        .await
        .expect("Failed to update back to Inactive");
    assert_eq!(worker.status, Some(WorkerStatus::Inactive));
}
