package shutdown

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
)

func TestNewShutdownManager(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)
	assert.NotNil(t, sm)
	assert.Equal(t, 0, len(sm.shutdowners))
	assert.False(t, sm.shuttingDown)
}

func TestShutdownManager_Register(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)

	shutdowner := NewShutdownFunc("test_service", func(ctx context.Context) error {
		return nil
	})

	sm.Register(shutdowner)
	assert.Equal(t, 1, len(sm.shutdowners))
}

func TestShutdownManager_Shutdown_Success(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)

	shutdownCount := 0
	shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
		shutdownCount++
		return nil
	})
	shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
		shutdownCount++
		return nil
	})

	sm.Register(shutdowner1)
	sm.Register(shutdowner2)

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	err := sm.Shutdown(ctx)
	require.NoError(t, err)
	assert.Equal(t, 2, shutdownCount)
}

func TestShutdownManager_Shutdown_WithError(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)

	shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
		return nil
	})
	shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
		return errors.New("shutdown error")
	})

	sm.Register(shutdowner1)
	sm.Register(shutdowner2)

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	err := sm.Shutdown(ctx)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "shutdown completed with 1 errors")
}

func TestShutdownManager_Shutdown_Timeout(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)

	shutdowner := NewShutdownFunc("slow_service", func(ctx context.Context) error {
		// Simuler un service qui prend trop de temps
		time.Sleep(2 * time.Second)
		return nil
	})

	sm.Register(shutdowner)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	err := sm.Shutdown(ctx)
	// Le timeout devrait être atteint
	assert.Error(t, err)
}

func TestShutdownManager_RegisterDuringShutdown(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	sm := NewShutdownManager(logger)

	shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
		return nil
	})

	sm.Register(shutdowner1)

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// Démarrer le shutdown dans une goroutine
	done := make(chan bool)
	go func() {
		sm.Shutdown(ctx)
		done <- true
	}()

	// Essayer d'enregistrer pendant le shutdown
	time.Sleep(10 * time.Millisecond)
	shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
		return nil
	})
	sm.Register(shutdowner2)

	<-done

	// Le deuxième service ne devrait pas être arrêté car enregistré pendant le shutdown
	assert.Equal(t, 1, len(sm.shutdowners))
}

func TestShutdownFunc(t *testing.T) {
	shutdownFunc := NewShutdownFunc("test", func(ctx context.Context) error {
		return nil
	})

	assert.Equal(t, "test", shutdownFunc.Name())

	ctx := context.Background()
	err := shutdownFunc.Shutdown(ctx)
	assert.NoError(t, err)
}
