package controllers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// EditorPreviewController handles real-time editor preview operations
type EditorPreviewController struct {
	DB *gorm.DB
}

// NewEditorPreviewController creates a new editor preview controller
func NewEditorPreviewController(db *gorm.DB) *EditorPreviewController {
	return &EditorPreviewController{DB: db}
}

// PreviewState represents the current editor state
type PreviewState struct {
	SessionID   string                 `json:"session_id"`
	PageType    string                 `json:"page_type"`
	Elements    []ElementPreviewConfig `json:"elements"`
	UpdatedAt   time.Time              `json:"updated_at"`
	UserID      uint                   `json:"user_id,omitempty"`
}

// ElementPreviewConfig represents a single element's preview configuration
type ElementPreviewConfig struct {
	ElementName  string                 `json:"element_name"`
	Variant      string                 `json:"variant"`
	Visible      bool                   `json:"visible"`
	DisplayOrder int                    `json:"display_order"`
	CustomStyles map[string]interface{} `json:"custom_styles,omitempty"`
}

// GetPreviewState retrieves the current preview state for a session
// GET /api/v1/editor/preview/:session_id
func (c *EditorPreviewController) GetPreviewState(ctx *gin.Context) {
	sessionID := ctx.Param("session_id")
	
	// In a real implementation, you would fetch from cache/session store
	// For now, return a stub response
	ctx.JSON(http.StatusOK, gin.H{
		"session_id": sessionID,
		"page_type":  "homepage",
		"elements":   []ElementPreviewConfig{},
		"updated_at": time.Now(),
	})
}

// UpdatePreviewState updates the preview state for a session
// POST /api/v1/editor/preview/:session_id
func (c *EditorPreviewController) UpdatePreviewState(ctx *gin.Context) {
	sessionID := ctx.Param("session_id")
	
	var state PreviewState
	if err := ctx.ShouldBindJSON(&state); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}
	
	state.SessionID = sessionID
	state.UpdatedAt = time.Now()
	
	// In a real implementation, you would:
	// 1. Store in Redis/cache with TTL (e.g., 1 hour)
	// 2. Associate with user session
	// 3. Validate element configurations
	
	ctx.JSON(http.StatusOK, gin.H{
		"success": true,
		"state":   state,
	})
}

// ApplyPreviewChanges commits preview changes to the database
// POST /api/v1/editor/preview/:session_id/apply
func (c *EditorPreviewController) ApplyPreviewChanges(ctx *gin.Context) {
	sessionID := ctx.Param("session_id")
	
	var payload struct {
		PageType string                 `json:"page_type" binding:"required"`
		Elements []ElementPreviewConfig `json:"elements" binding:"required"`
	}
	
	if err := ctx.ShouldBindJSON(&payload); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}
	
	// Start transaction
	tx := c.DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	
	// Delete existing configurations for this page type
	if err := tx.Exec("DELETE FROM page_element_configs WHERE page_type = ?", payload.PageType).Error; err != nil {
		tx.Rollback()
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear existing configurations"})
		return
	}
	
	// Insert new configurations
	for _, elem := range payload.Elements {
		config := map[string]interface{}{
			"page_type":     payload.PageType,
			"element_name":  elem.ElementName,
			"variant":       elem.Variant,
			"visible":       elem.Visible,
			"display_order": elem.DisplayOrder,
			"created_at":    time.Now(),
			"updated_at":    time.Now(),
		}
		
		if err := tx.Table("page_element_configs").Create(config).Error; err != nil {
			tx.Rollback()
			ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save configuration"})
			return
		}
	}
	
	// Commit transaction
	if err := tx.Commit().Error; err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit changes"})
		return
	}
	
	// Clear preview state from cache
	// In a real implementation: cache.Delete(sessionID)
	
	ctx.JSON(http.StatusOK, gin.H{
		"success":    true,
		"session_id": sessionID,
		"message":    "Changes applied successfully",
		"applied_at": time.Now(),
	})
}

// DiscardPreviewChanges discards preview changes without saving
// DELETE /api/v1/editor/preview/:session_id
func (c *EditorPreviewController) DiscardPreviewChanges(ctx *gin.Context) {
	sessionID := ctx.Param("session_id")
	
	// In a real implementation: cache.Delete(sessionID)
	
	ctx.JSON(http.StatusOK, gin.H{
		"success":    true,
		"session_id": sessionID,
		"message":    "Preview discarded",
	})
}

// ValidatePreviewConfig validates element configurations before saving
// POST /api/v1/editor/preview/validate
func (c *EditorPreviewController) ValidatePreviewConfig(ctx *gin.Context) {
	var configs []ElementPreviewConfig
	if err := ctx.ShouldBindJSON(&configs); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}
	
	// Validation rules
	errors := []string{}
	elementNames := make(map[string]bool)
	
	for i, config := range configs {
		// Check for duplicate element names
		if elementNames[config.ElementName] {
			errors = append(errors, "Duplicate element name: "+config.ElementName)
		}
		elementNames[config.ElementName] = true
		
		// Validate variant is not empty
		if config.Variant == "" {
			errors = append(errors, "Element "+config.ElementName+" has empty variant")
		}
		
		// Validate display order
		if config.DisplayOrder < 0 {
			errors = append(errors, "Element "+config.ElementName+" has invalid display order")
		}
		
		// Check for gaps in display order
		if config.DisplayOrder != i {
			errors = append(errors, "Display order has gaps or is not sequential")
		}
	}
	
	if len(errors) > 0 {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"valid":  false,
			"errors": errors,
		})
		return
	}
	
	ctx.JSON(http.StatusOK, gin.H{
		"valid":   true,
		"message": "Configuration is valid",
	})
}

// GetAvailableVariants returns available variants for an element
// GET /api/v1/editor/variants/:element_name
func (c *EditorPreviewController) GetAvailableVariants(ctx *gin.Context) {
	elementName := ctx.Param("element_name")
	
	// Define available variants for each element
	// In a real implementation, this could come from database or config
	variants := map[string][]map[string]interface{}{
		"hero": {
			{"value": "grid", "label": "Grid Layout", "description": "3-card grid hero section"},
			{"value": "scroller", "label": "Horizontal Scroller", "description": "Scrollable cards"},
			{"value": "swiper", "label": "Swiper", "description": "Swipeable carousel"},
			{"value": "swiper_full", "label": "Full-width Swiper", "description": "Full viewport carousel"},
		},
		"matches": {
			{"value": "default", "label": "Default", "description": "Standard match display"},
			{"value": "compact", "label": "Compact", "description": "Compact match cards"},
		},
		"table": {
			{"value": "default", "label": "Default", "description": "Standard table layout"},
			{"value": "compact", "label": "Compact", "description": "Compact table view"},
		},
		"videos": {
			{"value": "default", "label": "Default", "description": "Standard video grid"},
			{"value": "carousel", "label": "Carousel", "description": "Video carousel"},
		},
		"gallery": {
			{"value": "default", "label": "Default", "description": "Standard gallery grid"},
			{"value": "masonry", "label": "Masonry", "description": "Masonry layout"},
		},
		"sponsors": {
			{"value": "grid", "label": "Grid", "description": "Grid layout"},
			{"value": "slider", "label": "Slider", "description": "Horizontal slider"},
		},
		"newsletter": {
			{"value": "default", "label": "Default", "description": "Standard newsletter form"},
			{"value": "inline", "label": "Inline", "description": "Inline compact form"},
		},
	}
	
	if variantList, ok := variants[elementName]; ok {
		ctx.JSON(http.StatusOK, gin.H{
			"element_name": elementName,
			"variants":     variantList,
		})
	} else {
		ctx.JSON(http.StatusOK, gin.H{
			"element_name": elementName,
			"variants":     []map[string]interface{}{{"value": "default", "label": "Default"}},
		})
	}
}
