package nextgentasks

import (
	"sync"
	"testing"
	"time"

	"go.uber.org/zap"
)

func TestDeltaLakeLockConcurrency(t *testing.T) {
	logger := zap.NewNop()
	InitGlobalDeltaLakeLock(logger)
	lock := GetGlobalDeltaLakeLock()

	var wg sync.WaitGroup
	results := make([]string, 0, 4)
	resultsMutex := sync.Mutex{}

	for i := 0; i < 2; i++ {
		wg.Add(1)
		go func(taskID int) {
			defer wg.Done()

			taskName := "task" + string(rune('A'+taskID))
			lock.Lock(taskName)
			defer lock.Unlock(taskName)

			resultsMutex.Lock()
			results = append(results, "start "+taskName)
			resultsMutex.Unlock()

			time.Sleep(100 * time.Millisecond)

			resultsMutex.Lock()
			results = append(results, "end "+taskName)
			resultsMutex.Unlock()
		}(i)
	}

	wg.Wait()

	if len(results) != 4 {
		t.Errorf("Expected 4 results, got %d", len(results))
	}

	if results[0] == "start taskA" && results[1] == "end taskA" {

		if results[2] != "start taskB" || results[3] != "end taskB" {
			t.Error("Tasks should execute serially")
		}
	} else if results[0] == "start taskB" && results[1] == "end taskB" {

		if results[2] != "start taskA" || results[3] != "end taskA" {
			t.Error("Tasks should execute serially")
		}
	} else {
		t.Error("Tasks should not interleave")
	}

	t.Logf("Execution order: %v", results)
}

func TestDeltaLakeLockTimeout(t *testing.T) {
	logger := zap.NewNop()
	InitGlobalDeltaLakeLock(logger)
	lock := GetGlobalDeltaLakeLock()

	go func() {
		lock.Lock("background_task")
		defer lock.Unlock("background_task")
		time.Sleep(200 * time.Millisecond)
	}()

	time.Sleep(50 * time.Millisecond)

	start := time.Now()
	lock.Lock("main_task")
	duration := time.Since(start)
	lock.Unlock("main_task")

	if duration < 100*time.Millisecond {
		t.Errorf("Lock should have been held for at least 100ms, got %v", duration)
	}
	if duration > 300*time.Millisecond {
		t.Errorf("Lock held too long, got %v", duration)
	}

	t.Logf("Lock wait time: %v", duration)
}

func TestRealWorldScenario(t *testing.T) {
	logger := zap.NewNop()
	InitGlobalDeltaLakeLock(logger)
	lock := GetGlobalDeltaLakeLock()

	var wg sync.WaitGroup
	execOrder := make([]string, 0, 4)
	orderMutex := sync.Mutex{}

	wg.Add(1)
	go func() {
		defer wg.Done()

		lock.Lock("NextGen Metering")
		defer lock.Unlock("NextGen Metering")

		orderMutex.Lock()
		execOrder = append(execOrder, "NextGen Metering Start")
		orderMutex.Unlock()

		time.Sleep(150 * time.Millisecond)

		orderMutex.Lock()
		execOrder = append(execOrder, "NextGen Metering End")
		orderMutex.Unlock()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()

		lock.Lock("NextGen Reporter")
		defer lock.Unlock("NextGen Reporter")

		orderMutex.Lock()
		execOrder = append(execOrder, "NextGen Reporter Start")
		orderMutex.Unlock()

		time.Sleep(100 * time.Millisecond)

		orderMutex.Lock()
		execOrder = append(execOrder, "NextGen Reporter End")
		orderMutex.Unlock()
	}()

	wg.Wait()

	if len(execOrder) != 4 {
		t.Errorf("Expected 4 events, got %d", len(execOrder))
	}

	if (execOrder[0] == "NextGen Metering Start" && execOrder[1] == "NextGen Metering End") ||
		(execOrder[0] == "NextGen Reporter Start" && execOrder[1] == "NextGen Reporter End") {
		t.Logf("Tasks executed serially as expected: %v", execOrder)
	} else {
		t.Errorf("Tasks should not interleave: %v", execOrder)
	}
}
