package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"gopkg.in/yaml.v2"
)

// runSwagInit runs the swag init command to generate swagger docs
func runSwagInit() error {
	cmd := exec.Command("swag", "init", "-g", "../../cmd/api/main.go", "--parseDependency", "--parseInternal")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// ServiceConfig represents the main configuration structure
type ServiceConfig struct {
	Services map[string]Service `yaml:"services"`
	Global   GlobalConfig       `yaml:"global,omitempty"`
}

// GlobalConfig contains global configuration
type GlobalConfig struct {
	ModuleName    string `yaml:"module_name"`
	OutputDir     string `yaml:"output_dir"`
	PackagePrefix string `yaml:"package_prefix"`
	EnableSwagger bool   `yaml:"enable_swagger"`
	EnableLogging bool   `yaml:"enable_logging"`
	EnableMetrics bool   `yaml:"enable_metrics"`
}

// Service represents individual service configuration
type Service struct {
	Name         string                  `yaml:"name"`
	Category     string                  `yaml:"category"`
	Package      string                  `yaml:"package"`
	SubPackage   string                  `yaml:"sub_package"`
	Description  string                  `yaml:"description"`
	BaseURL      string                  `yaml:"base_url"`
	Timeout      int                     `yaml:"timeout"`
	RetryCount   int                     `yaml:"retry_count"`
	Endpoints    map[string]SubEndpoints `yaml:"endpoints"`
	Middleware   []string                `yaml:"middleware,omitempty"`   // FIXED: Changed to []string
	Dependencies []string                `yaml:"dependencies,omitempty"` // FIXED: Changed to []string
}

// Endpoint represents endpoint configuration
type Endpoint struct {
	Methods       []string          `yaml:"methods"`
	GetPath       string            `yaml:"get_path,omitempty"`
	PostPath      string            `yaml:"post_path,omitempty"`
	PutPath       string            `yaml:"put_path,omitempty"`
	DeletePath    string            `yaml:"delete_path,omitempty"`
	PatchPath     string            `yaml:"patch_path,omitempty"`
	Model         string            `yaml:"model"`
	ResponseModel string            `yaml:"response_model"`
	Description   string            `yaml:"description"`
	Summary       string            `yaml:"summary"`
	Tags          []string          `yaml:"tags"`
	RequireAuth   bool              `yaml:"require_auth"`
	RateLimit     int               `yaml:"rate_limit,omitempty"`
	CacheEnabled  bool              `yaml:"cache_enabled"`
	CacheTTL      int               `yaml:"cache_ttl,omitempty"`
	CustomHeaders map[string]string `yaml:"custom_headers,omitempty"`
}

// SubEndpoints represents nested endpoint configuration for tree structure
type SubEndpoints map[string]Endpoint

// TemplateData holds data for generating handlers
type TemplateData struct {
	ServiceName  string
	ServiceLower string
	ServiceUpper string
	Category     string
	Package      string
	Description  string
	BaseURL      string
	Timeout      int
	RetryCount   int
	Endpoints    []EndpointData
	Timestamp    string
	ModuleName   string
	HasValidator bool
	HasLogger    bool
	HasMetrics   bool
	HasSwagger   bool
	HasAuth      bool
	HasCache     bool
	Dependencies []string // FIXED: Changed to []string
	Middleware   []string // FIXED: Changed to []string
	GlobalConfig GlobalConfig
}

// EndpointData represents processed endpoint data
type EndpointData struct {
	Name           string
	NameLower      string
	NameUpper      string
	NameCamel      string
	Methods        []string
	GetPath        string
	PostPath       string
	PutPath        string
	DeletePath     string
	PatchPath      string
	Model          string
	ResponseModel  string
	DataModel      string // Data model name (e.g., PesertaData)
	Description    string
	Summary        string
	Tags           []string
	HasGet         bool
	HasPost        bool
	HasPut         bool
	HasDelete      bool
	HasPatch       bool
	RequireAuth    bool
	RateLimit      int
	CacheEnabled   bool
	CacheTTL       int
	PathParams     []string
	QueryParams    []string
	RequiredFields []string
	OptionalFields []string
	CustomHeaders  map[string]string
	ModelPackage   string // Package name for the model (peserta, sep, etc.)
}

// Updated template for merged handler structure
const handlerTemplate = `
// Service: {{.ServiceName}} ({{.Category}})
// Description: {{.Description}}

package {{.Package}}

import (
	"context"
	"encoding/json"
	"strings"
	"net/http"
	"time"

	"{{.ModuleName}}/internal/config"
	"{{.ModuleName}}/internal/models"
	"{{.ModuleName}}/internal/models/vclaim/{{.Package}}"
	"{{.ModuleName}}/internal/services/bpjs"
	"{{.ModuleName}}/pkg/logger"

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

// {{.ServiceName}}Handler handles {{.ServiceName}} BPJS services
type {{.ServiceName}}Handler struct {
	service   services.{{.ServiceName}}Service
	validator *validator.Validate
	logger    logger.Logger
	config    config.BpjsConfig
}

// {{.ServiceName}}HandlerConfig contains configuration for {{.ServiceName}}Handler
type {{.ServiceName}}HandlerConfig struct {
	BpjsConfig config.BpjsConfig
	Logger     logger.Logger
	Validator  *validator.Validate
}

// New{{.ServiceName}}Handler creates a new {{.ServiceName}}Handler
func New{{.ServiceName}}Handler(cfg {{.ServiceName}}HandlerConfig) *{{.ServiceName}}Handler {
	return &{{.ServiceName}}Handler{
		service:   services.NewService(cfg.BpjsConfig),
		validator: cfg.Validator,
		logger:    cfg.Logger,
		config:    cfg.BpjsConfig,
	}
}
{{range .Endpoints}}
{{if .HasGet}}
// Get{{.Name}} godoc
// @Summary      Get {{.Name}} data
// @Description  {{.Description}}
// @Tags         {{join .Tags ","}}
// @Accept       json
// @Produce      json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param        X-Request-ID    header    string                          false  "Request ID for tracking" {{range .PathParams}}
// @Param        {{.}}           path      string                          true   "{{.}}" example("example_value") {{end}}
// @Success      200             {object}  {{.ModelPackage}}.{{.ResponseModel}} "Successfully retrieved {{.Name}} data"
// @Failure      400             {object}  models.ErrorResponseBpjs        "Bad request - invalid parameters"
// @Failure      401             {object}  models.ErrorResponseBpjs        "Unauthorized - invalid API credentials"
// @Failure      404             {object}  models.ErrorResponseBpjs        "Not found - {{.Name}} not found"
// @Failure      500             {object}  models.ErrorResponseBpjs        "Internal server error"
// @Router       {{.GetPath}} [get]
func (h *{{$.ServiceName}}Handler) Get{{.Name}}(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second)
	defer cancel()

	// Generate request ID if not present
	requestID := c.GetHeader("X-Request-ID")
	if requestID == "" {
		requestID = uuid.New().String()
		c.Header("X-Request-ID", requestID)
	}

	{{if $.HasLogger}}
	h.logger.Info("Processing Get{{.Name}} request", map[string]interface{}{
		"request_id": requestID,
		"endpoint":   "{{.GetPath}}",
		{{range .PathParams}}
		"{{.}}": c.Param("{{.}}"),
		{{end}}
	})
	{{end}}

	// Extract path parameters
	{{range .PathParams}}
	{{.}} := c.Param("{{.}}")
	if {{.}} == "" {
		{{if $.HasLogger}}
		h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Missing required parameter {{.}}",
			RequestID: requestID,
		})
		return
	}
	{{end}}

	// Call service method
	var response {{.ModelPackage}}.{{.ResponseModel}}
	{{if .PathParams}}
	endpoint := "{{.GetPath}}"
	{{range .PathParams}}
	endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
	{{end}}
	rawResponse, err := h.service.GetRawResponse(ctx, endpoint)
	{{else}}
	rawResponse, err := h.service.GetRawResponse(ctx, "{{.GetPath}}")
	{{end}}
	if err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to get {{.Name}}", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Internal server error",
			RequestID: requestID,
		})
		return
	}

	// Map raw response to structured data
	var data {{.ModelPackage}}.{{.DataModel}}
	if err := json.Unmarshal(rawResponse, &data); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to unmarshal {{.Name}} data", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Failed to process response data",
			RequestID: requestID,
		})
		return
	}

	// Set response data
	response.Data = data
	response.Status = "success"
	response.RequestID = requestID
	c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasPost}}
// Create{{.Name}} godoc
// @Summary      Create new {{.Name}}
// @Description  {{.Description}}
// @Tags         {{join .Tags ","}}
// @Accept       json
// @Produce      json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param        X-Request-ID    header    string                          false  "Request ID for tracking"
// @Param        request         body      {{.ModelPackage}}.{{.Model}}     true   "{{.Name}} data"
// @Success      201             {object}  {{.ModelPackage}}.{{.ResponseModel}} "Successfully created {{.Name}}"
// @Failure      400             {object}  models.ErrorResponseBpjs        "Bad request - invalid request body or validation error"
// @Failure      401             {object}  models.ErrorResponseBpjs        "Unauthorized - invalid API credentials"
// @Failure      409             {object}  models.ErrorResponseBpjs        "Conflict - {{.Name}} already exists"
// @Failure      500             {object}  models.ErrorResponseBpjs        "Internal server error"
// @Router       {{.PostPath}} [post]
func (h *{{$.ServiceName}}Handler) Create{{.Name}}(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second)
	defer cancel()

	requestID := c.GetHeader("X-Request-ID")
	if requestID == "" {
		requestID = uuid.New().String()
		c.Header("X-Request-ID", requestID)
	}

	{{if $.HasLogger}}
	h.logger.Info("Processing Create{{.Name}} request", map[string]interface{}{
		"request_id": requestID,
	})
	{{end}}

	var req {{.ModelPackage}}.{{.Model}}
	if err := c.ShouldBindJSON(&req); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Invalid request body", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Invalid request body: " + err.Error(),
			RequestID: requestID,
		})
		return
	}

	// Validate request
	if err := h.validator.Struct(&req); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Validation failed", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Validation failed: " + err.Error(),
			RequestID: requestID,
		})
		return
	}

	// Call service method
	var response {{.ModelPackage}}.{{.ResponseModel}}
	rawResponse, err := h.service.PostRawResponse(ctx, "{{.PostPath}}", &req)
	if err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Internal server error",
			RequestID: requestID,
		})
		return
	}

	// Map raw response to structured data
	var data {{.ModelPackage}}.{{.DataModel}}
	if err := json.Unmarshal(rawResponse, &data); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to unmarshal {{.Name}} data", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Failed to process response data",
			RequestID: requestID,
		})
		return
	}

	// Set response data
	response.Data = data
	response.Status = "success"
	response.RequestID = requestID
	c.JSON(http.StatusCreated, response)
}
{{end}}

{{if .HasPut}}
// Update{{.Name}} godoc
// @Summary      Update existing {{.Name}}
// @Description  {{.Description}}
// @Tags         {{join .Tags ","}}
// @Accept       json
// @Produce      json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param        X-Request-ID    header    string                          false  "Request ID for tracking" {{range .PathParams}}
// @Param        {{.}}           path      string                          true   "{{.}}" example("example_value") {{end}}
// @Param        request         body      {{.ModelPackage}}.{{.Model}}     true   "{{.Name}} data"
// @Success      200             {object}  {{.ModelPackage}}.{{.ResponseModel}} "Successfully updated {{.Name}}"
// @Failure      400             {object}  models.ErrorResponseBpjs        "Bad request - invalid parameters or request body"
// @Failure      401             {object}  models.ErrorResponseBpjs        "Unauthorized - invalid API credentials"
// @Failure      404             {object}  models.ErrorResponseBpjs        "Not found - {{.Name}} not found"
// @Failure      500             {object}  models.ErrorResponseBpjs        "Internal server error"
// @Router       {{.PutPath}} [put]
func (h *{{$.ServiceName}}Handler) Update{{.Name}}(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second)
	defer cancel()

	requestID := c.GetHeader("X-Request-ID")
	if requestID == "" {
		requestID = uuid.New().String()
		c.Header("X-Request-ID", requestID)
	}

	{{if $.HasLogger}}
	h.logger.Info("Processing Update{{.Name}} request", map[string]interface{}{
		"request_id": requestID,
	})
	{{end}}

	{{range .PathParams}}
	{{.}} := c.Param("{{.}}")
	if {{.}} == "" {
		{{if $.HasLogger}}
		h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Missing required parameter {{.}}",
			RequestID: requestID,
		})
		return
	}
	{{end}}

	var req {{.ModelPackage}}.{{.Model}}
	if err := c.ShouldBindJSON(&req); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Invalid request body", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Invalid request body: " + err.Error(),
			RequestID: requestID,
		})
		return
	}

	// Validate request
	if err := h.validator.Struct(&req); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Validation failed", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Validation failed: " + err.Error(),
			RequestID: requestID,
		})
		return
	}

	// Call service method
	var response {{.ModelPackage}}.{{.ResponseModel}}
	{{if .PathParams}}
	endpoint := "{{.PutPath}}"
	{{range .PathParams}}
	endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
	{{end}}
	rawResponse, err := h.service.PutRawResponse(ctx, endpoint, &req)
	{{else}}
	rawResponse, err := h.service.PutRawResponse(ctx, "{{.PutPath}}", &req)
	{{end}}
	if err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to update {{.Name}}", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Internal server error",
			RequestID: requestID,
		})
		return
	}

	// Map raw response to structured data
	var data {{.ModelPackage}}.{{.DataModel}}
	if err := json.Unmarshal(rawResponse, &data); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to unmarshal {{.Name}} data", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Failed to process response data",
			RequestID: requestID,
		})
		return
	}

	// Set response data
	response.Data = data
	response.Status = "success"
	response.RequestID = requestID
	c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasDelete}}
// Delete{{.Name}} godoc
// @Summary      Delete existing {{.Name}}
// @Description  {{.Description}}
// @Tags         {{join .Tags ","}}
// @Accept       json
// @Produce      json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param        X-Request-ID    header    string                        false  "Request ID for tracking" {{range .PathParams}}
// @Param        {{.}}           path      string                         true   "{{.}}" example("example_value") {{end}} 
// @Success      200             {object}  {{.ModelPackage}}.{{.ResponseModel}} "Successfully deleted {{.Name}}"
// @Failure      400             {object}  models.ErrorResponseBpjs        "Bad request - invalid parameters"
// @Failure      401             {object}  models.ErrorResponseBpjs        "Unauthorized - invalid API credentials"
// @Failure      404             {object}  models.ErrorResponseBpjs        "Not found - {{.Name}} not found"
// @Failure      500             {object}  models.ErrorResponseBpjs        "Internal server error"
// @Router       {{.DeletePath}} [delete]
func (h *{{$.ServiceName}}Handler) Delete{{.Name}}(c *gin.Context) {
	ctx, cancel := context.WithTimeout(c.Request.Context(), {{$.Timeout}}*time.Second)
	defer cancel()

	requestID := c.GetHeader("X-Request-ID")
	if requestID == "" {
		requestID = uuid.New().String()
		c.Header("X-Request-ID", requestID)
	}

	{{if $.HasLogger}}
	h.logger.Info("Processing Delete{{.Name}} request", map[string]interface{}{
		"request_id": requestID,
	})
	{{end}}

	{{range .PathParams}}
	{{.}} := c.Param("{{.}}")
	if {{.}} == "" {
		{{if $.HasLogger}}
		h.logger.Error("Missing required parameter {{.}}", map[string]interface{}{
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusBadRequest, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Missing required parameter {{.}}",
			RequestID: requestID,
		})
		return
	}
	{{end}}

	// Call service method
	var response {{.ModelPackage}}.{{.ResponseModel}}
	{{if .PathParams}}
	endpoint := "{{.DeletePath}}"
	{{range .PathParams}}
	endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
	{{end}}
	rawResponse, err := h.service.DeleteRawResponse(ctx, endpoint)
	{{else}}
	rawResponse, err := h.service.DeleteRawResponse(ctx, "{{.DeletePath}}")
	{{end}}
	if err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to delete {{.Name}}", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Internal server error",
			RequestID: requestID,
		})
		return
	}

	// Map raw response to structured data
	var data {{.ModelPackage}}.{{.DataModel}}
	if err := json.Unmarshal(rawResponse, &data); err != nil {
		{{if $.HasLogger}}
		h.logger.Error("Failed to unmarshal {{.Name}} data", map[string]interface{}{
			"error":      err.Error(),
			"request_id": requestID,
		})
		{{end}}
		c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
			Status:    "error",
			Message:   "Failed to process response data",
			RequestID: requestID,
		})
		return
	}

	// Set response data
	response.Data = data
	response.Status = "success"
	response.RequestID = requestID
	c.JSON(http.StatusOK, response)
}
{{end}}
{{end}}
`

// Template for generating routes
const routesTemplate = `
package v1

import (
	"{{.ModuleName}}/internal/config"
	"{{.ModuleName}}/internal/middleware"
	"{{.ModuleName}}/internal/handlers/{{.Category}}/{{.Package}}"
	"github.com/gin-gonic/gin"
)

func Register{{.ServiceName}}Routes(router *gin.RouterGroup, cfg *config.Config) {
	handler := {{.Package}}.New{{.ServiceName}}Handler({{.Package}}.{{.ServiceName}}HandlerConfig{
		BpjsConfig: cfg.Bpjs,
		Logger:     *logger.Default(),
		Validator:  nil,
	})

	group := router.Group("/{{.ServiceLower}}")

	{{range .Endpoints}}
	{{if .HasGet}}
	group.GET("{{.GetPath}}", handler.Get{{.Name}})
	{{end}}
	{{if .HasPost}}
	group.POST("{{.PostPath}}", handler.Create{{.Name}})
	{{end}}
	{{if .HasPut}}
	group.PUT("{{.PutPath}}", handler.Update{{.Name}})
	{{end}}
	{{if .HasDelete}}
	group.DELETE("{{.DeletePath}}", handler.Delete{{.Name}})
	{{end}}
	{{if .HasPatch}}
	group.PATCH("{{.PatchPath}}", handler.Patch{{.Name}})
	{{end}}
	{{end}}
}
`

func main() {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}

	configFile := os.Args[1]
	var targetService string
	if len(os.Args) > 2 {
		targetService = os.Args[2]
	}

	// Load configuration
	config, err := loadConfig(configFile)
	if err != nil {
		fmt.Printf("❌ Error loading config: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("🚀 Starting BPJS Dynamic Handler Generation...")
	fmt.Printf("📁 Config file: %s\n", configFile)
	if targetService != "" {
		fmt.Printf("🎯 Target service: %s\n", targetService)
	}

	generated := 0
	errors := 0

	// Generate handlers
	for serviceName, service := range config.Services {
		if targetService != "" && serviceName != targetService {
			continue
		}

		fmt.Printf("🔧 Generating handler for service: %s (%s)\n", service.Name, service.Category)
		err := generateHandler(serviceName, service, config.Global)
		if err != nil {
			fmt.Printf("❌ Error generating handler for %s: %v\n", serviceName, err)
			errors++
			continue
		}
		fmt.Printf("✅ Generated handler: %s.go\n", strings.ToLower(serviceName))

		// Generate routes for this service
		err = generateRoutes(serviceName, service, config.Global)
		if err != nil {
			fmt.Printf("❌ Error generating routes for %s: %v\n", serviceName, err)
			errors++
			continue
		}
		fmt.Printf("✅ Generated routes: %s_routes.go\n", strings.ToLower(serviceName))
		generated++
	}

	// Summary
	fmt.Println("\n📊 Generation Summary:")
	fmt.Printf("   ✅ Successfully generated: %d handlers and routes\n", generated)
	if errors > 0 {
		fmt.Printf("   ❌ Failed: %d handlers/routes\n", errors)
	}

	if generated > 0 {
		fmt.Println("🎉 Generation completed successfully!")

		// Generate Swagger documentation if enabled
		// if config.Global.EnableSwagger {
		// 	fmt.Println("📚 Generating Swagger documentation...")
		// 	if err := runSwagInit(); err != nil {
		// 		fmt.Printf("⚠️  Warning: Failed to generate Swagger docs: %v\n", err)
		// 	} else {
		// 		fmt.Println("✅ Swagger documentation generated successfully!")
		// 	}
		// }
	}
}

func printUsage() {
	fmt.Println("BPJS Dynamic Handler Generator")
	fmt.Println()
	fmt.Println("Usage:")
	fmt.Println("  go run generate-dynamic-handler.go <config-file> [service-name]")
	fmt.Println()
	fmt.Println("Examples:")
	fmt.Println("  go run generate-dynamic-handler.go services-config.yaml")
	fmt.Println("  go run generate-dynamic-handler.go services-config.yaml vclaim")
}

func loadConfig(filename string) (*ServiceConfig, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file: %w", err)
	}

	var config ServiceConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, fmt.Errorf("failed to parse YAML config: %w", err)
	}

	// Set default values
	if config.Global.ModuleName == "" {
		config.Global.ModuleName = "api-service"
	}
	if config.Global.OutputDir == "" {
		config.Global.OutputDir = "internal/handlers"
	}

	return &config, nil
}

func generateHandler(serviceName string, service Service, globalConfig GlobalConfig) error {
	// Create template with custom functions
	tmpl := template.New("handler").Funcs(template.FuncMap{
		"contains":   strings.Contains,
		"join":       strings.Join,
		"title":      strings.Title,
		"trimPrefix": strings.TrimPrefix,
	})

	tmpl, err := tmpl.Parse(handlerTemplate)
	if err != nil {
		return fmt.Errorf("failed to parse template: %w", err)
	}

	outputDir := globalConfig.OutputDir
	if outputDir == "" {
		outputDir = "internal/handlers"
	}

	generatedFiles := 0
	errorsCount := 0

	// Loop over each module group in service.Endpoints
	for groupName, subEndpoints := range service.Endpoints {
		// Prepare template data for this group
		templateData := TemplateData{
			ServiceName:  service.Name,
			ServiceLower: strings.ToLower(service.Name),
			ServiceUpper: strings.ToUpper(service.Name),
			Category:     service.Category,
			Package:      groupName, // package name is group name
			Description:  service.Description,
			BaseURL:      service.BaseURL,
			Timeout:      getOrDefault(service.Timeout, 30),
			RetryCount:   getOrDefault(service.RetryCount, 3),
			Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
			ModuleName:   globalConfig.ModuleName,
			HasValidator: true,
			HasLogger:    globalConfig.EnableLogging,
			HasMetrics:   globalConfig.EnableMetrics,
			HasSwagger:   globalConfig.EnableSwagger,
			Dependencies: service.Dependencies,
			Middleware:   service.Middleware,
			GlobalConfig: globalConfig,
		}

		// Check for advanced features in this group
		for _, endpoint := range subEndpoints {
			if endpoint.RequireAuth {
				templateData.HasAuth = true
			}
			if endpoint.CacheEnabled {
				templateData.HasCache = true
			}
		}

		// Process endpoints for this group
		for subEndpointName, endpoint := range subEndpoints {
			fullEndpointName := groupName
			if subEndpointName != "" {
				fullEndpointName = fullEndpointName + strings.Title(subEndpointName)
			}
			endpointData := processEndpoint(fullEndpointName, endpoint, groupName)
			templateData.Endpoints = append(templateData.Endpoints, endpointData)
		}

		// Create output directory for this group: outputDir/service.Package/groupName
		groupOutputDir := filepath.Join(outputDir, service.Package, groupName)
		if err := os.MkdirAll(groupOutputDir, 0755); err != nil {
			errorsCount++
			fmt.Printf("❌ Failed to create directory %s: %v\n", groupOutputDir, err)
			continue
		}

		// Generate handler file for this group
		filename := filepath.Join(groupOutputDir, fmt.Sprintf("%s.go", groupName))

		// Check if file already exists, skip generation if so
		if _, err := os.Stat(filename); err == nil {
			fmt.Printf("⚠️  Skipping generation for existing file: %s\n", filename)
			continue
		}

		file, err := os.Create(filename)
		if err != nil {
			errorsCount++
			fmt.Printf("❌ Failed to create file %s: %v\n", filename, err)
			continue
		}

		err = tmpl.Execute(file, templateData)
		file.Close()
		if err != nil {
			errorsCount++
			fmt.Printf("❌ Failed to execute template for %s: %v\n", filename, err)
			continue
		}

		fmt.Printf("✅ Generated handler: %s\n", filename)
		generatedFiles++
	}

	if errorsCount > 0 {
		return fmt.Errorf("generation completed with %d errors", errorsCount)
	}

	if generatedFiles == 0 {
		return fmt.Errorf("no handlers generated")
	}

	return nil
}

func generateRoutes(serviceName string, service Service, globalConfig GlobalConfig) error {
	// Read the main routes.go file
	routesFilePath := "internal/routes/v1/routes.go"
	routesContent, err := ioutil.ReadFile(routesFilePath)
	if err != nil {
		return fmt.Errorf("failed to read routes file: %w", err)
	}

	routesContentStr := string(routesContent)

	// Check if routes are already registered
	if strings.Contains(routesContentStr, fmt.Sprintf("Register%sRoutes", service.Name)) {
		fmt.Printf("⚠️  Routes for %s already registered in main routes file\n", service.Name)
		return nil
	}

	// Prepare template data for all groups
	var allRoutes []string

	// Loop over each module group in service.Endpoints
	for groupName, subEndpoints := range service.Endpoints {
		// Prepare template data for this group
		templateData := TemplateData{
			ServiceName:  service.Name,
			ServiceLower: strings.ToLower(service.Name),
			ServiceUpper: strings.ToUpper(service.Name),
			Category:     service.Category,
			Package:      groupName, // package name is group name
			Description:  service.Description,
			BaseURL:      service.BaseURL,
			Timeout:      getOrDefault(service.Timeout, 30),
			RetryCount:   getOrDefault(service.RetryCount, 3),
			Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
			ModuleName:   globalConfig.ModuleName,
			HasValidator: true,
			HasLogger:    globalConfig.EnableLogging,
			HasMetrics:   globalConfig.EnableMetrics,
			HasSwagger:   globalConfig.EnableSwagger,
			Dependencies: service.Dependencies,
			Middleware:   service.Middleware,
			GlobalConfig: globalConfig,
		}

		// Process endpoints for this group
		for subEndpointName, endpoint := range subEndpoints {
			fullEndpointName := groupName
			if subEndpointName != "" {
				fullEndpointName = fullEndpointName + strings.Title(subEndpointName)
			}
			endpointData := processEndpoint(fullEndpointName, endpoint, groupName)
			templateData.Endpoints = append(templateData.Endpoints, endpointData)
		}

		// Generate routes code for this group
		var routesCode strings.Builder

		routesCode.WriteString(fmt.Sprintf("\n\t// %s routes\n", strings.Title(groupName)))
		routesCode.WriteString(fmt.Sprintf("\t%sHandler := %s.New%sHandler(%s.%sHandlerConfig{\n", groupName, groupName, service.Name, groupName, service.Name))
		routesCode.WriteString("\t\tBpjsConfig: cfg.Bpjs,\n")
		routesCode.WriteString("\t\tLogger:     *logger.Default(),\n")
		routesCode.WriteString("\t\tValidator:  nil,\n")
		routesCode.WriteString("\t})\n")
		routesCode.WriteString(fmt.Sprintf("\t%sGroup := v1.Group(\"/%s\")\n", groupName, groupName))

		for _, endpoint := range templateData.Endpoints {
			if endpoint.HasGet {
				routesCode.WriteString(fmt.Sprintf("\t%sGroup.GET(\"%s\", %sHandler.Get%s)\n", groupName, endpoint.GetPath, groupName, endpoint.Name))
			}
			if endpoint.HasPost {
				routesCode.WriteString(fmt.Sprintf("\t%sGroup.POST(\"%s\", %sHandler.Create%s)\n", groupName, endpoint.PostPath, groupName, endpoint.Name))
			}
			if endpoint.HasPut {
				routesCode.WriteString(fmt.Sprintf("\t%sGroup.PUT(\"%s\", %sHandler.Update%s)\n", groupName, endpoint.PutPath, groupName, endpoint.Name))
			}
			if endpoint.HasDelete {
				routesCode.WriteString(fmt.Sprintf("\t%sGroup.DELETE(\"%s\", %sHandler.Delete%s)\n", groupName, endpoint.DeletePath, groupName, endpoint.Name))
			}
			if endpoint.HasPatch {
				routesCode.WriteString(fmt.Sprintf("\t%sGroup.PATCH(\"%s\", %sHandler.Patch%s)\n", groupName, endpoint.PatchPath, groupName, endpoint.Name))
			}
		}

		allRoutes = append(allRoutes, routesCode.String())
	}

	// Find the PUBLISHED ROUTES section and insert the routes
	publishedRoutesMarker := "// ============= PUBLISHED ROUTES ==============================================="
	if !strings.Contains(routesContentStr, publishedRoutesMarker) {
		return fmt.Errorf("PUBLISHED ROUTES marker not found in routes.go")
	}

	// Insert the routes after the marker
	insertionPoint := strings.Index(routesContentStr, publishedRoutesMarker) + len(publishedRoutesMarker)
	newRoutesContent := routesContentStr[:insertionPoint] + "\n" + strings.Join(allRoutes, "\n") + "\n" + routesContentStr[insertionPoint:]

	// Write back the modified routes file
	err = ioutil.WriteFile(routesFilePath, []byte(newRoutesContent), 0644)
	if err != nil {
		return fmt.Errorf("failed to write updated routes file: %w", err)
	}

	fmt.Printf("✅ Updated main routes file with %s routes\n", service.Name)
	return nil
}

func processEndpoint(name string, endpoint Endpoint, endpointGroup string) EndpointData {
	data := EndpointData{
		Name:          strings.Title(name),
		NameLower:     strings.ToLower(name),
		NameUpper:     strings.ToUpper(name),
		NameCamel:     toCamelCase(name),
		Methods:       endpoint.Methods,
		GetPath:       endpoint.GetPath,
		PostPath:      endpoint.PostPath,
		PutPath:       endpoint.PutPath,
		DeletePath:    endpoint.DeletePath,
		PatchPath:     endpoint.PatchPath,
		Model:         endpoint.Model,
		ResponseModel: endpoint.ResponseModel,
		DataModel:     strings.Replace(endpoint.ResponseModel, "Response", "Data", 1),
		Description:   endpoint.Description,
		Summary:       endpoint.Summary,
		Tags:          endpoint.Tags,
		RequireAuth:   endpoint.RequireAuth,
		RateLimit:     endpoint.RateLimit,
		CacheEnabled:  endpoint.CacheEnabled,
		CacheTTL:      getOrDefault(endpoint.CacheTTL, 300),
		CustomHeaders: endpoint.CustomHeaders,
		ModelPackage:  endpointGroup, // Set the model package based on endpoint group
	}

	// Set method flags and extract path parameters
	for _, method := range endpoint.Methods {
		switch strings.ToUpper(method) {
		case "GET":
			data.HasGet = true
			data.PathParams = extractPathParams(endpoint.GetPath)
		case "POST":
			data.HasPost = true
		case "PUT":
			data.HasPut = true
			data.PathParams = extractPathParams(endpoint.PutPath)
		case "DELETE":
			data.HasDelete = true
			data.PathParams = extractPathParams(endpoint.DeletePath)
		case "PATCH":
			data.HasPatch = true
			data.PathParams = extractPathParams(endpoint.PatchPath)
		}
	}

	return data
}

func extractPathParams(path string) []string {
	if path == "" {
		return nil
	}

	var params []string
	parts := strings.Split(path, "/")
	for _, part := range parts {
		if strings.HasPrefix(part, ":") {
			params = append(params, strings.TrimPrefix(part, ":"))
		}
	}

	return params
}

func toCamelCase(str string) string {
	words := strings.FieldsFunc(str, func(c rune) bool {
		return c == '_' || c == '-' || c == ' '
	})

	if len(words) == 0 {
		return str
	}

	result := strings.ToLower(words[0])
	for _, word := range words[1:] {
		result += strings.Title(strings.ToLower(word))
	}

	return result
}

func getOrDefault(value, defaultValue int) int {
	if value == 0 {
		return defaultValue
	}
	return value
}
