package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// HandlerData contains template data for handler generation
type HandlerData struct {
	Name          string
	NameLower     string
	NamePlural    string
	Category      string
	CategoryPath  string
	ModuleName    string
	TableName     string
	HasGet        bool
	HasPost       bool
	HasPut        bool
	HasDelete     bool
	HasStats      bool
	HasDynamic    bool
	HasSearch     bool
	HasFilter     bool
	HasPagination bool
	Timestamp     string
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run generate-handler.go [category/]entity [methods]")
		fmt.Println("Examples:")
		fmt.Println(" go run generate-handler.go product get post put delete")
		fmt.Println(" go run generate-handler.go retribusi/tarif get post put delete dynamic search")
		os.Exit(1)
	}

	// Parse entity path (could be "entity" or "category/entity")
	entityPath := os.Args[1]
	methods := []string{}
	if len(os.Args) > 2 {
		methods = os.Args[2:]
	} else {
		// Default methods with advanced features
		methods = []string{"get", "post", "put", "delete", "dynamic", "search"}
	}

	// Parse category and entity
	var category, entityName string
	if strings.Contains(entityPath, "/") {
		parts := strings.Split(entityPath, "/")
		if len(parts) != 2 {
			fmt.Println("❌ Error: Invalid path format. Use 'category/entity' or just 'entity'")
			os.Exit(1)
		}
		category = parts[0]
		entityName = parts[1]
	} else {
		category = ""
		entityName = entityPath
	}

	// Format names
	entityName = strings.Title(entityName) // PascalCase entity name
	entityLower := strings.ToLower(entityName)
	entityPlural := entityLower + "s"

	// Table name: include category if exists
	var tableName string
	if category != "" {
		tableName = "data_" + category + "_" + entityLower
	} else {
		tableName = "data_" + entityLower
	}

	data := HandlerData{
		Name:          entityName,
		NameLower:     entityLower,
		NamePlural:    entityPlural,
		Category:      category,
		CategoryPath:  category,
		ModuleName:    "api-service",
		TableName:     tableName,
		HasPagination: true,
		HasFilter:     true,
		Timestamp:     time.Now().Format("2006-01-02 15:04:05"),
	}

	// Set methods based on arguments
	for _, m := range methods {
		switch strings.ToLower(m) {
		case "get":
			data.HasGet = true
		case "post":
			data.HasPost = true
		case "put":
			data.HasPut = true
		case "delete":
			data.HasDelete = true
		case "stats":
			data.HasStats = true
		case "dynamic":
			data.HasDynamic = true
		case "search":
			data.HasSearch = true
		}
	}

	// Always add stats if we have get
	if data.HasGet {
		data.HasStats = true
	}

	// Create directories with improved logic
	var handlerDir, modelDir string
	if category != "" {
		handlerDir = filepath.Join("internal", "handlers", category)
		modelDir = filepath.Join("internal", "models", category)
	} else {
		handlerDir = filepath.Join("internal", "handlers")
		modelDir = filepath.Join("internal", "models")
	}

	// Create directories
	for _, d := range []string{handlerDir, modelDir} {
		if err := os.MkdirAll(d, 0755); err != nil {
			panic(err)
		}
	}

	// Generate files
	generateHandlerFile(data, handlerDir)
	generateModelFile(data, modelDir)
	updateRoutesFile(data)

	fmt.Printf("✅ Successfully generated handler: %s\n", entityName)
	if category != "" {
		fmt.Printf("📁 Category: %s\n", category)
	}
	fmt.Printf("📁 Handler: %s\n", filepath.Join(handlerDir, entityLower+".go"))
	fmt.Printf("📁 Model: %s\n", filepath.Join(modelDir, entityLower+".go"))
}

// ================= HANDLER GENERATION =====================
func generateHandlerFile(data HandlerData, handlerDir string) {
	// Build import path based on category
	var modelsImportPath string
	if data.Category != "" {
		modelsImportPath = data.ModuleName + "/internal/models/" + data.Category
	} else {
		modelsImportPath = data.ModuleName + "/internal/models"
	}

	handlerContent := `package handlers

import (
	"` + data.ModuleName + `/internal/config"
	"` + data.ModuleName + `/internal/database"
	models "` + data.ModuleName + `/internal/models"
	models` + data.NameLower + ` "` + modelsImportPath + `"`

	// Add conditional imports for dynamic and search functionality
	if data.HasDynamic || data.HasSearch {
		handlerContent += `
	utils "` + data.ModuleName + `/internal/utils/filters"`
	}

	handlerContent += `
	"` + data.ModuleName + `/internal/utils/validation"
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/google/uuid"
)

var (
	db       database.Service
	once     sync.Once
	validate *validator.Validate
)

// Initialize the database connection and validator
func init() {
	once.Do(func() {
		db = database.New(config.LoadConfig())
		validate = validator.New()
		// Register custom validations if needed
		validate.RegisterValidation("` + data.NameLower + `_status", validate` + data.Name + `Status)
		if db == nil {
			log.Fatal("Failed to initialize database connection")
		}
	})
}

// Custom validation for ` + data.NameLower + ` status
func validate` + data.Name + `Status(fl validator.FieldLevel) bool {
	return models.IsValidStatus(fl.Field().String())
}

// ` + data.Name + `Handler handles ` + data.NameLower + ` services
type ` + data.Name + `Handler struct {
	db database.Service
}

// New` + data.Name + `Handler creates a new ` + data.Name + `Handler
func New` + data.Name + `Handler() *` + data.Name + `Handler {
	return &` + data.Name + `Handler{
		db: db,
	}
}`

	// Add methods
	if data.HasGet {
		handlerContent += generateGetMethods(data)
	}
	if data.HasDynamic {
		handlerContent += generateDynamicMethod(data)
	}
	if data.HasSearch {
		handlerContent += generateSearchMethod(data)
	}
	if data.HasPost {
		handlerContent += generateCreateMethod(data)
	}
	if data.HasPut {
		handlerContent += generateUpdateMethod(data)
	}
	if data.HasDelete {
		handlerContent += generateDeleteMethod(data)
	}
	if data.HasStats {
		handlerContent += generateStatsMethod(data)
	}

	// Add helper methods
	handlerContent += generateHelperMethods(data)

	writeFile(filepath.Join(handlerDir, data.NameLower+".go"), handlerContent)

}

func generateGetMethods(data HandlerData) string {
	return `

// Get` + data.Name + ` godoc
// @Summary Get ` + data.NameLower + ` with pagination and optional aggregation
// @Description Returns a paginated list of ` + data.NamePlural + ` with optional summary statistics
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param limit query int false "Limit (max 100)" default(10)
// @Param offset query int false "Offset" default(0)
// @Param include_summary query bool false "Include aggregation summary" default(false)
// @Param status query string false "Filter by status"
// @Param search query string false "Search in multiple fields"
// @Success 200 {object} models` + data.NameLower + `.` + data.Name + `GetResponse "Success response"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Bad request"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NamePlural + ` [get]
func (h *` + data.Name + `Handler) Get` + data.Name + `(c *gin.Context) {
	// Parse pagination parameters
	limit, offset, err := h.parsePaginationParams(c)
	if err != nil {
		h.respondError(c, "Invalid pagination parameters", err, http.StatusBadRequest)
		return
	}

	// Parse filter parameters
	filter := h.parseFilterParams(c)
	includeAggregation := c.Query("include_summary") == "true"
	
	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	// Create context with timeout
	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute concurrent operations
	var (
		items         []models` + data.NameLower + `.` + data.Name + `
		total         int
		aggregateData *models.AggregateData
		wg            sync.WaitGroup
		errChan       = make(chan error, 3)
		mu            sync.Mutex
	)

	// Fetch total count
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := h.getTotalCount(ctx, dbConn, filter, &total); err != nil {
			mu.Lock()
			errChan <- fmt.Errorf("failed to get total count: %w", err)
			mu.Unlock()
		}
	}()

	// Fetch main data
	wg.Add(1)
	go func() {
		defer wg.Done()
		result, err := h.fetch` + data.Name + `s(ctx, dbConn, filter, limit, offset)
		mu.Lock()
		if err != nil {
			errChan <- fmt.Errorf("failed to fetch data: %w", err)
		} else {
			items = result
		}
		mu.Unlock()
	}()

	// Fetch aggregation data if requested
	if includeAggregation {
		wg.Add(1)
		go func() {
			defer wg.Done()
			result, err := h.getAggregateData(ctx, dbConn, filter)
			mu.Lock()
			if err != nil {
				errChan <- fmt.Errorf("failed to get aggregate data: %w", err)
			} else {
				aggregateData = result
			}
			mu.Unlock()
		}()
	}

	// Wait for all goroutines
	wg.Wait()
	close(errChan)

	// Check for errors
	for err := range errChan {
		if err != nil {
			h.logAndRespondError(c, "Data processing failed", err, http.StatusInternalServerError)
			return
		}
	}

	// Build response
	meta := h.calculateMeta(limit, offset, total)
	response := models` + data.NameLower + `.` + data.Name + `GetResponse{
		Message: "Data ` + data.NameLower + ` berhasil diambil",
		Data:    items,
		Meta:    meta,
	}

	if includeAggregation && aggregateData != nil {
		response.Summary = aggregateData
	}

	c.JSON(http.StatusOK, response)
}

// Get` + data.Name + `ByID godoc
// @Summary Get ` + data.Name + ` by ID
// @Description Returns a single ` + data.NameLower + ` by ID
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Success 200 {object} models` + data.NameLower + `.` + data.Name + `GetByIDResponse "Success response"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models` + data.NameLower + `.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NameLower + `/{id} [get]
func (h *` + data.Name + `Handler) Get` + data.Name + `ByID(c *gin.Context) {
	id := c.Param("id")
	
	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	item, err := h.get` + data.Name + `ByID(ctx, dbConn, id)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "` + data.Name + ` not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to get ` + data.NameLower + `", err, http.StatusInternalServerError)
		}
		return
	}

	response := models` + data.NameLower + `.` + data.Name + `GetByIDResponse{
		Message: "` + data.Name + ` details retrieved successfully",
		Data:    item,
	}

	c.JSON(http.StatusOK, response)
}`
}

func generateDynamicMethod(data HandlerData) string {
	return `

// Get` + data.Name + `Dynamic godoc
// @Summary Get ` + data.NameLower + ` with dynamic filtering
// @Description Returns ` + data.NamePlural + ` with advanced dynamic filtering like Directus
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param fields query string false "Fields to select (e.g., fields=*.*)"
// @Param filter[column][operator] query string false "Dynamic filters (e.g., filter[name][_eq]=value)"
// @Param sort query string false "Sort fields (e.g., sort=date_created,-name)"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} models` + data.NameLower + `.` + data.Name + `GetResponse "Success response"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Bad request"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NamePlural + `/dynamic [get]
func (h *` + data.Name + `Handler) Get` + data.Name + `Dynamic(c *gin.Context) {
	// Parse query parameters
	parser := utils.NewQueryParser().SetLimits(10, 100)
	dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query())
	if err != nil {
		h.respondError(c, "Invalid query parameters", err, http.StatusBadRequest)
		return
	}

	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	// Create context with timeout
	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute query with dynamic filtering
	items, total, err := h.fetch` + data.Name + `sDynamic(ctx, dbConn, dynamicQuery)
	if err != nil {
		h.logAndRespondError(c, "Failed to fetch data", err, http.StatusInternalServerError)
		return
	}

	// Build response
	meta := h.calculateMeta(dynamicQuery.Limit, dynamicQuery.Offset, total)
	response := models` + data.NameLower + `.` + data.Name + `GetResponse{
		Message: "Data ` + data.NameLower + ` berhasil diambil",
		Data:    items,
		Meta:    meta,
	}

	c.JSON(http.StatusOK, response)
}`
}

func generateSearchMethod(data HandlerData) string {
	return `

// Search` + data.Name + `Advanced provides advanced search capabilities
func (h *` + data.Name + `Handler) Search` + data.Name + `Advanced(c *gin.Context) {
	// Parse complex search parameters
	searchQuery := c.Query("q")
	if searchQuery == "" {
		h.respondError(c, "Search query is required", fmt.Errorf("empty search query"), http.StatusBadRequest)
		return
	}

	// Build dynamic query for search
	query := utils.DynamicQuery{
		Fields: []string{"*"},
		Filters: []utils.FilterGroup{{
			Filters: []utils.DynamicFilter{
				{
					Column:   "status",
					Operator: utils.OpNotEqual,
					Value:    "deleted",
				},
				{
					Column:   "name",
					Operator: utils.OpContains,
					Value:    searchQuery,
					LogicOp:  "OR",
				},
			},
			LogicOp: "AND",
		}},
		Sort: []utils.SortField{{
			Column: "date_created",
			Order:  "DESC",
		}},
		Limit:  20,
		Offset: 0,
	}

	// Parse pagination if provided
	if limit := c.Query("limit"); limit != "" {
		if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 {
			query.Limit = l
		}
	}
	if offset := c.Query("offset"); offset != "" {
		if o, err := strconv.Atoi(offset); err == nil && o >= 0 {
			query.Offset = o
		}
	}

	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute search
	items, total, err := h.fetch` + data.Name + `sDynamic(ctx, dbConn, query)
	if err != nil {
		h.logAndRespondError(c, "Search failed", err, http.StatusInternalServerError)
		return
	}

	// Build response
	meta := h.calculateMeta(query.Limit, query.Offset, total)
	response := models` + data.NameLower + `.` + data.Name + `GetResponse{
		Message: fmt.Sprintf("Search results for '%s'", searchQuery),
		Data:    items,
		Meta:    meta,
	}

	c.JSON(http.StatusOK, response)
}`
}

func generateCreateMethod(data HandlerData) string {
	return `

// Create` + data.Name + ` godoc
// @Summary Create ` + data.NameLower + `
// @Description Creates a new ` + data.NameLower + ` record
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param request body models` + data.NameLower + `.` + data.Name + `CreateRequest true "` + data.Name + ` creation request"
// @Success 201 {object} models` + data.NameLower + `.` + data.Name + `CreateResponse "` + data.Name + ` created successfully"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Bad request or validation error"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NamePlural + ` [post]
func (h *` + data.Name + `Handler) Create` + data.Name + `(c *gin.Context) {
	var req models` + data.NameLower + `.` + data.Name + `CreateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
		return
	}

	// Validate request
	if err := validate.Struct(&req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	// Validate duplicate and daily submission
	if err := h.validate` + data.Name + `Submission(ctx, dbConn, &req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	item, err := h.create` + data.Name + `(ctx, dbConn, &req)
	if err != nil {
		h.logAndRespondError(c, "Failed to create ` + data.NameLower + `", err, http.StatusInternalServerError)
		return
	}

	response := models` + data.NameLower + `.` + data.Name + `CreateResponse{
		Message: "` + data.Name + ` berhasil dibuat",
		Data:    item,
	}

	c.JSON(http.StatusCreated, response)
}`
}

func generateUpdateMethod(data HandlerData) string {
	return `

// Update` + data.Name + ` godoc
// @Summary Update ` + data.NameLower + `
// @Description Updates an existing ` + data.NameLower + ` record
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Param request body models` + data.NameLower + `.` + data.Name + `UpdateRequest true "` + data.Name + ` update request"
// @Success 200 {object} models` + data.NameLower + `.` + data.Name + `UpdateResponse "` + data.Name + ` updated successfully"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Bad request or validation error"
// @Failure 404 {object} models` + data.NameLower + `.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NameLower + `/{id} [put]
func (h *` + data.Name + `Handler) Update` + data.Name + `(c *gin.Context) {
	id := c.Param("id")
	
	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	var req models` + data.NameLower + `.` + data.Name + `UpdateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
		return
	}

	// Set ID from path parameter
	req.ID = id

	// Validate request
	if err := validate.Struct(&req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	item, err := h.update` + data.Name + `(ctx, dbConn, &req)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "` + data.Name + ` not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to update ` + data.NameLower + `", err, http.StatusInternalServerError)
		}
		return
	}

	response := models` + data.NameLower + `.` + data.Name + `UpdateResponse{
		Message: "` + data.Name + ` berhasil diperbarui",
		Data:    item,
	}

	c.JSON(http.StatusOK, response)
}`
}

func generateDeleteMethod(data HandlerData) string {
	return `

// Delete` + data.Name + ` godoc
// @Summary Delete ` + data.NameLower + `
// @Description Soft deletes a ` + data.NameLower + ` by setting status to 'deleted'
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Success 200 {object} models` + data.NameLower + `.` + data.Name + `DeleteResponse "` + data.Name + ` deleted successfully"
// @Failure 400 {object} models` + data.NameLower + `.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models` + data.NameLower + `.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NameLower + `/{id} [delete]
func (h *` + data.Name + `Handler) Delete` + data.Name + `(c *gin.Context) {
	id := c.Param("id")
	
	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	err = h.delete` + data.Name + `(ctx, dbConn, id)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "` + data.Name + ` not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to delete ` + data.NameLower + `", err, http.StatusInternalServerError)
		}
		return
	}

	response := models` + data.NameLower + `.` + data.Name + `DeleteResponse{
		Message: "` + data.Name + ` berhasil dihapus",
		ID:      id,
	}

	c.JSON(http.StatusOK, response)
}`
}

func generateStatsMethod(data HandlerData) string {
	return `

// Get` + data.Name + `Stats godoc
// @Summary Get ` + data.NameLower + ` statistics
// @Description Returns comprehensive statistics about ` + data.NameLower + ` data
// @Tags ` + data.NameLower + `
// @Accept json
// @Produce json
// @Param status query string false "Filter statistics by status"
// @Success 200 {object} models.AggregateData "Statistics data"
// @Failure 500 {object} models` + data.NameLower + `.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NamePlural + `/stats [get]
func (h *` + data.Name + `Handler) Get` + data.Name + `Stats(c *gin.Context) {
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	filter := h.parseFilterParams(c)
	aggregateData, err := h.getAggregateData(ctx, dbConn, filter)
	if err != nil {
		h.logAndRespondError(c, "Failed to get statistics", err, http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Statistik ` + data.NameLower + ` berhasil diambil",
		"data":    aggregateData,
	})
}`
}

func generateHelperMethods(data HandlerData) string {
	helperMethods := `

// Database operations
func (h *` + data.Name + `Handler) get` + data.Name + `ByID(ctx context.Context, dbConn *sql.DB, id string) (*models` + data.NameLower + `.` + data.Name + `, error) {
	query := "SELECT id, status, sort, user_created, date_created, user_updated, date_updated, name FROM ` + data.TableName + ` WHERE id = $1 AND status != 'deleted'"
	row := dbConn.QueryRowContext(ctx, query, id)
	
	var item models` + data.NameLower + `.` + data.Name + `
	err := row.Scan(&item.ID, &item.Status, &item.Sort, &item.UserCreated, &item.DateCreated, &item.UserUpdated, &item.DateUpdated, &item.Name)
	if err != nil {
		return nil, err
	}
	
	return &item, nil
}

func (h *` + data.Name + `Handler) create` + data.Name + `(ctx context.Context, dbConn *sql.DB, req *models` + data.NameLower + `.` + data.Name + `CreateRequest) (*models` + data.NameLower + `.` + data.Name + `, error) {
	id := uuid.New().String()
	now := time.Now()
	
	query := "INSERT INTO ` + data.TableName + ` (id, status, date_created, date_updated, name) VALUES ($1, $2, $3, $4, $5) RETURNING id, status, sort, user_created, date_created, user_updated, date_updated, name"
	row := dbConn.QueryRowContext(ctx, query, id, req.Status, now, now, req.Name)
	
	var item models` + data.NameLower + `.` + data.Name + `
	err := row.Scan(&item.ID, &item.Status, &item.Sort, &item.UserCreated, &item.DateCreated, &item.UserUpdated, &item.DateUpdated, &item.Name)
	if err != nil {
		return nil, fmt.Errorf("failed to create ` + data.NameLower + `: %w", err)
	}
	
	return &item, nil
}

func (h *` + data.Name + `Handler) update` + data.Name + `(ctx context.Context, dbConn *sql.DB, req *models` + data.NameLower + `.` + data.Name + `UpdateRequest) (*models` + data.NameLower + `.` + data.Name + `, error) {
	now := time.Now()
	
	query := "UPDATE ` + data.TableName + ` SET status = $2, date_updated = $3, name = $4 WHERE id = $1 AND status != 'deleted' RETURNING id, status, sort, user_created, date_created, user_updated, date_updated, name"
	row := dbConn.QueryRowContext(ctx, query, req.ID, req.Status, now, req.Name)
	
	var item models` + data.NameLower + `.` + data.Name + `
	err := row.Scan(&item.ID, &item.Status, &item.Sort, &item.UserCreated, &item.DateCreated, &item.UserUpdated, &item.DateUpdated, &item.Name)
	if err != nil {
		return nil, fmt.Errorf("failed to update ` + data.NameLower + `: %w", err)
	}
	
	return &item, nil
}

func (h *` + data.Name + `Handler) delete` + data.Name + `(ctx context.Context, dbConn *sql.DB, id string) error {
	now := time.Now()
	query := "UPDATE ` + data.TableName + ` SET status = 'deleted', date_updated = $2 WHERE id = $1 AND status != 'deleted'"
	
	result, err := dbConn.ExecContext(ctx, query, id, now)
	if err != nil {
		return fmt.Errorf("failed to delete ` + data.NameLower + `: %w", err)
	}
	
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to get affected rows: %w", err)
	}
	
	if rowsAffected == 0 {
		return sql.ErrNoRows
	}
	
	return nil
}

func (h *` + data.Name + `Handler) fetch` + data.Name + `s(ctx context.Context, dbConn *sql.DB, filter models` + data.NameLower + `.` + data.Name + `Filter, limit, offset int) ([]models` + data.NameLower + `.` + data.Name + `, error) {
	whereClause, args := h.buildWhereClause(filter)
	query := fmt.Sprintf("SELECT id, status, sort, user_created, date_created, user_updated, date_updated, name FROM ` + data.TableName + ` WHERE %s ORDER BY date_created DESC NULLS LAST LIMIT $%d OFFSET $%d", whereClause, len(args)+1, len(args)+2)
	args = append(args, limit, offset)
	
	rows, err := dbConn.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("fetch ` + data.NamePlural + ` query failed: %w", err)
	}
	defer rows.Close()
	
	items := make([]models` + data.NameLower + `.` + data.Name + `, 0, limit)
	for rows.Next() {
		item, err := h.scan` + data.Name + `(rows)
		if err != nil {
			return nil, fmt.Errorf("scan ` + data.Name + ` failed: %w", err)
		}
		items = append(items, item)
	}
	
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("rows iteration error: %w", err)
	}
	
	log.Printf("Successfully fetched %d ` + data.NamePlural + ` with filters applied", len(items))
	return items, nil
}`

	// Add dynamic fetch method if needed
	if data.HasDynamic {
		helperMethods += `

// fetchRetribusisDynamic executes dynamic query
func (h *` + data.Name + `Handler) fetch` + data.Name + `sDynamic(ctx context.Context, dbConn *sql.DB, query utils.DynamicQuery) ([]models` + data.NameLower + `.` + data.Name + `, int, error) {
	// Setup query builder
	builder := utils.NewQueryBuilder("` + data.TableName + `").
		SetAllowedColumns([]string{
			"id", "status", "sort", "user_created", "date_created",
			"user_updated", "date_updated", "name",
		})

	// Add default filter to exclude deleted records
	query.Filters = append([]utils.FilterGroup{{
		Filters: []utils.DynamicFilter{{
			Column:   "status",
			Operator: utils.OpNotEqual,
			Value:    "deleted",
		}},
		LogicOp: "AND",
	}}, query.Filters...)

	// Execute concurrent queries
	var (
		items   []models` + data.NameLower + `.` + data.Name + `
		total   int
		wg      sync.WaitGroup
		errChan = make(chan error, 2)
		mu      sync.Mutex
	)

	// Fetch total count
	wg.Add(1)
	go func() {
		defer wg.Done()
		countQuery := query
		countQuery.Limit = 0
		countQuery.Offset = 0
		countSQL, countArgs, err := builder.BuildCountQuery(countQuery)
		if err != nil {
			errChan <- fmt.Errorf("failed to build count query: %w", err)
			return
		}
		if err := dbConn.QueryRowContext(ctx, countSQL, countArgs...).Scan(&total); err != nil {
			errChan <- fmt.Errorf("failed to get total count: %w", err)
			return
		}
	}()

	// Fetch main data
	wg.Add(1)
	go func() {
		defer wg.Done()
		mainSQL, mainArgs, err := builder.BuildQuery(query)
		if err != nil {
			errChan <- fmt.Errorf("failed to build main query: %w", err)
			return
		}
		
		rows, err := dbConn.QueryContext(ctx, mainSQL, mainArgs...)
		if err != nil {
			errChan <- fmt.Errorf("failed to execute main query: %w", err)
			return
		}
		defer rows.Close()
		
		var results []models` + data.NameLower + `.` + data.Name + `
		for rows.Next() {
			item, err := h.scan` + data.Name + `(rows)
			if err != nil {
				errChan <- fmt.Errorf("failed to scan ` + data.NameLower + `: %w", err)
				return
			}
			results = append(results, item)
		}
		
		if err := rows.Err(); err != nil {
			errChan <- fmt.Errorf("rows iteration error: %w", err)
			return
		}
		
		mu.Lock()
		items = results
		mu.Unlock()
	}()

	// Wait for all goroutines
	wg.Wait()
	close(errChan)

	// Check for errors
	for err := range errChan {
		if err != nil {
			return nil, 0, err
		}
	}

	return items, total, nil
}

// Optimized scanning function yang menggunakan sql.Null* types langsung
func (h *` + data.Name + `Handler) scan` + data.Name + `(rows *sql.Rows) (models` + data.NameLower + `.` + data.Name + `, error) {
	var item models` + data.NameLower + `.` + data.Name + `
	return item, rows.Scan(
		&item.ID, &item.Status, &item.Sort, &item.UserCreated,
		&item.DateCreated, &item.UserUpdated, &item.DateUpdated, &item.Name,
	)
}`
	}

	helperMethods += `

func (h *` + data.Name + `Handler) getTotalCount(ctx context.Context, dbConn *sql.DB, filter models` + data.NameLower + `.` + data.Name + `Filter, total *int) error {
	whereClause, args := h.buildWhereClause(filter)
	countQuery := fmt.Sprintf("SELECT COUNT(*) FROM ` + data.TableName + ` WHERE %s", whereClause)
	if err := dbConn.QueryRowContext(ctx, countQuery, args...).Scan(total); err != nil {
		return fmt.Errorf("total count query failed: %w", err)
	}
	return nil
}

// Get comprehensive aggregate data dengan filter support
func (h *` + data.Name + `Handler) getAggregateData(ctx context.Context, dbConn *sql.DB, filter models` + data.NameLower + `.` + data.Name + `Filter) (*models.AggregateData, error) {
	aggregate := &models.AggregateData{
		ByStatus: make(map[string]int),
	}
	
	// Build where clause untuk filter
	whereClause, args := h.buildWhereClause(filter)
	
	// Use concurrent execution untuk performance
	var wg sync.WaitGroup
	var mu sync.Mutex
	errChan := make(chan error, 4)

	// 1. Count by status
	wg.Add(1)
	go func() {
		defer wg.Done()
		statusQuery := fmt.Sprintf("SELECT status, COUNT(*) FROM ` + data.TableName + ` WHERE %s GROUP BY status ORDER BY status", whereClause)
		
		rows, err := dbConn.QueryContext(ctx, statusQuery, args...)
		if err != nil {
			errChan <- fmt.Errorf("status query failed: %w", err)
			return
		}
		defer rows.Close()
		
		mu.Lock()
		for rows.Next() {
			var status string
			var count int
			if err := rows.Scan(&status, &count); err != nil {
				mu.Unlock()
				errChan <- fmt.Errorf("status scan failed: %w", err)
				return
			}
			aggregate.ByStatus[status] = count
			switch status {
			case "active":
				aggregate.TotalActive = count
			case "draft":
				aggregate.TotalDraft = count
			case "inactive":
				aggregate.TotalInactive = count
			}
		}
		mu.Unlock()
		
		if err := rows.Err(); err != nil {
			errChan <- fmt.Errorf("status iteration error: %w", err)
		}
	}()

	// 2. Get last updated time dan today statistics
	wg.Add(1)
	go func() {
		defer wg.Done()
		
		// Last updated
		lastUpdatedQuery := fmt.Sprintf("SELECT MAX(date_updated) FROM ` + data.TableName + ` WHERE %s AND date_updated IS NOT NULL", whereClause)
		var lastUpdated sql.NullTime
		if err := dbConn.QueryRowContext(ctx, lastUpdatedQuery, args...).Scan(&lastUpdated); err != nil {
			errChan <- fmt.Errorf("last updated query failed: %w", err)
			return
		}

		// Today statistics
		today := time.Now().Format("2006-01-02")
		todayStatsQuery := fmt.Sprintf(` + "`" + `
			SELECT 
				SUM(CASE WHEN DATE(date_created) = $%d THEN 1 ELSE 0 END) as created_today,
				SUM(CASE WHEN DATE(date_updated) = $%d AND DATE(date_created) != $%d THEN 1 ELSE 0 END) as updated_today
			FROM ` + data.TableName + ` 
			WHERE %s` + "`" + `, len(args)+1, len(args)+1, len(args)+1, whereClause)

		todayArgs := append(args, today)
		var createdToday, updatedToday int
		if err := dbConn.QueryRowContext(ctx, todayStatsQuery, todayArgs...).Scan(&createdToday, &updatedToday); err != nil {
			errChan <- fmt.Errorf("today stats query failed: %w", err)
			return
		}

		mu.Lock()
		if lastUpdated.Valid {
			aggregate.LastUpdated = &lastUpdated.Time
		}
		aggregate.CreatedToday = createdToday
		aggregate.UpdatedToday = updatedToday
		mu.Unlock()
	}()

	// Wait for all goroutines
	wg.Wait()
	close(errChan)

	// Check for errors
	for err := range errChan {
		if err != nil {
			return nil, err
		}
	}
	
	return aggregate, nil
}

// Enhanced error handling
func (h *` + data.Name + `Handler) logAndRespondError(c *gin.Context, message string, err error, statusCode int) {
	log.Printf("[ERROR] %s: %v", message, err)
	h.respondError(c, message, err, statusCode)
}

func (h *` + data.Name + `Handler) respondError(c *gin.Context, message string, err error, statusCode int) {
	errorMessage := message
	if gin.Mode() == gin.ReleaseMode {
		errorMessage = "Internal server error"
	}
	
	c.JSON(statusCode, models.ErrorResponse{
		Error:     errorMessage,
		Code:      statusCode,
		Message:   err.Error(),
		Timestamp: time.Now(),
	})
}

// Parse pagination parameters dengan validation yang lebih ketat
func (h *` + data.Name + `Handler) parsePaginationParams(c *gin.Context) (int, int, error) {
	limit := 10 // Default limit
	offset := 0 // Default offset
	
	if limitStr := c.Query("limit"); limitStr != "" {
		parsedLimit, err := strconv.Atoi(limitStr)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid limit parameter: %s", limitStr)
		}
		if parsedLimit <= 0 {
			return 0, 0, fmt.Errorf("limit must be greater than 0")
		}
		if parsedLimit > 100 {
			return 0, 0, fmt.Errorf("limit cannot exceed 100")
		}
		limit = parsedLimit
	}
	
	if offsetStr := c.Query("offset"); offsetStr != "" {
		parsedOffset, err := strconv.Atoi(offsetStr)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid offset parameter: %s", offsetStr)
		}
		if parsedOffset < 0 {
			return 0, 0, fmt.Errorf("offset cannot be negative")
		}
		offset = parsedOffset
	}
	
	log.Printf("Pagination - Limit: %d, Offset: %d", limit, offset)
	return limit, offset, nil
}

func (h *` + data.Name + `Handler) parseFilterParams(c *gin.Context) models` + data.NameLower + `.` + data.Name + `Filter {
	filter := models` + data.NameLower + `.` + data.Name + `Filter{}
	
	if status := c.Query("status"); status != "" {
		if models.IsValidStatus(status) {
			filter.Status = &status
		}
	}
	
	if search := c.Query("search"); search != "" {
		filter.Search = &search
	}
	
	// Parse date filters
	if dateFromStr := c.Query("date_from"); dateFromStr != "" {
		if dateFrom, err := time.Parse("2006-01-02", dateFromStr); err == nil {
			filter.DateFrom = &dateFrom
		}
	}
	
	if dateToStr := c.Query("date_to"); dateToStr != "" {
		if dateTo, err := time.Parse("2006-01-02", dateToStr); err == nil {
			filter.DateTo = &dateTo
		}
	}
	
	return filter
}

// Build WHERE clause dengan filter parameters
func (h *` + data.Name + `Handler) buildWhereClause(filter models` + data.NameLower + `.` + data.Name + `Filter) (string, []interface{}) {
	conditions := []string{"status != 'deleted'"}
	args := []interface{}{}
	paramCount := 1
	
	if filter.Status != nil {
		conditions = append(conditions, fmt.Sprintf("status = $%d", paramCount))
		args = append(args, *filter.Status)
		paramCount++
	}
	
	if filter.Search != nil {
		searchCondition := fmt.Sprintf("name ILIKE $%d", paramCount)
		conditions = append(conditions, searchCondition)
		searchTerm := "%" + *filter.Search + "%"
		args = append(args, searchTerm)
		paramCount++
	}
	
	if filter.DateFrom != nil {
		conditions = append(conditions, fmt.Sprintf("date_created >= $%d", paramCount))
		args = append(args, *filter.DateFrom)
		paramCount++
	}
	
	if filter.DateTo != nil {
		conditions = append(conditions, fmt.Sprintf("date_created <= $%d", paramCount))
		args = append(args, filter.DateTo.Add(24*time.Hour-time.Nanosecond))
		paramCount++
	}
	
	return strings.Join(conditions, " AND "), args
}

func (h *` + data.Name + `Handler) calculateMeta(limit, offset, total int) models.MetaResponse {
	totalPages := 0
	currentPage := 1
	if limit > 0 {
		totalPages = (total + limit - 1) / limit // Ceiling division
		currentPage = (offset / limit) + 1
	}
	
	return models.MetaResponse{
		Limit:       limit,
		Offset:      offset,
		Total:       total,
		TotalPages:  totalPages,
		CurrentPage: currentPage,
		HasNext:     offset+limit < total,
		HasPrev:     offset > 0,
	}
}

// validate` + data.Name + `Submission performs validation for duplicate entries and daily submission limits
func (h *` + data.Name + `Handler) validate` + data.Name + `Submission(ctx context.Context, dbConn *sql.DB, req *models` + data.NameLower + `.` + data.Name + `CreateRequest) error {
	// Import the validation utility
	validator := validation.NewDuplicateValidator(dbConn)
	
	// Use default configuration
	config := validation.ValidationConfig{
		TableName:      "` + data.TableName + `",
		IDColumn:       "id",
		StatusColumn:   "status",
		DateColumn:     "date_created",
		ActiveStatuses: []string{"active", "draft"},
	}
	
	// Validate duplicate entries with active status for today
	err := validator.ValidateDuplicate(ctx, config, "dummy_id")
	if err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}
	
	// Validate once per day submission
	err = validator.ValidateOncePerDay(ctx, "` + data.TableName + `", "id", "date_created", "daily_limit")
	if err != nil {
		return fmt.Errorf("daily submission limit exceeded: %w", err)
	}
	
	return nil
}

// Example usage of the validation utility with custom configuration
func (h *` + data.Name + `Handler) validateWithCustomConfig(ctx context.Context, dbConn *sql.DB, req *models` + data.NameLower + `.` + data.Name + `CreateRequest) error {
	// Create validator instance
	validator := validation.NewDuplicateValidator(dbConn)
	
	// Use custom configuration
	config := validation.ValidationConfig{
		TableName:      "` + data.TableName + `",
		IDColumn:       "id",
		StatusColumn:   "status",
		DateColumn:     "date_created",
		ActiveStatuses: []string{"active", "draft"},
		AdditionalFields: map[string]interface{}{
			"name": req.Name,
		},
	}
	
	// Validate with custom fields
	fields := map[string]interface{}{
		"name": *req.Name,
	}
	
	err := validator.ValidateDuplicateWithCustomFields(ctx, config, fields)
	if err != nil {
		return fmt.Errorf("custom validation failed: %w", err)
	}
	
	return nil
}

// GetLastSubmissionTime example
func (h *` + data.Name + `Handler) getLastSubmissionTimeExample(ctx context.Context, dbConn *sql.DB, identifier string) (*time.Time, error) {
	validator := validation.NewDuplicateValidator(dbConn)
	return validator.GetLastSubmissionTime(ctx, "` + data.TableName + `", "id", "date_created", identifier)
}`

	return helperMethods
}

// Keep existing functions for model generation and routes...
// (The remaining functions stay the same as in the original file)

// ================= MODEL GENERATION =====================
func generateModelFile(data HandlerData, modelDir string) {
	modelContent := `package models

import (
	"` + data.ModuleName + `/internal/models"
	"database/sql"
	"encoding/json"
	"time"
)

// ` + data.Name + ` represents the data structure for the ` + data.NameLower + ` table
// with proper null handling and optimized JSON marshaling
type ` + data.Name + ` struct {
	ID          string                  ` + "`json:\"id\" db:\"id\"`" + `
	Status      string                  ` + "`json:\"status\" db:\"status\"`" + `
	Sort        models.NullableInt32    ` + "`json:\"sort,omitempty\" db:\"sort\"`" + `
	UserCreated sql.NullString          ` + "`json:\"user_created,omitempty\" db:\"user_created\"`" + `
	DateCreated sql.NullTime            ` + "`json:\"date_created,omitempty\" db:\"date_created\"`" + `
	UserUpdated sql.NullString          ` + "`json:\"user_updated,omitempty\" db:\"user_updated\"`" + `
	DateUpdated sql.NullTime            ` + "`json:\"date_updated,omitempty\" db:\"date_updated\"`" + `
	Name        sql.NullString          ` + "`json:\"name,omitempty\" db:\"name\"`" + `
}

// Custom JSON marshaling untuk ` + data.Name + ` agar NULL values tidak muncul di response
func (r ` + data.Name + `) MarshalJSON() ([]byte, error) {
	type Alias ` + data.Name + `
	aux := &struct {
		Sort        *int       ` + "`json:\"sort,omitempty\"`" + `
		UserCreated *string    ` + "`json:\"user_created,omitempty\"`" + `
		DateCreated *time.Time ` + "`json:\"date_created,omitempty\"`" + `
		UserUpdated *string    ` + "`json:\"user_updated,omitempty\"`" + `
		DateUpdated *time.Time ` + "`json:\"date_updated,omitempty\"`" + `
		Name        *string    ` + "`json:\"name,omitempty\"`" + `
		*Alias
	}{
		Alias: (*Alias)(&r),
	}

	// Convert NullableInt32 to pointer
	if r.Sort.Valid {
		sort := int(r.Sort.Int32)
		aux.Sort = &sort
	}

	if r.UserCreated.Valid {
		aux.UserCreated = &r.UserCreated.String
	}

	if r.DateCreated.Valid {
		aux.DateCreated = &r.DateCreated.Time
	}

	if r.UserUpdated.Valid {
		aux.UserUpdated = &r.UserUpdated.String
	}

	if r.DateUpdated.Valid {
		aux.DateUpdated = &r.DateUpdated.Time
	}

	if r.Name.Valid {
		aux.Name = &r.Name.String
	}

	return json.Marshal(aux)
}

// Helper methods untuk mendapatkan nilai yang aman
func (r *` + data.Name + `) GetName() string {
	if r.Name.Valid {
		return r.Name.String
	}
	return ""
}`

	// Add request/response structs based on enabled methods
	if data.HasGet {
		modelContent += `

// Response struct untuk GET by ID - diperbaiki struktur
type ` + data.Name + `GetByIDResponse struct {
	Message string    ` + "`json:\"message\"`" + `
	Data    *` + data.Name + ` ` + "`json:\"data\"`" + `
}

// Enhanced GET response dengan pagination dan aggregation
type ` + data.Name + `GetResponse struct {
	Message string                     ` + "`json:\"message\"`" + `
	Data    []` + data.Name + `               ` + "`json:\"data\"`" + `
	Meta    models.MetaResponse        ` + "`json:\"meta\"`" + `
	Summary *models.AggregateData      ` + "`json:\"summary,omitempty\"`" + `
}`
	}

	if data.HasPost {
		modelContent += `

// Request struct untuk create - dioptimalkan dengan validasi
type ` + data.Name + `CreateRequest struct {
	Status string  ` + "`json:\"status\" validate:\"required,oneof=draft active inactive\"`" + `
	Name   *string ` + "`json:\"name,omitempty\" validate:\"omitempty,min=1,max=255\"`" + `
}

// Response struct untuk create
type ` + data.Name + `CreateResponse struct {
	Message string    ` + "`json:\"message\"`" + `
	Data    *` + data.Name + ` ` + "`json:\"data\"`" + `
}`
	}

	if data.HasPut {
		modelContent += `

// Update request - sama seperti create tapi dengan ID
type ` + data.Name + `UpdateRequest struct {
	ID     string  ` + "`json:\"-\" validate:\"required,uuid4\"`" + ` // ID dari URL path
	Status string  ` + "`json:\"status\" validate:\"required,oneof=draft active inactive\"`" + `
	Name   *string ` + "`json:\"name,omitempty\" validate:\"omitempty,min=1,max=255\"`" + `
}

// Response struct untuk update
type ` + data.Name + `UpdateResponse struct {
	Message string    ` + "`json:\"message\"`" + `
	Data    *` + data.Name + ` ` + "`json:\"data\"`" + `
}`
	}

	if data.HasDelete {
		modelContent += `

// Response struct untuk delete
type ` + data.Name + `DeleteResponse struct {
	Message string ` + "`json:\"message\"`" + `
	ID      string ` + "`json:\"id\"`" + `
}`
	}

	// Add filter struct
	modelContent += `

// Filter struct untuk query parameters
type ` + data.Name + `Filter struct {
	Status   *string    ` + "`json:\"status,omitempty\" form:\"status\"`" + `
	Search   *string    ` + "`json:\"search,omitempty\" form:\"search\"`" + `
	DateFrom *time.Time ` + "`json:\"date_from,omitempty\" form:\"date_from\"`" + `
	DateTo   *time.Time ` + "`json:\"date_to,omitempty\" form:\"date_to\"`" + `
}`

	writeFile(filepath.Join(modelDir, data.NameLower+".go"), modelContent)
}

// ================= ROUTES GENERATION =====================
func updateRoutesFile(data HandlerData) {
	routesFile := "internal/routes/v1/routes.go"
	content, err := os.ReadFile(routesFile)
	if err != nil {
		fmt.Printf("⚠️ Could not read routes.go: %v\n", err)
		fmt.Printf("📝 Please manually add these routes to your routes.go file:\n")
		printRoutesSample(data)
		return
	}

	routesContent := string(content)

	// Build import path
	var importPath, importAlias string
	if data.Category != "" {
		importPath = fmt.Sprintf("%s/internal/handlers", data.ModuleName)
		importAlias = data.NameLower + "Handlers"
	} else {
		importPath = fmt.Sprintf("%s/internal/handlers", data.ModuleName)
		importAlias = data.NameLower + "Handlers"
	}

	// Add import
	importPattern := fmt.Sprintf("%s \"%s\"", importAlias, importPath)
	if !strings.Contains(routesContent, importPattern) {
		importToAdd := fmt.Sprintf("\t%s \"%s\"", importAlias, importPath)
		if strings.Contains(routesContent, "import (") {
			routesContent = strings.Replace(routesContent, "import (",
				"import (\n"+importToAdd, 1)
		}
	}

	// Build new routes in protected group format
	newRoutes := generateProtectedRouteBlock(data)

	// Insert above protected routes marker
	insertMarker := "// ============= PUBLISHED ROUTES ==============================================="
	if strings.Contains(routesContent, insertMarker) {
		if !strings.Contains(routesContent, fmt.Sprintf("New%sHandler", data.Name)) {
			// Insert before the marker
			routesContent = strings.Replace(routesContent, insertMarker,
				newRoutes+"\n\t"+insertMarker, 1)
		} else {
			fmt.Printf("✅ Routes for %s already exist, skipping...\n", data.Name)
			return
		}
	} else {
		// Fallback: insert at end of setupV1Routes function
		setupFuncEnd := "\treturn r"
		if strings.Contains(routesContent, setupFuncEnd) {
			routesContent = strings.Replace(routesContent, setupFuncEnd,
				newRoutes+"\n\n\t"+setupFuncEnd, 1)
		}
	}

	if err := os.WriteFile(routesFile, []byte(routesContent), 0644); err != nil {
		fmt.Printf("Error writing routes.go: %v\n", err)
		return
	}

	fmt.Printf("✅ Updated routes.go with %s endpoints\n", data.Name)
}

func generateProtectedRouteBlock(data HandlerData) string {
	routes := fmt.Sprintf(`
	// %s endpoints
	%sHandler := %sHandlers.New%sHandler()
	%sGroup := v1.Group("/%s")
	{
		%sGroup.GET("", %sHandler.Get%s)`,
		strings.Title(data.NamePlural), data.NameLower, data.NameLower, data.Name,
		data.NameLower, data.NameLower,
		data.NameLower, data.NameLower, data.Name)

	if data.HasDynamic {
		routes += fmt.Sprintf(`
		%sGroup.GET("/dynamic", %sHandler.Get%sDynamic)    // Route baru`,
			data.NameLower, data.NameLower, data.Name)
	}

	if data.HasSearch {
		routes += fmt.Sprintf(`
		%sGroup.GET("/search", %sHandler.Search%sAdvanced) // Route pencarian`,
			data.NameLower, data.NameLower, data.Name)
	}

	routes += fmt.Sprintf(`
		%sGroup.GET("/:id", %sHandler.Get%sByID)`,
		data.NameLower, data.NameLower, data.Name)

	if data.HasPost {
		routes += fmt.Sprintf(`
		%sGroup.POST("", %sHandler.Create%s)`,
			data.NameLower, data.NameLower, data.Name)
	}

	if data.HasPut {
		routes += fmt.Sprintf(`
		%sGroup.PUT("/:id", %sHandler.Update%s)`,
			data.NameLower, data.NameLower, data.Name)
	}

	if data.HasDelete {
		routes += fmt.Sprintf(`
		%sGroup.DELETE("/:id", %sHandler.Delete%s)`,
			data.NameLower, data.NameLower, data.Name)
	}

	if data.HasStats {
		routes += fmt.Sprintf(`
		%sGroup.GET("/stats", %sHandler.Get%sStats)`,
			data.NameLower, data.NameLower, data.Name)
	}

	routes += `
	}`

	return routes
}

func printRoutesSample(data HandlerData) {
	fmt.Print(generateProtectedRouteBlock(data))
	fmt.Println()
}

// ================= UTILITY FUNCTIONS =====================
func writeFile(filename, content string) {
	if err := os.WriteFile(filename, []byte(content), 0644); err != nil {
		fmt.Printf("❌ Error creating file %s: %v\n", filename, err)
		return
	}
	fmt.Printf("✅ Generated: %s\n", filename)
}
