package workers

import (
	"context"
	"testing"
	"time"

	"veza-backend-api/internal/email"
	"veza-backend-api/internal/services"

	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupTestJobWorker(t *testing.T) (*JobWorker, *gorm.DB) {
	// Base de données de test en mémoire
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to open test database: %v", err)
	}

	// Auto-migrate the Job struct for tests
	if err := db.AutoMigrate(&Job{}); err != nil {
		t.Fatalf("Failed to migrate test database: %v", err)
	}

	logger := zap.NewNop() // Use Nop for tests to avoid noise
	jobService := services.NewJobService(logger)

	// Config SMTP de test (mock)
	smtpConfig := email.SMTPConfig{
		Host:     "localhost",
		Port:     "1025",
		Username: "test",
		Password: "test",
		From:     "test@example.com",
	}
	emailSender := email.NewSMTPEmailSender(smtpConfig, logger)

	worker := NewJobWorker(
		db,
		jobService,
		logger,
		10, // queueSize (ignored)
		1,  // workers
		3,  // maxRetries
		emailSender,
	)

	return worker, db
}

func TestJobWorker_Enqueue(t *testing.T) {
	worker, _ := setupTestJobWorker(t)

	job := Job{
		Type:     "email",
		Priority: 2,
		Payload: map[string]interface{}{
			"to":      "test@example.com",
			"subject": "Test",
			"body":    "Test body",
		},
	}

	worker.Enqueue(job)

	stats := worker.GetStats()
	queueSize := stats["queue_pending"].(int64)
	if queueSize != 1 {
		t.Errorf("Expected queue pending to be 1, got %d", queueSize)
	}
}

func TestJobWorker_EnqueueEmailJob(t *testing.T) {
	worker, _ := setupTestJobWorker(t)

	worker.EnqueueEmailJob("test@example.com", "Test Subject", "Test Body")

	stats := worker.GetStats()
	queueSize := stats["queue_pending"].(int64)
	if queueSize != 1 {
		t.Errorf("Expected queue pending to be 1, got %d", queueSize)
	}
}

func TestJobWorker_EnqueueEmailJobWithTemplate(t *testing.T) {
	worker, _ := setupTestJobWorker(t)

	templateData := map[string]interface{}{
		"Username": "testuser",
		"ResetURL": "http://localhost/reset?token=abc123",
	}

	worker.EnqueueEmailJobWithTemplate(
		"test@example.com",
		"Reset Password",
		"password_reset",
		templateData,
	)

	stats := worker.GetStats()
	queueSize := stats["queue_pending"].(int64)
	if queueSize != 1 {
		t.Errorf("Expected queue pending to be 1, got %d", queueSize)
	}
}

func TestJobWorker_Start(t *testing.T) {
	worker, _ := setupTestJobWorker(t)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Démarrer le worker
	worker.Start(ctx)

	// Enqueue un job
	worker.EnqueueEmailJob("test@example.com", "Test", "Body")

	// Attendre un peu pour que le worker traite le job (polling interval is 1s, setupTestJobWorker uses real NewJobWorker so 1s)
	// We need to override polling interval or wait longer.
	// Or we can modify NewJobWorker to accept config/options but that would change signature again.
	// For test, 1s interval might be slow.
	// Let's modify JobWorker struct locally in test if possible, assuming fields are exported or we add a Setter.
	// They are unexported.
	// We can update pollingInterval via reflection or just wait > 1s.
	// Or we can construct JobWorker manually in setupTestJobWorker if NewJobWorker doesn't allow it.
	// Since NewJobWorker hardcodes 1s, we should wait slightly more than 1s in test if we want to verify processing.
	// Or we just check that it started.

	// Let's modify valid wait time
	worker.pollingInterval = 10 * time.Millisecond // Set shorter interval for test (if allowed, wait, it's unexported in package workers? Yes but test is in package workers)

	// Wait for processing
	time.Sleep(200 * time.Millisecond)

	// Le job devrait être traité (pending 0)
	stats := worker.GetStats()
	pending := stats["queue_pending"].(int64)
	processing := stats["queue_processing"].(int64)
	// It relies on email sending success which might fail with mock?
	// If failed, it might be in pending (retry) or failed.

	t.Logf("Stats: %+v", stats)
	if pending > 0 && processing == 0 {
		t.Log("Job still pending or retrying")
	}
}
