package integration

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

	"veza-backend-api/internal/config"
	"veza-backend-api/internal/database"
	"veza-backend-api/internal/testutils"

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

// IntegrationTestSetup contient les ressources pour un test d'intégration (T0041)
type IntegrationTestSetup struct {
	DB     *database.Database
	Router *gin.Engine
	Config *config.Config
}

// SetupIntegrationDB configure une base de données PostgreSQL pour les tests d'intégration (T0041)
func SetupIntegrationDB(t *testing.T) *database.Database {
	// Utiliser une base de données de test dédiée
	dbURL := testutils.GetTestDatabaseURL()

	dbConfig := &database.Config{
		URL:          dbURL,
		MaxOpenConns: 5,
		MaxIdleConns: 2,
		MaxLifetime:  5 * time.Minute,
		MaxIdleTime:  1 * time.Minute,
	}

	db, err := database.NewDatabase(dbConfig)
	require.NoError(t, err, "Failed to setup integration database")

	// Nettoyer les tables
	testutils.CleanupDatabase(t, db)

	t.Cleanup(func() {
		testutils.CleanupDatabase(t, db)
		if err := db.Close(); err != nil {
			t.Logf("Error closing database: %v", err)
		}
	})

	return db
}

// SetupIntegrationTest configure un environnement de test complet (T0041)
func SetupIntegrationTest(t *testing.T) *IntegrationTestSetup {
	// Skip si mode short
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Setup database
	db := SetupIntegrationDB(t)

	// Setup config avec valeurs de test
	testConfig := config.NewTestConfig(t)
	testConfig.Database = db

	// Setup router
	gin.SetMode(gin.TestMode)
	router := gin.New()

	// Note: routes.SetupRoutes nécessite des services complets
	// Pour les tests d'intégration, on peut créer un router minimal
	// ou utiliser routes.SetupRoutes si tous les services sont configurés
	// routes.SetupRoutes(router, ...)

	return &IntegrationTestSetup{
		DB:     db,
		Router: router,
		Config: testConfig,
	}
}

// TestClient simplifie les appels HTTP dans les tests (T0041)
type TestClient struct {
	server *httptest.Server
	client *http.Client
}

// NewTestClient crée un nouveau client de test (T0041)
func NewTestClient(router *gin.Engine) *TestClient {
	server := httptest.NewServer(router)
	return &TestClient{
		server: server,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// Get fait une requête GET (T0041)
func (c *TestClient) Get(path string) (*http.Response, error) {
	return c.client.Get(c.server.URL + path)
}

// GetWithContext fait une requête GET avec contexte (T0041)
func (c *TestClient) GetWithContext(ctx context.Context, path string) (*http.Response, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.server.URL+path, nil)
	if err != nil {
		return nil, err
	}
	return c.client.Do(req)
}

// Post fait une requête POST (T0041)
func (c *TestClient) Post(path, contentType string, body []byte) (*http.Response, error) {
	return c.client.Post(c.server.URL+path, contentType, http.NoBody)
}

// PostWithBody fait une requête POST avec body (T0041)
func (c *TestClient) PostWithBody(path, contentType string, body []byte) (*http.Response, error) {
	req, err := http.NewRequest(http.MethodPost, c.server.URL+path, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	return c.client.Do(req)
}

// PostWithContext fait une requête POST avec contexte et body (T0041)
func (c *TestClient) PostWithContext(ctx context.Context, path, contentType string, body []byte) (*http.Response, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.server.URL+path, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	return c.client.Do(req)
}

// Put fait une requête PUT (T0041)
func (c *TestClient) Put(path, contentType string, body []byte) (*http.Response, error) {
	req, err := http.NewRequest(http.MethodPut, c.server.URL+path, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	return c.client.Do(req)
}

// Delete fait une requête DELETE (T0041)
func (c *TestClient) Delete(path string) (*http.Response, error) {
	req, err := http.NewRequest(http.MethodDelete, c.server.URL+path, nil)
	if err != nil {
		return nil, err
	}
	return c.client.Do(req)
}

// Close ferme le serveur de test (T0041)
func (c *TestClient) Close() {
	c.server.Close()
}
