package updates

import (
	"testing"
	"time"
)

func TestUpdateQueue_Enqueue(t *testing.T) {
	queue := NewUpdateQueue()

	// Test successful enqueue
	job1, accepted := queue.Enqueue("https://example.com/update1.tar.gz")
	if !accepted {
		t.Fatal("First job should be accepted")
	}
	if job1 == nil {
		t.Fatal("Job should not be nil")
	}
	if job1.State != JobStateQueued {
		t.Errorf("Job state should be queued, got %s", job1.State)
	}

	// Test rejection when another job is running
	queue.MarkRunning(job1.ID)
	job2, accepted := queue.Enqueue("https://example.com/update2.tar.gz")
	if accepted {
		t.Fatal("Second job should be rejected when first is running")
	}
	if job2 != nil {
		t.Error("Rejected job should be nil")
	}
}

func TestUpdateQueue_MarkRunning(t *testing.T) {
	queue := NewUpdateQueue()

	job, _ := queue.Enqueue("https://example.com/update.tar.gz")
	if job.State != JobStateQueued {
		t.Errorf("Initial state should be queued, got %s", job.State)
	}

	success := queue.MarkRunning(job.ID)
	if !success {
		t.Fatal("MarkRunning should succeed")
	}

	currentJob := queue.GetCurrentJob()
	if currentJob.State != JobStateRunning {
		t.Errorf("State should be running, got %s", currentJob.State)
	}

	// Test marking wrong job ID
	success = queue.MarkRunning("wrong-id")
	if success {
		t.Error("MarkRunning with wrong ID should fail")
	}
}

func TestUpdateQueue_MarkCompleted(t *testing.T) {
	queue := NewUpdateQueue()

	job, _ := queue.Enqueue("https://example.com/update.tar.gz")
	queue.MarkRunning(job.ID)

	// Test successful completion
	queue.MarkCompleted(job.ID, nil)
	currentJob := queue.GetCurrentJob()
	if currentJob.State != JobStateCompleted {
		t.Errorf("State should be completed, got %s", currentJob.State)
	}
	if currentJob.Error != nil {
		t.Error("Error should be nil for successful completion")
	}

	// Check history
	history := queue.GetHistory()
	if len(history) != 1 {
		t.Errorf("History should contain 1 job, got %d", len(history))
	}
}

func TestUpdateQueue_MarkCompletedWithError(t *testing.T) {
	queue := NewUpdateQueue()

	job, _ := queue.Enqueue("https://example.com/update.tar.gz")
	queue.MarkRunning(job.ID)

	// Test failed completion
	testErr := &testError{"test error"}
	queue.MarkCompleted(job.ID, testErr)

	currentJob := queue.GetCurrentJob()
	if currentJob.State != JobStateFailed {
		t.Errorf("State should be failed, got %s", currentJob.State)
	}
	if currentJob.Error == nil {
		t.Error("Error should not be nil for failed completion")
	}
	if currentJob.Error.Error() != "test error" {
		t.Errorf("Error message should be 'test error', got %s", currentJob.Error.Error())
	}
}

func TestUpdateQueue_Cancel(t *testing.T) {
	queue := NewUpdateQueue()

	job, _ := queue.Enqueue("https://example.com/update.tar.gz")
	queue.MarkRunning(job.ID)

	success := queue.Cancel(job.ID)
	if !success {
		t.Fatal("Cancel should succeed")
	}

	currentJob := queue.GetCurrentJob()
	if currentJob.State != JobStateCancelled {
		t.Errorf("State should be cancelled, got %s", currentJob.State)
	}

	// Verify context was cancelled
	select {
	case <-currentJob.Context.Done():
		// Context was cancelled as expected
	case <-time.After(100 * time.Millisecond):
		t.Error("Context should be cancelled")
	}
}

func TestUpdateQueue_IsRunning(t *testing.T) {
	queue := NewUpdateQueue()

	if queue.IsRunning() {
		t.Error("Queue should not be running initially")
	}

	job, _ := queue.Enqueue("https://example.com/update.tar.gz")
	if !queue.IsRunning() {
		t.Error("Queue should be running after enqueue")
	}

	queue.MarkRunning(job.ID)
	if !queue.IsRunning() {
		t.Error("Queue should be running after marking as running")
	}

	queue.MarkCompleted(job.ID, nil)
	// Note: IsRunning will still return true for a short period after completion
	// This is by design to allow status polling
}

func TestUpdateQueue_History(t *testing.T) {
	queue := NewUpdateQueue()

	// Add multiple jobs
	for i := 0; i < 5; i++ {
		job, _ := queue.Enqueue("https://example.com/update.tar.gz")
		queue.MarkRunning(job.ID)
		queue.MarkCompleted(job.ID, nil)

		// Wait for the job to be cleared from current
		time.Sleep(50 * time.Millisecond)
	}

	history := queue.GetHistory()
	if len(history) != 5 {
		t.Errorf("History should contain 5 jobs, got %d", len(history))
	}

	// Verify history ordering (should be chronological)
	for i := 1; i < len(history); i++ {
		if history[i].StartedAt.Before(history[i-1].StartedAt) {
			t.Error("History should be in chronological order")
		}
	}
}

func TestUpdateQueue_MaxHistory(t *testing.T) {
	queue := NewUpdateQueue()
	queue.maxHistory = 3

	// Add more jobs than maxHistory
	for i := 0; i < 5; i++ {
		job, _ := queue.Enqueue("https://example.com/update.tar.gz")
		queue.MarkRunning(job.ID)
		queue.MarkCompleted(job.ID, nil)

		// Wait for the job to be cleared
		time.Sleep(50 * time.Millisecond)
	}

	history := queue.GetHistory()
	if len(history) > queue.maxHistory {
		t.Errorf("History should be limited to %d jobs, got %d", queue.maxHistory, len(history))
	}
}

// Helper type for testing
type testError struct {
	msg string
}

func (e *testError) Error() string {
	return e.msg
}
