//go:build integration
// +build integration

package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/repositories"
	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// setupProfileIntegrationTestRouter crée un router de test pour les tests de profile
// BE-SEC-001: Tests d'intégration pour vérification ownership
func setupProfileIntegrationTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, func()) {
	gin.SetMode(gin.TestMode)

	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Enable foreign keys for SQLite
	db.Exec("PRAGMA foreign_keys = ON")

	// Auto-migrate - include all models needed for PermissionService
	err = db.AutoMigrate(
		&models.User{},
		&models.Role{},
		&models.Permission{},
		&models.UserRole{},
		&models.RolePermission{},
	)
	require.NoError(t, err)

	// Setup logger
	logger := zap.NewNop()

	// Setup services
	userRepo := repositories.NewGormUserRepository(db)
	userService := services.NewUserServiceWithDB(userRepo, db)
	profileHandler := NewProfileHandler(userService, logger)

	// Setup PermissionService for admin checks
	permissionService := services.NewPermissionService(db)
	profileHandler.SetPermissionService(permissionService)

	// Create router
	router := gin.New()
	v1 := router.Group("/api/v1")
	{
		// Protected routes with mock auth middleware
		protected := v1.Group("/users")
		protected.Use(func(c *gin.Context) {
			// Mock auth middleware - set user_id from header
			// This simulates RequireAuth() middleware
			userIDStr := c.GetHeader("X-User-ID")
			if userIDStr == "" {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
				c.Abort()
				return
			}
			uid, err := uuid.Parse(userIDStr)
			if err != nil {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid user id"})
				c.Abort()
				return
			}
			c.Set("user_id", uid)
			c.Next()
		})
		{
			// Note: In real router, RequireOwnershipOrAdmin middleware would be here
			// For this test, we're testing the handler's ownership check directly
			protected.PUT("/:id", profileHandler.UpdateProfile)
		}
	}

	cleanup := func() {
		// Database will be closed automatically
	}

	return router, db, cleanup
}

// createTestUser crée un utilisateur de test
func createTestUserForProfile(t *testing.T, db *gorm.DB, userID uuid.UUID, username string, isAdmin bool) *models.User {
	user := &models.User{
		ID:           userID,
		Username:     username,
		Slug:         username,
		Email:        username + "@example.com",
		PasswordHash: "hashed_password",
		IsActive:     true,
		CreatedAt:    time.Now(),
	}
	if isAdmin {
		user.Role = "admin"
		user.IsAdmin = true
	} else {
		user.Role = "user"
	}
	// Create user first
	err := db.Create(user).Error
	require.NoError(t, err)

	// Then create admin role and assign it if admin
	if isAdmin {
		// Create admin role and assign it to user
		adminRole := &models.Role{
			Name:        "admin",
			DisplayName: "Administrator",
			IsSystem:    true,
			IsActive:    true,
		}
		err = db.FirstOrCreate(adminRole, models.Role{Name: "admin"}).Error
		require.NoError(t, err)

		userRole := &models.UserRole{
			UserID:   userID,
			RoleID:   adminRole.ID,
			RoleName: "admin",
			IsActive: true,
		}
		err = db.Create(userRole).Error
		require.NoError(t, err)
	}
	return user
}

// BE-SEC-001: Test that user cannot update another user's profile
func TestUpdateProfile_UserCannotUpdateOtherUserProfile(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	router, db, cleanup := setupProfileIntegrationTestRouter(t)
	defer cleanup()

	// Create two users
	ownerID := uuid.New()
	otherUserID := uuid.New()
	createTestUserForProfile(t, db, ownerID, "owner", false)
	createTestUserForProfile(t, db, otherUserID, "otheruser", false)

	// Try to update owner's profile as otherUser
	updateReq := UpdateProfileRequest{
		FirstName: "Hacked",
		Bio:       "I shouldn't be able to do this",
	}
	body, _ := json.Marshal(updateReq)

	req := httptest.NewRequest(http.MethodPut, "/api/v1/users/"+ownerID.String(), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", otherUserID.String()) // otherUser is authenticated

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Should return 403 Forbidden
	assert.Equal(t, http.StatusForbidden, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	// Error can be a string or a map, check both
	if errorStr, ok := response["error"].(string); ok {
		assert.Contains(t, errorStr, "cannot update other user's profile")
	} else if errorMap, ok := response["error"].(map[string]interface{}); ok {
		if message, ok := errorMap["message"].(string); ok {
			assert.Contains(t, message, "cannot update other user's profile")
		}
	}
}

// BE-SEC-001: Test that admin can update any profile
func TestUpdateProfile_AdminCanUpdateAnyProfile(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	router, db, cleanup := setupProfileIntegrationTestRouter(t)
	defer cleanup()

	// Create two users: owner and admin
	ownerID := uuid.New()
	adminID := uuid.New()
	createTestUserForProfile(t, db, ownerID, "owner", false)
	createTestUserForProfile(t, db, adminID, "admin", true)

	// Admin tries to update owner's profile
	updateReq := UpdateProfileRequest{
		FirstName: "Updated by Admin",
		Bio:       "Admin updated this profile",
	}
	body, _ := json.Marshal(updateReq)

	req := httptest.NewRequest(http.MethodPut, "/api/v1/users/"+ownerID.String(), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", adminID.String()) // admin is authenticated

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Admin should be able to update
	assert.Equal(t, http.StatusOK, w.Code)

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

// BE-SEC-001: Test that user can update their own profile
func TestUpdateProfile_UserCanUpdateOwnProfile(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	router, db, cleanup := setupProfileIntegrationTestRouter(t)
	defer cleanup()

	// Create a user
	userID := uuid.New()
	createTestUserForProfile(t, db, userID, "testuser", false)

	// User tries to update their own profile
	updateReq := UpdateProfileRequest{
		FirstName: "Updated First Name",
		Bio:       "Updated bio",
	}
	body, _ := json.Marshal(updateReq)

	req := httptest.NewRequest(http.MethodPut, "/api/v1/users/"+userID.String(), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String()) // user is authenticated

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// User should be able to update their own profile
	assert.Equal(t, http.StatusOK, w.Code)

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

	// Verify the profile was actually updated
	profileData := response["data"].(map[string]interface{})
	profile := profileData["profile"].(map[string]interface{})
	assert.Equal(t, "Updated First Name", profile["first_name"])
}
