package approval

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestNewApprovalGateManager(t *testing.T) {
	manager := NewApprovalGateManager()
	assert.NotNil(t, manager)
	assert.Equal(t, 0, manager.stats.TotalGates)
}

func TestCreateGate(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	assert.NotNil(t, gate)
	assert.Equal(t, "T0.1", gate.TaskID)
	assert.Equal(t, StatusPending, gate.Decision.Status)
}

func TestApproveGate(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	err := manager.ApproveGate(gate.ID, "reviewer-1", "Approved")
	assert.NoError(t, err)
	assert.True(t, manager.IsApproved(gate.ID))
}

func TestRejectGate(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	err := manager.RejectGate(gate.ID, "reviewer-1", "Rejected")
	assert.NoError(t, err)
	assert.True(t, manager.IsRejected(gate.ID))
}

func TestGetPendingGates(t *testing.T) {
	manager := NewApprovalGateManager()
	gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	gate2 := manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour)
	
	manager.ApproveGate(gate1.ID, "reviewer-1", "")
	
	pending := manager.GetPendingGates()
	assert.Equal(t, 1, len(pending))
	if len(pending) > 0 {
		assert.Equal(t, gate2.ID, pending[0].ID)
	}
}

func TestGetGatesByTaskFiltering(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	
	gates := manager.GetGatesByTask("T0.1")
	assert.Equal(t, 1, len(gates))
	assert.Equal(t, gate.ID, gates[0].ID)
}

func TestGetGatesByWorkflowFiltering(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	
	gates := manager.GetGatesByWorkflow("workflow-1")
	assert.Equal(t, 1, len(gates))
	assert.Equal(t, gate.ID, gates[0].ID)
}

func TestIsApproved(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	assert.False(t, manager.IsApproved(gate.ID))
	
	manager.ApproveGate(gate.ID, "reviewer-1", "")
	assert.True(t, manager.IsApproved(gate.ID))
}

func TestIsRejected(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	assert.False(t, manager.IsRejected(gate.ID))
	
	manager.RejectGate(gate.ID, "reviewer-1", "")
	assert.True(t, manager.IsRejected(gate.ID))
}

func TestIsPending(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	assert.True(t, manager.IsPending(gate.ID))
	
	manager.ApproveGate(gate.ID, "reviewer-1", "")
	assert.False(t, manager.IsPending(gate.ID))
}

func TestGetStats(t *testing.T) {
	manager := NewApprovalGateManager()
	gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	_ = manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour)
	
	manager.ApproveGate(gate1.ID, "reviewer-1", "")
	
	stats := manager.GetStats()
	assert.Equal(t, 2, stats.TotalGates)
	assert.Equal(t, 1, stats.ApprovedGates)
	assert.Equal(t, 1, stats.PendingGates)
}

func TestGetHistory(t *testing.T) {
	manager := NewApprovalGateManager()
	gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	gate2 := manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour)
	
	manager.ApproveGate(gate1.ID, "reviewer-1", "")
	manager.RejectGate(gate2.ID, "reviewer-2", "Needs work")
	
	history := manager.GetHistory()
	assert.Greater(t, len(history), 0)
}

func TestClear(t *testing.T) {
	manager := NewApprovalGateManager()
	manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	assert.Equal(t, 1, manager.stats.TotalGates)
	
	manager.Clear()
	assert.Equal(t, 0, manager.stats.TotalGates)
}

func TestExpiredGate(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 1*time.Millisecond)
	
	time.Sleep(10 * time.Millisecond)
	
	err := manager.ApproveGate(gate.ID, "reviewer-1", "")
	assert.Error(t, err)
}

func TestMultipleApprovals(t *testing.T) {
	manager := NewApprovalGateManager()
	gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	gate.RequiredApprovals = 2
	
	err := manager.ApproveGate(gate.ID, "reviewer-1", "")
	assert.NoError(t, err)
	
	retrieved, _ := manager.GetGate(gate.ID)
	assert.Equal(t, 1, len(retrieved.Approvals))
}

func BenchmarkCreateGate(b *testing.B) {
	manager := NewApprovalGateManager()
	for i := 0; i < b.N; i++ {
		manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	}
}

func BenchmarkApproveGate(b *testing.B) {
	manager := NewApprovalGateManager()
	gates := make([]*ApprovalGate, b.N)
	for i := 0; i < b.N; i++ {
		gates[i] = manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour)
	}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		manager.ApproveGate(gates[i].ID, "reviewer-1", "")
	}
}
