package daemon

import (
	"context"
	"errors"
	"testing"
	"time"
)

func TestRequestTrackerWaitDeliverAndCleanup(t *testing.T) {
	tracker := NewRequestTracker[int]()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	done := make(chan int, 1)
	go func() {
		result, err := tracker.Wait(ctx, "req-1", nil)
		if err != nil {
			t.Errorf("Wait returned error: %v", err)
			return
		}
		done <- result
	}()
	for tracker.Len() == 0 {
		time.Sleep(time.Millisecond)
	}
	if !tracker.Deliver("req-1", 42) {
		t.Fatal("Deliver returned false")
	}
	select {
	case got := <-done:
		if got != 42 {
			t.Fatalf("Wait result = %d, want 42", got)
		}
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for tracker result")
	}
	for deadline := time.Now().Add(time.Second); tracker.Len() != 0 && time.Now().Before(deadline); {
		time.Sleep(time.Millisecond)
	}
	if got := tracker.Len(); got != 0 {
		t.Fatalf("tracker len after cleanup = %d, want 0", got)
	}
}

func TestRequestTrackerSendErrorCleansUp(t *testing.T) {
	tracker := NewRequestTracker[int]()
	sendErr := errors.New("send failed")
	_, err := tracker.Wait(context.Background(), "req-1", func() error { return sendErr })
	if !errors.Is(err, sendErr) {
		t.Fatalf("Wait error = %v, want %v", err, sendErr)
	}
	if got := tracker.Len(); got != 0 {
		t.Fatalf("tracker len = %d, want 0", got)
	}
}

func TestRequestTrackerFailAll(t *testing.T) {
	tracker := NewRequestTracker[int]()
	ch1, cleanup1, err := tracker.Register("req-1")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup1()
	ch2, cleanup2, err := tracker.Register("req-2")
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup2()
	if got := tracker.FailAll(7); got != 2 {
		t.Fatalf("FailAll delivered %d results, want 2", got)
	}
	if got := <-ch1; got != 7 {
		t.Fatalf("ch1 = %d, want 7", got)
	}
	if got := <-ch2; got != 7 {
		t.Fatalf("ch2 = %d, want 7", got)
	}
}

func TestRequestTrackerWithStoreUsesLegacyMap(t *testing.T) {
	requests := map[string]chan string{}
	tracker := NewRequestTrackerWithStore[string](nil, &requests)
	ch, cleanup, err := tracker.Register("req-1")
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := requests["req-1"]; !ok {
		t.Fatal("legacy map was not populated")
	}
	if !tracker.Deliver("req-1", "ok") {
		t.Fatal("Deliver returned false")
	}
	if got := <-ch; got != "ok" {
		t.Fatalf("delivered %q, want ok", got)
	}
	cleanup()
	if _, ok := requests["req-1"]; ok {
		t.Fatal("legacy map entry was not cleaned up")
	}
}
