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 // Untuk backward compatibility (bagian pertama)
	DirPath       string // Path direktori lengkap
	ModuleName    string
	TableName     string
	HasGet        bool
	HasPost       bool
	HasPut        bool
	HasDelete     bool
	HasStats      bool
	HasDynamic    bool
	HasSearch     bool
	HasFilter     bool
	HasPagination bool
	Timestamp     string
}

type PathInfo struct {
	Category   string
	EntityName string
	DirPath    string
	FilePath   string
}

// parseEntityPath - Logic parsing yang diperbaiki
func parseEntityPath(entityPath string) (*PathInfo, error) {
	if strings.TrimSpace(entityPath) == "" {
		return nil, fmt.Errorf("entity path cannot be empty")
	}
	var pathInfo PathInfo
	parts := strings.Split(entityPath, "/")
	// Validasi minimal 1 bagian (file saja) dan maksimal 4
	if len(parts) < 1 || len(parts) > 4 {
		return nil, fmt.Errorf("invalid path format: use up to 4 levels like 'level1/level2/level3/entity'")
	}
	// Validasi bagian kosong
	for i, part := range parts {
		if strings.TrimSpace(part) == "" {
			return nil, fmt.Errorf("empty path segment at position %d", i+1)
		}
	}

	pathInfo.EntityName = parts[len(parts)-1]
	if len(parts) > 1 {
		pathInfo.Category = parts[len(parts)-2]
		pathInfo.DirPath = strings.Join(parts[:len(parts)-1], "/")
		pathInfo.FilePath = pathInfo.DirPath + "/" + strings.ToLower(pathInfo.EntityName) + ".go"
	} else {
		pathInfo.Category = "models"
		pathInfo.DirPath = ""
		pathInfo.FilePath = strings.ToLower(pathInfo.EntityName) + ".go"
	}
	return &pathInfo, nil
}

// validateMethods - Validasi method yang diinput
func validateMethods(methods []string) error {
	validMethods := map[string]bool{
		"get": true, "post": true, "put": true, "delete": true,
		"stats": true, "dynamic": true, "search": true,
	}

	for _, method := range methods {
		if !validMethods[strings.ToLower(method)] {
			return fmt.Errorf("invalid method: %s. Valid methods: get, post, put, delete, stats, dynamic, search", method)
		}
	}
	return nil
}

// generateTableName - Generate table name berdasarkan path lengkap
func generateTableName(pathInfo *PathInfo) string {
	entityLower := strings.ToLower(pathInfo.EntityName)

	if pathInfo.DirPath != "" {
		// Replace "/" dengan "_" untuk table name
		pathForTable := strings.ReplaceAll(pathInfo.DirPath, "/", "_")
		return "data_" + pathForTable + "_" + entityLower
	}
	return "data_" + entityLower
}

// createDirectories - Buat direktori sesuai struktur path
func createDirectories(pathInfo *PathInfo) (string, string, error) {
	var handlerDir, modelDir string

	if pathInfo.DirPath != "" {
		handlerDir = filepath.Join("internal", "handlers", pathInfo.DirPath)
		modelDir = filepath.Join("internal", "models", pathInfo.DirPath)
	} else {
		handlerDir = filepath.Join("internal", "handlers")
		modelDir = filepath.Join("internal", "models")
	}

	// Create directories
	for _, dir := range []string{handlerDir, modelDir} {
		if err := os.MkdirAll(dir, 0755); err != nil {
			return "", "", fmt.Errorf("failed to create directory %s: %v", dir, err)
		}
	}

	return handlerDir, modelDir, nil
}

// setMethods - Set method flags berdasarkan input
func setMethods(data *HandlerData, methods []string) {
	methodMap := map[string]*bool{
		"get":     &data.HasGet,
		"post":    &data.HasPost,
		"put":     &data.HasPut,
		"delete":  &data.HasDelete,
		"stats":   &data.HasStats,
		"dynamic": &data.HasDynamic,
		"search":  &data.HasSearch,
	}

	for _, method := range methods {
		if flag, exists := methodMap[strings.ToLower(method)]; exists {
			*flag = true
		}
	}

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

func main() {
	// Validasi argument
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run generate-handler.go [path/]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")
		fmt.Println("  go run generate-handler.go product/category/subcategory/item get post")
		fmt.Println("\nSupported methods: get, post, put, delete, stats, dynamic, search")
		os.Exit(1)
	}

	// Parse entity path
	entityPath := strings.TrimSpace(os.Args[1])
	pathInfo, err := parseEntityPath(entityPath)
	if err != nil {
		fmt.Printf("❌ Error parsing path: %v\n", err)
		os.Exit(1)
	}

	// Parse methods
	var 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"}
	}

	// Validate methods
	if err := validateMethods(methods); err != nil {
		fmt.Printf("❌ %v\n", err)
		os.Exit(1)
	}

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

	// Generate table name
	tableName := generateTableName(pathInfo)

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

	// Set methods
	setMethods(&data, methods)

	// Create directories
	handlerDir, modelDir, err := createDirectories(pathInfo)
	if err != nil {
		fmt.Printf("❌ Error creating directories: %v\n", err)
		os.Exit(1)
	}

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

	// Success output
	fmt.Printf("✅ Successfully generated handler: %s\n", entityName)
	if pathInfo.Category != "" {
		fmt.Printf("📁 Category: %s\n", pathInfo.Category)
	}
	if pathInfo.DirPath != "" {
		fmt.Printf("📂 Path: %s\n", pathInfo.DirPath)
	}
	fmt.Printf("📄 Handler: %s\n", filepath.Join(handlerDir, entityLower+".go"))
	fmt.Printf("📄 Model: %s\n", filepath.Join(modelDir, entityLower+".go"))
	fmt.Printf("🗄️  Table: %s\n", tableName)
	fmt.Printf("🛠️  Methods: %s\n", strings.Join(methods, ", "))
}

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

	// pakai strings.Builder biar lebih clean
	var handlerContent strings.Builder

	// Header
	handlerContent.WriteString("package handlers\n\n")
	handlerContent.WriteString("import (\n")
	handlerContent.WriteString(`    "` + data.ModuleName + `/internal/config"` + "\n")
	handlerContent.WriteString(`    "` + data.ModuleName + `/internal/database"` + "\n")
	handlerContent.WriteString(`    models "` + data.ModuleName + `/internal/models"` + "\n")
	if data.Category != "models" {
		handlerContent.WriteString(`    "` + data.ModuleName + `/internal/models/` + data.Category + `"` + "\n")
	}

	// Conditional imports
	if data.HasDynamic || data.HasSearch {
		handlerContent.WriteString(`    utils "` + data.ModuleName + `/internal/utils/filters"` + "\n")
	}

	handlerContent.WriteString(`    "` + data.ModuleName + `/internal/utils/validation"` + "\n")
	handlerContent.WriteString(`    "context"` + "\n")
	handlerContent.WriteString(`    "database/sql"` + "\n")
	handlerContent.WriteString(`    "fmt"` + "\n")
	handlerContent.WriteString(`    "log"` + "\n")
	handlerContent.WriteString(`    "net/http"` + "\n")
	handlerContent.WriteString(`    "strconv"` + "\n")
	handlerContent.WriteString(`    "strings"` + "\n")
	handlerContent.WriteString(`    "sync"` + "\n")
	handlerContent.WriteString(`    "time"` + "\n\n")
	handlerContent.WriteString(`    "github.com/gin-gonic/gin"` + "\n")
	handlerContent.WriteString(`    "github.com/go-playground/validator/v10"` + "\n")
	handlerContent.WriteString(`    "github.com/google/uuid"` + "\n")
	handlerContent.WriteString(")\n\n")

	// Vars
	handlerContent.WriteString("var (\n")
	handlerContent.WriteString("    " + data.NameLower + "db       database.Service\n")
	handlerContent.WriteString("    " + data.NameLower + "once     sync.Once\n")
	handlerContent.WriteString("    " + data.NameLower + "validate *validator.Validate\n")
	handlerContent.WriteString(")\n\n")

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

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

	// Handler struct
	handlerContent.WriteString("// " + data.Name + "Handler handles " + data.NameLower + " services\n")
	handlerContent.WriteString("type " + data.Name + "Handler struct {\n")
	handlerContent.WriteString("    db database.Service\n")
	handlerContent.WriteString("}\n\n")

	// Constructor
	handlerContent.WriteString("// New" + data.Name + "Handler creates a new " + data.Name + "Handler\n")
	handlerContent.WriteString("func New" + data.Name + "Handler() *" + data.Name + "Handler {\n")
	handlerContent.WriteString("    return &" + data.Name + "Handler{\n")
	handlerContent.WriteString("        db: " + data.NameLower + "db,\n")
	handlerContent.WriteString("    }\n")
	handlerContent.WriteString("}\n")

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

	// Add helper methods
	handlerContent.WriteString(generateHelperMethods(data))

	// Write into file
	writeFile(filepath.Join(handlerDir, data.NameLower+".go"), handlerContent.String())
}

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.Name + `
// @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} ` + data.Category + `.` + data.Name + `GetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.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         []` + data.Category + `.` + 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 := ` + data.Category + `.` + data.Name + `GetResponse{
		Message: "Data ` + data.Category + ` 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.Name + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Success 200 {object} ` + data.Category + `.` + data.Name + `GetByIDResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models.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 := ` + data.Category + `.` + data.Name + `GetByIDResponse{
		Message: "` + data.Category + ` 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.Name + `
// @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} ` + data.Category + `.` + data.Name + `GetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.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 := ` + data.Category + `.` + data.Name + `GetResponse{
		Message: "Data ` + data.Category + ` 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 := ` + data.Category + `.` + 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.Name + `
// @Accept json
// @Produce json
// @Param request body ` + data.Category + `.` + data.Name + `CreateRequest true "` + data.Name + ` creation request"
// @Success 201 {object} ` + data.Category + `.` + data.Name + `CreateResponse "` + data.Name + ` created successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/` + data.NamePlural + ` [post]
func (h *` + data.Name + `Handler) Create` + data.Name + `(c *gin.Context) {
	var req ` + data.Category + `.` + data.Name + `CreateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
		return
	}

	// Validate request
	if err := ` + data.NameLower + `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 := ` + data.Category + `.` + 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.Name + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Param request body ` + data.Category + `.` + data.Name + `UpdateRequest true "` + data.Name + ` update request"
// @Success 200 {object} ` + data.Category + `.` + data.Name + `UpdateResponse "` + data.Name + ` updated successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 404 {object} models.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models.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 ` + data.Category + `.` + 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 := ` + data.NameLower + `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 := ` + data.Category + `.` + 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.Name + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID (UUID)"
// @Success 200 {object} ` + data.Category + `.` + data.Name + `DeleteResponse "` + data.Name + ` deleted successfully"
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "` + data.Name + ` not found"
// @Failure 500 {object} models.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 := ` + data.Category + `.` + 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.Name + `
// @Accept json
// @Produce json
// @Param status query string false "Filter statistics by status"
// @Success 200 {object} models.AggregateData "Statistics data"
// @Failure 500 {object} models.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) (*` + data.Category + `.` + 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 ` + data.Category + `.` + 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 *` + data.Category + `.` + data.Name + `CreateRequest) (*` + data.Category + `.` + 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 ` + data.Category + `.` + 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 *` + data.Category + `.` + data.Name + `UpdateRequest) (*` + data.Category + `.` + 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 ` + data.Category + `.` + 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 ` + data.Category + `.` + data.Name + `Filter, limit, offset int) ([]` + data.Category + `.` + 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([]` + data.Category + `.` + 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) ([]` + data.Category + `.` + 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   [] ` + data.Category + `.` + 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 []` + data.Category + `.` + 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
}
`
	}

	helperMethods += `
// Optimized scanning function
func (h *` + data.Name + `Handler) scan` + data.Name + `(rows *sql.Rows) (` + data.Category + `.` + data.Name + `, error) {
	var item ` + data.Category + `.` + data.Name + `
	
	// Scan into individual fields to handle nullable types properly
	err := rows.Scan(
		&item.ID, 
		&item.Status, 
		&item.Sort.Int32, &item.Sort.Valid, // models.NullableInt32
		&item.UserCreated.String, &item.UserCreated.Valid, // sql.NullString
		&item.DateCreated.Time, &item.DateCreated.Valid, // sql.NullTime
		&item.UserUpdated.String, &item.UserUpdated.Valid, // sql.NullString
		&item.DateUpdated.Time, &item.DateUpdated.Valid, // sql.NullTime
		&item.Name.String, &item.Name.Valid, // sql.NullString
	)
	
	return item, err
}

func (h *` + data.Name + `Handler) getTotalCount(ctx context.Context, dbConn *sql.DB, filter ` + data.Category + `.` + 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 ` + data.Category + `.` + 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) ` + data.Category + `.` + data.Name + `Filter {
	filter := ` + data.Category + `.` + 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 ` + data.Category + `.` + 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 *` + data.Category + `.` + 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 *` + data.Category + `.` + 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) {
	// Tentukan import block
	var importBlock, nullablePrefix string

	if data.Category == "models" {
		importBlock = `import (
    "database/sql"
    "encoding/json"
    "time"
)
`
	} else {
		nullablePrefix = "models."
		importBlock = `import (
    "` + data.ModuleName + `/internal/models"
    "database/sql"
    "encoding/json"
    "time"
)
`
	}

	modelContent := `package ` + data.Category + `

` + importBlock + `

// ` + 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         ` + nullablePrefix + "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),
    }

    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
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    ` + nullablePrefix + "MetaResponse    `json:\"meta\"`" + `
    Summary *` + nullablePrefix + "AggregateData  `json:\"summary,omitempty\"`" + `
}
`
	}
	if data.HasPost {
		modelContent += `

// Request struct untuk create
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
type ` + data.Name + `UpdateRequest struct {
    ID     string   ` + "`json:\"-\" validate:\"required,uuid4\"`" + `
    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 != "models" {
		importPath = fmt.Sprintf("%s/internal/handlers/"+data.Category, data.ModuleName)
		importAlias = data.Category + data.Name + "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 {
	// fmt.Printf("📁 Group Part: %s\n", groupPath)
	var sb strings.Builder
	var importPath, groupPath string
	if data.Category != "models" {
		importPath = data.Category + data.Name
		groupPath = strings.ToLower(data.Category) + "/" + data.NameLower
	} else {
		importPath = data.NameLower
		groupPath = data.NameLower
	}
	// Komentar dan deklarasi handler & grup
	sb.WriteString("// ")
	sb.WriteString(data.Name)
	sb.WriteString(" endpoints\n")
	sb.WriteString("    ")
	sb.WriteString(importPath)
	sb.WriteString("Handler := ")
	sb.WriteString(importPath)
	sb.WriteString("Handlers.New")
	sb.WriteString(data.Name)
	sb.WriteString("Handler()\n    ")
	sb.WriteString(importPath)

	sb.WriteString("Group := v1.Group(\"/")
	sb.WriteString(groupPath)
	sb.WriteString("\")\n    {\n        ")
	sb.WriteString(importPath)
	sb.WriteString("Group.GET(\"\", ")
	sb.WriteString(importPath)
	sb.WriteString("Handler.Get")
	sb.WriteString(data.Name)
	sb.WriteString(")\n")

	if data.HasDynamic {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.GET(\"/dynamic\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Get")
		sb.WriteString(data.Name)
		sb.WriteString("Dynamic)    // Route baru\n")
	}
	if data.HasSearch {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.GET(\"/search\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Search")
		sb.WriteString(data.Name)
		sb.WriteString("Advanced) // Route pencarian\n")
	}
	sb.WriteString("        ")
	sb.WriteString(importPath)
	sb.WriteString("Group.GET(\"/:id\", ")
	sb.WriteString(importPath)
	sb.WriteString("Handler.Get")
	sb.WriteString(data.Name)
	sb.WriteString("ByID)\n")

	if data.HasPost {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.POST(\"\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Create")
		sb.WriteString(data.Name)
		sb.WriteString(")\n")
	}
	if data.HasPut {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.PUT(\"/:id\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Update")
		sb.WriteString(data.Name)
		sb.WriteString(")\n")
	}
	if data.HasDelete {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.DELETE(\"/:id\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Delete")
		sb.WriteString(data.Name)
		sb.WriteString(")\n")
	}
	if data.HasStats {
		sb.WriteString("        ")
		sb.WriteString(importPath)
		sb.WriteString("Group.GET(\"/stats\", ")
		sb.WriteString(importPath)
		sb.WriteString("Handler.Get")
		sb.WriteString(data.Name)
		sb.WriteString("Stats)\n")
	}
	sb.WriteString("    }\n")
	return sb.String()
}

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)
}
