package handlers

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

	"veza-backend-api/internal/core/marketplace"
	"veza-backend-api/internal/models"

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

// MockStorageService is a mock implementation of StorageService
type MockStorageService struct {
	GetDownloadURLFunc func(ctx context.Context, filePath string) (string, error)
}

func (m *MockStorageService) GetDownloadURL(ctx context.Context, filePath string) (string, error) {
	if m.GetDownloadURLFunc != nil {
		return m.GetDownloadURLFunc(ctx, filePath)
	}
	return fmt.Sprintf("/download/%s", filePath), nil
}

// setupTestMarketplaceHandler creates a test handler with real services and in-memory database
func setupTestMarketplaceHandler(t *testing.T) (*MarketplaceHandler, *gorm.DB, *gin.Engine, func()) {
	gin.SetMode(gin.TestMode)
	logger := zaptest.NewLogger(t)

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

	// Auto-migrate models
	err = db.AutoMigrate(
		&models.User{},
		&models.Track{},
		&marketplace.Product{},
		&marketplace.Order{},
		&marketplace.OrderItem{},
		&marketplace.License{},
	)
	require.NoError(t, err)

	// Setup storage service
	storageService := &MockStorageService{}

	// Setup marketplace service
	marketService := marketplace.NewService(db, logger, storageService)

	handler := NewMarketplaceHandler(marketService, logger)

	router := gin.New()
	router.Use(func(c *gin.Context) {
		// Mock auth middleware - set user_id from header if present
		userIDStr := c.GetHeader("X-User-ID")
		if userIDStr != "" {
			uid, err := uuid.Parse(userIDStr)
			if err == nil {
				c.Set("user_id", uid)
			}
		}
		c.Next()
	})

	cleanup := func() {
		// Database cleanup handled by test
	}

	return handler, db, router, cleanup
}

// Helper to create a test user
func createTestUserForMarketplace(id uuid.UUID, username string) *models.User {
	return &models.User{
		ID:         id,
		Username:   username,
		Email:      fmt.Sprintf("%s@example.com", username),
		IsActive:   true,
		IsVerified: true,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
	}
}

// Helper to create a test track
func createTestTrackForMarketplace(id uuid.UUID, userID uuid.UUID) *models.Track {
	return &models.Track{
		ID:        id,
		UserID:    userID,
		Title:     "Test Track",
		Artist:    "Test Artist",
		FilePath:  "/tmp/test-uploads/test.mp3",
		Format:    "mp3",
		FileSize:  1024,
		Duration:  180,
		IsPublic:  true,
		Status:    models.TrackStatusCompleted,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
}

// TestMarketplaceHandler_CreateProduct_Success tests successful product creation
func TestMarketplaceHandler_CreateProduct_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user and track
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	trackID := uuid.New()
	track := createTestTrackForMarketplace(trackID, userID)
	err = db.Create(track).Error
	require.NoError(t, err)

	router.POST("/marketplace/products", handler.CreateProduct)

	createReq := CreateProductRequest{
		Title:       "My Product",
		Description: "A test product",
		Price:       9.99,
		ProductType: "track",
		TrackID:     trackID.String(),
		LicenseType: "standard",
	}
	body, _ := json.Marshal(createReq)
	req := httptest.NewRequest(http.MethodPost, "/marketplace/products", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusCreated, w.Code)
}

// TestMarketplaceHandler_CreateProduct_InvalidTrack tests product creation with invalid track
func TestMarketplaceHandler_CreateProduct_InvalidTrack(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	router.POST("/marketplace/products", handler.CreateProduct)

	createReq := CreateProductRequest{
		Title:       "My Product",
		Description: "A test product",
		Price:       9.99,
		ProductType: "track",
		TrackID:     uuid.New().String(), // Non-existent track
		LicenseType: "standard",
	}
	body, _ := json.Marshal(createReq)
	req := httptest.NewRequest(http.MethodPost, "/marketplace/products", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
}

// TestMarketplaceHandler_ListProducts_Success tests successful product listing
func TestMarketplaceHandler_ListProducts_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	// Create test products
	for i := 0; i < 3; i++ {
		product := &marketplace.Product{
			ID:          uuid.New(),
			SellerID:    userID,
			Title:       fmt.Sprintf("Product %d", i+1),
			Description: "Test product",
			Price:       9.99,
			ProductType: "track",
			Status:      marketplace.ProductStatusActive,
			CreatedAt:   time.Now(),
			UpdatedAt:   time.Now(),
		}
		err = db.Create(product).Error
		require.NoError(t, err)
	}

	router.GET("/marketplace/products", handler.ListProducts)

	req := httptest.NewRequest(http.MethodGet, "/marketplace/products", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response["success"].(bool))
}

// TestMarketplaceHandler_UpdateProduct_Success tests successful product update
func TestMarketplaceHandler_UpdateProduct_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	// Create test product
	productID := uuid.New()
	product := &marketplace.Product{
		ID:          productID,
		SellerID:    userID,
		Title:       "Original Title",
		Description: "Original Description",
		Price:       9.99,
		ProductType: "track",
		Status:      marketplace.ProductStatusActive,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	err = db.Create(product).Error
	require.NoError(t, err)

	router.PUT("/marketplace/products/:id", handler.UpdateProduct)

	title := "Updated Title"
	updateReq := UpdateProductRequest{
		Title: &title,
	}
	body, _ := json.Marshal(updateReq)
	req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/marketplace/products/%s", productID.String()), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response["success"].(bool))
}

// TestMarketplaceHandler_UpdateProduct_NotFound tests product update with non-existent product
func TestMarketplaceHandler_UpdateProduct_NotFound(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	router.PUT("/marketplace/products/:id", handler.UpdateProduct)

	title := "Updated Title"
	updateReq := UpdateProductRequest{
		Title: &title,
	}
	body, _ := json.Marshal(updateReq)
	nonExistentID := uuid.New()
	req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/marketplace/products/%s", nonExistentID.String()), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
}

// TestMarketplaceHandler_CreateOrder_Success tests successful order creation
func TestMarketplaceHandler_CreateOrder_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test users
	sellerID := uuid.New()
	seller := createTestUserForMarketplace(sellerID, "seller")
	err := db.Create(seller).Error
	require.NoError(t, err)

	buyerID := uuid.New()
	buyer := createTestUserForMarketplace(buyerID, "buyer")
	err = db.Create(buyer).Error
	require.NoError(t, err)

	// Create test product
	productID := uuid.New()
	product := &marketplace.Product{
		ID:          productID,
		SellerID:    sellerID,
		Title:       "Test Product",
		Description: "Test Description",
		Price:       9.99,
		ProductType: "track",
		Status:      marketplace.ProductStatusActive,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	err = db.Create(product).Error
	require.NoError(t, err)

	router.POST("/marketplace/orders", handler.CreateOrder)

	createReq := CreateOrderRequest{
		Items: []struct {
			ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
		}{
			{ProductID: productID.String()},
		},
	}
	body, _ := json.Marshal(createReq)
	req := httptest.NewRequest(http.MethodPost, "/marketplace/orders", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", buyerID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusCreated, w.Code)
}

// TestMarketplaceHandler_CreateOrder_InvalidProduct tests order creation with invalid product
func TestMarketplaceHandler_CreateOrder_InvalidProduct(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	buyerID := uuid.New()
	buyer := createTestUserForMarketplace(buyerID, "buyer")
	err := db.Create(buyer).Error
	require.NoError(t, err)

	router.POST("/marketplace/orders", handler.CreateOrder)

	createReq := CreateOrderRequest{
		Items: []struct {
			ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
		}{
			{ProductID: uuid.New().String()}, // Non-existent product
		},
	}
	body, _ := json.Marshal(createReq)
	req := httptest.NewRequest(http.MethodPost, "/marketplace/orders", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", buyerID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

// TestMarketplaceHandler_ListOrders_Success tests successful order listing
func TestMarketplaceHandler_ListOrders_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	buyerID := uuid.New()
	buyer := createTestUserForMarketplace(buyerID, "buyer")
	err := db.Create(buyer).Error
	require.NoError(t, err)

	// Create test orders
	for i := 0; i < 2; i++ {
		order := &marketplace.Order{
			ID:          uuid.New(),
			BuyerID:     buyerID,
			TotalAmount: 9.99,
			Currency:    "EUR",
			Status:      "paid",
			CreatedAt:   time.Now(),
			UpdatedAt:   time.Now(),
		}
		err = db.Create(order).Error
		require.NoError(t, err)
	}

	router.GET("/marketplace/orders", handler.ListOrders)

	req := httptest.NewRequest(http.MethodGet, "/marketplace/orders", nil)
	req.Header.Set("X-User-ID", buyerID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response["success"].(bool))
}

// TestMarketplaceHandler_GetOrder_Success tests successful order retrieval
func TestMarketplaceHandler_GetOrder_Success(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	buyerID := uuid.New()
	buyer := createTestUserForMarketplace(buyerID, "buyer")
	err := db.Create(buyer).Error
	require.NoError(t, err)

	// Create test order
	orderID := uuid.New()
	order := &marketplace.Order{
		ID:          orderID,
		BuyerID:     buyerID,
		TotalAmount: 9.99,
		Currency:    "EUR",
		Status:      "paid",
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	err = db.Create(order).Error
	require.NoError(t, err)

	router.GET("/marketplace/orders/:id", handler.GetOrder)

	req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/marketplace/orders/%s", orderID.String()), nil)
	req.Header.Set("X-User-ID", buyerID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response["success"].(bool))
}

// TestMarketplaceHandler_GetOrder_NotFound tests order retrieval with non-existent order
func TestMarketplaceHandler_GetOrder_NotFound(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	buyerID := uuid.New()
	buyer := createTestUserForMarketplace(buyerID, "buyer")
	err := db.Create(buyer).Error
	require.NoError(t, err)

	router.GET("/marketplace/orders/:id", handler.GetOrder)

	nonExistentID := uuid.New()
	req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/marketplace/orders/%s", nonExistentID.String()), nil)
	req.Header.Set("X-User-ID", buyerID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
}

// TestMarketplaceHandler_GetDownloadURL_NoLicense tests download URL retrieval without license
func TestMarketplaceHandler_GetDownloadURL_NoLicense(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "user")
	err := db.Create(user).Error
	require.NoError(t, err)

	// Create test product
	productID := uuid.New()
	product := &marketplace.Product{
		ID:          productID,
		SellerID:    uuid.New(),
		Title:       "Test Product",
		Description: "Test Description",
		Price:       9.99,
		ProductType: "track",
		Status:      marketplace.ProductStatusActive,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	err = db.Create(product).Error
	require.NoError(t, err)

	router.GET("/marketplace/download/:product_id", handler.GetDownloadURL)

	req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/marketplace/download/%s", productID.String()), nil)
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Should fail because user doesn't have a license
	assert.Equal(t, http.StatusForbidden, w.Code)
}

// TestMarketplaceHandler_CreateProduct_InvalidTrackID tests product creation with invalid track ID format
func TestMarketplaceHandler_CreateProduct_InvalidTrackID(t *testing.T) {
	handler, db, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	// Create test user
	userID := uuid.New()
	user := createTestUserForMarketplace(userID, "seller")
	err := db.Create(user).Error
	require.NoError(t, err)

	router.POST("/marketplace/products", handler.CreateProduct)

	createReq := CreateProductRequest{
		Title:       "My Product",
		Description: "A test product",
		Price:       9.99,
		ProductType: "track",
		TrackID:     "invalid-uuid", // Invalid UUID format
		LicenseType: "standard",
	}
	body, _ := json.Marshal(createReq)
	req := httptest.NewRequest(http.MethodPost, "/marketplace/products", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

// TestMarketplaceHandler_UpdateProduct_InvalidID tests product update with invalid ID format
func TestMarketplaceHandler_UpdateProduct_InvalidID(t *testing.T) {
	handler, _, router, cleanup := setupTestMarketplaceHandler(t)
	defer cleanup()

	router.PUT("/marketplace/products/:id", handler.UpdateProduct)

	title := "Updated Title"
	updateReq := UpdateProductRequest{
		Title: &title,
	}
	body, _ := json.Marshal(updateReq)
	req := httptest.NewRequest(http.MethodPut, "/marketplace/products/invalid-id", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", uuid.New().String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}
