package testing

import (
	"bytes"
	"encoding/json"
	"fotbal-club/internal/models"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// SetupTestDB creates an in-memory SQLite database for testing
func SetupTestDB(t *testing.T) *gorm.DB {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	if err != nil {
		t.Fatalf("Failed to create test database: %v", err)
	}

	// Auto-migrate all models
	err = db.AutoMigrate(
		&models.User{},
		&models.Article{},
		&models.Category{},
		&models.Player{},
		&models.Settings{},
		&models.ContactMessage{},
		&models.NewsletterSubscription{},
		&models.Clothing{},
		&models.Poll{},
		&models.PollOption{},
		&models.PollVote{},
	)

	if err != nil {
		t.Fatalf("Failed to migrate test database: %v", err)
	}

	return db
}

// CreateTestUser creates a test user
func CreateTestUser(db *gorm.DB, role string) *models.User {
	user := &models.User{
		Email:     "test@example.com",
		Password:  "$2a$10$test_hashed_password",
		FirstName: "Test",
		LastName:  "User",
		Role:      role,
	}
	db.Create(user)
	return user
}

// CreateTestArticle creates a test article
func CreateTestArticle(db *gorm.DB, authorID uint) *models.Article {
	article := &models.Article{
		Title:     "Test Article",
		Content:   "<p>Test content</p>",
		Slug:      "test-article",
		Published: true,
		AuthorID:  &authorID,
	}
	db.Create(article)
	return article
}

// MakeTestRequest creates a test HTTP request
func MakeTestRequest(method, path string, body interface{}) (*http.Request, error) {
	var reqBody *bytes.Buffer
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return nil, err
		}
		reqBody = bytes.NewBuffer(jsonData)
	} else {
		reqBody = &bytes.Buffer{}
	}

	req, err := http.NewRequest(method, path, reqBody)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	return req, nil
}

// ExecuteRequest executes a test request and returns response
func ExecuteRequest(router *gin.Engine, req *http.Request) *httptest.ResponseRecorder {
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)
	return w
}

// AssertJSONResponse checks JSON response
func AssertJSONResponse(t *testing.T, w *httptest.ResponseRecorder, expectedStatus int, expectedBody map[string]interface{}) {
	assert.Equal(t, expectedStatus, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	for key, expectedValue := range expectedBody {
		assert.Equal(t, expectedValue, response[key], "Mismatch for key: %s", key)
	}
}

// MockGinContext creates a mock Gin context for testing
func MockGinContext() (*gin.Context, *httptest.ResponseRecorder) {
	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	return c, w
}

// CleanupTestDB closes and cleans up test database
func CleanupTestDB(db *gorm.DB) {
	sqlDB, _ := db.DB()
	if sqlDB != nil {
		sqlDB.Close()
	}
}

// SeedTestData populates test database with sample data
func SeedTestData(db *gorm.DB) {
	// Create admin user
	admin := CreateTestUser(db, "admin")

	// Create categories
	categories := []models.Category{
		{Name: "Aktuality", Description: "Club news"},
		{Name: "Zápasy", Description: "Match reports"},
		{Name: "Tréninky", Description: "Training updates"},
	}
	for _, cat := range categories {
		db.Create(&cat)
	}

	// Create articles
	for i := 1; i <= 10; i++ {
		article := &models.Article{
			Title:      "Test Article " + strconv.Itoa(i),
			Content:    "<p>Test content</p>",
			Slug:       "test-article-" + strconv.Itoa(i),
			Published:  true,
			AuthorID:   &admin.ID,
			CategoryID: &[]uint{1}[0],
		}
		db.Create(article)
	}
}

// AssertDatabaseState checks database state
func AssertDatabaseState(t *testing.T, db *gorm.DB, model interface{}, count int64) {
	var actualCount int64
	db.Model(model).Count(&actualCount)
	assert.Equal(t, count, actualCount)
}
