package integration

import (
	"context"
	"net/http"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestIntegrationTestSetup(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test")
	}

	setup := SetupIntegrationTest(t)
	defer func() {
		if setup.DB != nil {
			setup.DB.Close()
		}
	}()

	assert.NotNil(t, setup.DB)
	assert.NotNil(t, setup.Router)
	assert.NotNil(t, setup.Config)
}

func TestSetupIntegrationDB(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test")
	}

	db := SetupIntegrationDB(t)
	defer db.Close()

	// Vérifier que la connexion fonctionne
	err := db.Ping()
	assert.NoError(t, err)

	// Vérifier que GORM fonctionne
	assert.NotNil(t, db.GormDB)
}

func TestTestClient(t *testing.T) {
	router := gin.New()
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"ok": true})
	})

	client := NewTestClient(router)
	defer client.Close()

	resp, err := client.Get("/test")
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, 200, resp.StatusCode)
}

func TestTestClient_Get(t *testing.T) {
	router := gin.New()
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "success"})
	})

	client := NewTestClient(router)
	defer client.Close()

	resp, err := client.Get("/test")
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, http.StatusOK, resp.StatusCode)
	assert.Equal(t, "application/json; charset=utf-8", resp.Header.Get("Content-Type"))
}

func TestTestClient_Post(t *testing.T) {
	router := gin.New()
	router.POST("/test", func(c *gin.Context) {
		c.JSON(201, gin.H{"created": true})
	})

	client := NewTestClient(router)
	defer client.Close()

	resp, err := client.Post("/test", "application/json", nil)
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, http.StatusCreated, resp.StatusCode)
}

func TestTestClient_Put(t *testing.T) {
	router := gin.New()
	router.PUT("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"updated": true})
	})

	client := NewTestClient(router)
	defer client.Close()

	resp, err := client.Put("/test", "application/json", nil)
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestTestClient_Delete(t *testing.T) {
	router := gin.New()
	router.DELETE("/test", func(c *gin.Context) {
		c.Status(204)
	})

	client := NewTestClient(router)
	defer client.Close()

	resp, err := client.Delete("/test")
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, http.StatusNoContent, resp.StatusCode)
}

func TestTestClient_GetWithContext(t *testing.T) {
	router := gin.New()
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"ok": true})
	})

	client := NewTestClient(router)
	defer client.Close()

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

	resp, err := client.GetWithContext(ctx, "/test")
	require.NoError(t, err)
	defer resp.Body.Close()

	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestTestClient_Close(t *testing.T) {
	router := gin.New()
	router.GET("/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"ok": true})
	})

	client := NewTestClient(router)

	// Vérifier que le serveur répond
	resp, err := client.Get("/test")
	require.NoError(t, err)
	resp.Body.Close()

	// Fermer le client
	client.Close()

	// Après fermeture, les requêtes devraient échouer
	_, err = client.Get("/test")
	assert.Error(t, err)
}

func TestGetTestDatabaseURL(t *testing.T) {
	// Test avec variable d'environnement non définie (valeur par défaut)
	url := GetTestDatabaseURL()
	assert.NotEmpty(t, url)
	assert.Contains(t, url, "postgresql://")

	// Note: On ne peut pas facilement tester avec une variable d'environnement
	// car elle affecterait d'autres tests, mais on vérifie au moins la valeur par défaut
}

func TestCleanupDatabase(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test")
	}

	db := SetupIntegrationDB(t)
	defer db.Close()

	// CleanupDatabase ne devrait pas planter même sur une base vide
	CleanupDatabase(t, db)

	// Vérifier que la connexion fonctionne toujours
	err := db.Ping()
	assert.NoError(t, err)
}

func TestSetupIntegrationTest_SkipShort(t *testing.T) {
	// Ce test doit être exécuté avec -short pour vérifier le skip
	originalShort := testing.Short()
	// Note: On ne peut pas forcer testing.Short() à true, donc on teste le cas normal

	setup := SetupIntegrationTest(t)
	if originalShort {
		// Si en mode short, setup devrait être nil ou le test skip
		return
	}

	defer func() {
		if setup.DB != nil {
			setup.DB.Close()
		}
	}()

	// En mode normal, setup devrait être valide
	assert.NotNil(t, setup)
}

func TestTestClient_Timeout(t *testing.T) {
	router := gin.New()
	router.GET("/slow", func(c *gin.Context) {
		time.Sleep(2 * time.Second)
		c.JSON(200, gin.H{"ok": true})
	})

	client := NewTestClient(router)
	client.client.Timeout = 1 * time.Second // Timeout plus court que la réponse
	defer client.Close()

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

	// La requête devrait timeout
	_, err := client.GetWithContext(ctx, "/slow")
	assert.Error(t, err)
}
