package rest

import (
	"fmt"
	"net/http"
	"regexp"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/backend/internal/features/analytics"
	"github.com/nexuscrm/backend/pkg/apierr"
)

var validObjectNameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)

// validateObjectName extracts and validates objectName from the URL path.
// Returns the objectName and true if valid, or writes an error response and returns false.
func validateObjectName(c *gin.Context) (string, bool) {
	name := c.Param("objectName")
	if name == "" {
		RespondAppError(c, apierr.Invalid("objectName is required"))
		return "", false
	}
	if !validObjectNameRe.MatchString(name) {
		RespondAppError(c, apierr.Invalid("objectName contains invalid characters"))
		return "", false
	}
	return name, true
}

// FormulaViewHandler handles formula view management API endpoints
type FormulaViewHandler struct {
	svcMgr *services.ServiceManager
}

// NewFormulaViewHandler creates a new FormulaViewHandler
func NewFormulaViewHandler(svcMgr *services.ServiceManager) *FormulaViewHandler {
	return &FormulaViewHandler{svcMgr: svcMgr}
}

// RebuildView regenerates the TiDB View for a specific object
// POST /metadata/formula-views/:objectName/rebuild
func (h *FormulaViewHandler) RebuildView(c *gin.Context) {
	objectName, ok := validateObjectName(c)
	if !ok {
		return
	}

	if err := h.svcMgr.FormulaViewSvc.RebuildView(c.Request.Context(), objectName); err != nil {
		RespondAppError(c, fmt.Errorf("rebuild view %s: %w", objectName, err))
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success":  true,
		"object":   objectName,
		"viewName": "v_" + objectName,
		"message":  "View rebuilt successfully",
	})
}

// RebuildAllViews regenerates TiDB Views for all objects with active formula fields
// POST /metadata/formula-views/rebuild-all
func (h *FormulaViewHandler) RebuildAllViews(c *gin.Context) {
	if err := h.svcMgr.FormulaViewSvc.RebuildAllViews(c.Request.Context()); err != nil {
		RespondAppError(c, err)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "All formula views rebuilt",
	})
}

// ViewStatus returns the status of a formula view for an object
// GET /metadata/formula-views/:objectName/status
func (h *FormulaViewHandler) ViewStatus(c *gin.Context) {
	objectName, ok := validateObjectName(c)
	if !ok {
		return
	}

	ctx := c.Request.Context()
	exists := h.svcMgr.FormulaViewSvc.ViewExists(ctx, objectName)

	schema := h.svcMgr.Metadata.GetSchema(ctx, objectName)
	activeCount := 0
	frozenCount := 0
	if schema != nil {
		for _, f := range schema.Fields {
			if f.Formula != nil && *f.Formula != "" {
				if f.FormulaMode != nil && *f.FormulaMode == "active" {
					activeCount++
				} else {
					frozenCount++
				}
			}
		}
	}

	resolved := h.svcMgr.FormulaViewSvc.ResolveTableName(ctx, schema)

	c.JSON(http.StatusOK, gin.H{
		"object":             objectName,
		"viewExists":         exists,
		"viewName":           "v_" + objectName,
		"activeFormulaCount": activeCount,
		"frozenFormulaCount": frozenCount,
		"resolvedTableName":  resolved,
	})
}

// ValidateFormula validates a formula expression and returns the compiled SQL
// POST /metadata/formula-views/:objectName/validate
func (h *FormulaViewHandler) ValidateFormula(c *gin.Context) {
	var req struct {
		Formula    string `json:"formula"`
		ReturnType string `json:"returnType"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		RespondAppError(c, apierr.Invalid("invalid request body: "+err.Error()))
		return
	}
	if req.Formula == "" {
		RespondAppError(c, apierr.Invalid("formula is required"))
		return
	}

	// Parse the formula into an AST
	ast, err := analytics.Parse(req.Formula)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"valid": false,
			"error": err.Error(),
		})
		return
	}

	// Extract field references and cross-table references
	referencedFields := analytics.ExtractFieldRefs(ast)
	crossRefs := analytics.ExtractCrossRefs(ast)

	crossRefStrings := make([]string, len(crossRefs))
	for i, cr := range crossRefs {
		crossRefStrings[i] = cr.Object + "." + cr.Field
	}

	// Compile the AST to SQL
	ctx := &analytics.CompileContext{
		TableAlias:    "t",
		JoinAliases:   map[string]string{},
		QualifyFields: false,
	}
	compiled, err := analytics.Compile(ast, ctx)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"valid": false,
			"error": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"valid":            true,
		"compiledSQL":      compiled,
		"referencedFields": referencedFields,
		"crossTableRefs":   crossRefStrings,
	})
}

// PreviewDDL returns the View DDL that would be generated without executing it
// GET /metadata/formula-views/:objectName/ddl
func (h *FormulaViewHandler) PreviewDDL(c *gin.Context) {
	objectName, ok := validateObjectName(c)
	if !ok {
		return
	}

	ctx := c.Request.Context()
	ddl, err := h.svcMgr.FormulaViewSvc.PreviewDDL(ctx, objectName)
	if err != nil {
		RespondAppError(c, err)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"object": objectName,
		"ddl":    ddl,
	})
}
