package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"
	"time"

	"gopkg.in/yaml.v2"
)

// Enhanced structures untuk validasi
type HandlerValidation struct {
	ExistingFunctions map[string]bool
	NewFunctions      []string
	UpdatedFiles      []string
	CreatedFiles      []string
}

type DirectoryInfo struct {
	Path      string
	IsFile    bool
	Functions []FunctionInfo
	Children  map[string]*DirectoryInfo
}

type FunctionInfo struct {
	Name     string
	Methods  []string
	Endpoint string
	Config   EndpointConfig
}

type EndpointConfig struct {
	Methods       []string `yaml:"methods"`
	GetRoutes     string   `yaml:"get_routes"`
	PostRoutes    string   `yaml:"post_routes"`
	PutRoutes     string   `yaml:"put_routes"`
	DeleteRoutes  string   `yaml:"delete_routes"`
	GetPath       string   `yaml:"get_path"`
	PostPath      string   `yaml:"post_path"`
	PutPath       string   `yaml:"put_path"`
	DeletePath    string   `yaml:"delete_path"`
	Model         string   `yaml:"model"`
	ResponseModel string   `yaml:"response_model"`
	RequestModel  string   `yaml:"request_model"`
	Description   string   `yaml:"description"`
	Summary       string   `yaml:"summary"`
	Tags          []string `yaml:"tags"`
	RequireAuth   bool     `yaml:"require_auth"`
	CacheEnabled  bool     `yaml:"cache_enabled"`
	CacheTTL      int      `yaml:"cache_ttl"`
}

type GlobalConfig struct {
	ModuleName    string `yaml:"module_name"`
	OutputDir     string `yaml:"output_dir"`
	EnableSwagger bool   `yaml:"enable_swagger"`
	EnableLogging bool   `yaml:"enable_logging"`
}

type ServiceConfig struct {
	Global   GlobalConfig       `yaml:"global,omitempty"`
	Services map[string]Service `yaml:"services"`
}

type Service struct {
	Name         string                   `yaml:"name"`
	Category     string                   `yaml:"category"`
	Package      string                   `yaml:"package"`
	Description  string                   `yaml:"description"`
	BaseURL      string                   `yaml:"base_url"`
	Timeout      int                      `yaml:"timeout"`
	RetryCount   int                      `yaml:"retry_count"`
	Endpoints    map[string]EndpointGroup `yaml:"endpoints"`
	Dependencies string                   `yaml:"dependencies,omitempty"`
	Middleware   string                   `yaml:"middleware,omitempty"`
}

type EndpointGroup struct {
	Description   string                    `yaml:"description"`
	HandlerFolder string                    `yaml:"handler_folder"`
	HandlerFile   string                    `yaml:"handler_file"`
	HandlerName   string                    `yaml:"handler_name"`
	Functions     map[string]FunctionConfig `yaml:"functions"`
}

type FunctionConfig struct {
	Methods    []string `yaml:"methods"`
	Path       string   `yaml:"path"`
	Model      string   `yaml:"model"`
	RoutesLink string   `yaml:"routes_link"`
	// Routes untuk endpoint generation
	GetRoutes    string `yaml:"get_routes"`
	PostRoutes   string `yaml:"post_routes"`
	PutRoutes    string `yaml:"put_routes"`
	DeleteRoutes string `yaml:"delete_routes"`
	// ✅ Path untuk swagger documentation
	GetPath       string   `yaml:"get_path"`
	PostPath      string   `yaml:"post_path"`
	PutPath       string   `yaml:"put_path"`
	DeletePath    string   `yaml:"delete_path"`
	ResponseModel string   `yaml:"response_model"`
	RequestModel  string   `yaml:"request_model"`
	Description   string   `yaml:"description"`
	Summary       string   `yaml:"summary"`
	Tags          []string `yaml:"tags"`
	RequireAuth   bool     `yaml:"require_auth"`
	CacheEnabled  bool     `yaml:"cache_enabled"`
	CacheTTL      int      `yaml:"cache_ttl"`
}

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
	HasSwagger                bool
	GlobalConfig              GlobalConfig
	ShouldGenerateStruct      bool
	ShouldGenerateConstructor bool
	FunctionalArea            string // e.g. "rujukan", "search"
	HandlerName               string
}

type EndpointData struct {
	Name          string
	NameLower     string
	NameUpper     string
	NameCamel     string
	Methods       []string
	GetRoutes     string
	PostRoutes    string
	PutRoutes     string
	DeleteRoutes  string
	GetPath       string
	PostPath      string
	PutPath       string
	DeletePath    string
	Model         string
	ResponseModel string
	RequestModel  string
	DataModel     string
	Description   string
	Summary       string
	Tags          []string
	HasGet        bool
	HasPost       bool
	HasPut        bool
	HasDelete     bool
	RequireAuth   bool
	CacheEnabled  bool
	CacheTTL      int
	PathParams    []string
	ModelPackage  string
}

// Fungsi utama yang diupdate untuk menggunakan separation of concerns
func generateHandlerWithValidation(serviceName string, svc Service, gc GlobalConfig) error {
	// Step 1: Generate base handler file (struct + constructor) - SEKALI SAJA
	// err := generateBaseHandlerFile(serviceName, svc, gc)
	// if err != nil {
	// 	return fmt.Errorf("generate base handler: %w", err)
	// }

	// Step 2: Generate methods files per endpoint group
	baseDir := gc.OutputDir
	for groupName, grp := range svc.Endpoints {
		folder := filepath.Join(baseDir, grp.HandlerFolder)
		if err := os.MkdirAll(folder, 0755); err != nil {
			return fmt.Errorf("mkdir %s: %w", folder, err)
		}

		// Generate methods file dengan naming yang jelas
		methodsFileName := fmt.Sprintf("%s.go", strings.ToLower(groupName))
		methodsFilePath := filepath.Join(folder, methodsFileName)

		// Check if methods file exists
		fileExists := false
		if _, err := os.Stat(methodsFilePath); err == nil {
			fileExists = true
		}

		if !fileExists {
			// Create new methods file
			err := createMethodsFileFromConfig(methodsFilePath, svc, grp, gc, groupName)
			if err != nil {
				return fmt.Errorf("create methods file %s: %w", methodsFilePath, err)
			}
			fmt.Printf("✅ Created methods file: %s\n", methodsFilePath)
		} else {
			// Update existing methods file with new functions only
			err := updateExistingMethodsFile(methodsFilePath, svc, grp, gc, groupName)
			if err != nil {
				return fmt.Errorf("update methods file %s: %w", methodsFilePath, err)
			}
			fmt.Printf("✅ Updated methods file: %s\n", methodsFilePath)
		}
	}
	// ✅ Step 2: Generate routes
	err := generateRoutes(serviceName, svc, gc)
	if err != nil {
		return fmt.Errorf("generate routes: %w", err)
	}

	return nil
}

// Create new methods file
func createMethodsFileFromConfig(filePath string, svc Service, grp EndpointGroup, gc GlobalConfig, functionalArea string) error {
	// Collect all functions for this group
	var allEndpoints []EndpointData
	for fname, fcfg := range grp.Functions {
		td := processFunctionData(svc, grp, fname, fcfg, gc)
		allEndpoints = append(allEndpoints, td.Endpoints...)
	}

	templateData := TemplateData{
		ServiceName:               svc.Name,
		ServiceLower:              strings.ToLower(svc.Name),
		ServiceUpper:              strings.ToUpper(svc.Name),
		Category:                  svc.Category,
		Package:                   grp.HandlerFolder,
		Description:               svc.Description,
		BaseURL:                   svc.BaseURL,
		Timeout:                   svc.Timeout,
		RetryCount:                svc.RetryCount,
		Endpoints:                 allEndpoints,
		Timestamp:                 time.Now().Format("2006-01-02 15:04:05"),
		ModuleName:                gc.ModuleName,
		HasValidator:              true,
		HasLogger:                 gc.EnableLogging,
		HasSwagger:                gc.EnableSwagger,
		GlobalConfig:              gc,
		ShouldGenerateStruct:      false, // NEVER generate struct in methods file
		ShouldGenerateConstructor: false, // NEVER generate constructor in methods file
		FunctionalArea:            functionalArea,
		HandlerName:               grp.HandlerName, // PERBAIKAN: Pastikan HandlerName diset
	}

	return createMethodsFile(filePath, templateData)
}

// Create methods file using methods-only template
func createMethodsFile(filePath string, templateData TemplateData) error {
	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	tmpl := template.New("methods").Funcs(template.FuncMap{
		"title": strings.Title,
		"index": func(slice []string, i int) string {
			if i >= 0 && i < len(slice) {
				return slice[i]
			}
			return ""
		},
	})

	tmpl, err := tmpl.Parse(handlerTemplate)
	if err != nil {
		return err
	}

	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	return tmpl.Execute(file, templateData)
}

// Update existing methods file (sama seperti sebelumnya tapi tanpa struct)
func updateExistingMethodsFile(filePath string, svc Service, grp EndpointGroup, gc GlobalConfig, functionalArea string) error {
	existingContent, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}

	content := string(existingContent)
	// Check for existing functions and collect new ones
	var newEndpoints []EndpointData

	for fname, fcfg := range grp.Functions {
		functionExists := false
		for _, method := range fcfg.Methods {
			funcName := generateFunctionName(fname, method)
			// PERBAIKAN: Gunakan grp.HandlerName bukan svc.Name
			sig := fmt.Sprintf("func (h *%sHandler) %s", grp.HandlerName, funcName)
			if strings.Contains(content, sig) {
				fmt.Printf("⚠️ Skip existing: %s (%s)\n", fname, funcName)
				functionExists = true
				break
			}
		}

		if functionExists {
			continue
		}

		td := processFunctionData(svc, grp, fname, fcfg, gc)
		newEndpoints = append(newEndpoints, td.Endpoints...)
		fmt.Printf("✅ Will add: %s\n", fname)
	}

	if len(newEndpoints) == 0 {
		fmt.Printf("⏭️ No new functions to add\n")
		return nil
	}

	// Generate new functions using methods-only template
	templateData := TemplateData{
		ServiceName:               svc.Name,
		ServiceLower:              strings.ToLower(svc.Name),
		ServiceUpper:              strings.ToUpper(svc.Name),
		Category:                  svc.Category,
		Package:                   grp.HandlerFolder,
		Description:               svc.Description,
		BaseURL:                   svc.BaseURL,
		Timeout:                   svc.Timeout,
		RetryCount:                svc.RetryCount,
		Endpoints:                 newEndpoints,
		Timestamp:                 time.Now().Format("2006-01-02 15:04:05"),
		ModuleName:                gc.ModuleName,
		HasValidator:              true,
		HasLogger:                 gc.EnableLogging,
		HasSwagger:                gc.EnableSwagger,
		GlobalConfig:              gc,
		ShouldGenerateStruct:      false, // NEVER
		ShouldGenerateConstructor: false, // NEVER
		FunctionalArea:            functionalArea,
		HandlerName:               grp.HandlerName, // PERBAIKAN: Pastikan HandlerName diset
	}

	newFunctions, err := generateNewMethodsOnly(templateData)
	if err != nil {
		return err
	}

	// Merge content
	mergedContent := mergeGoFileContent(content, newFunctions)
	return ioutil.WriteFile(filePath, []byte(mergedContent), 0644)
}

// Generate new methods using methods-only template
func generateNewMethodsOnly(templateData TemplateData) (string, error) {
	tmpl := template.New("newMethods")
	tmpl, err := tmpl.Parse(handlerTemplate)
	if err != nil {
		return "", err
	}

	var result strings.Builder
	err = tmpl.Execute(&result, templateData)
	if err != nil {
		return "", err
	}

	return result.String(), nil
}

// Helper toCamelCase
func toCamelCase(s string) string {
	parts := strings.FieldsFunc(s, func(r rune) bool {
		return r == '_' || r == '-' || r == ' '
	})
	for i, p := range parts {
		parts[i] = strings.Title(strings.ToLower(p))
	}
	return strings.Join(parts, "")
}

// Parse struktur direktori dari endpoints YAML
func parseDirectoryStructure(endpoints map[string]interface{}, serviceName string) *DirectoryInfo {
	root := &DirectoryInfo{
		Path:     "",
		Children: make(map[string]*DirectoryInfo),
	}

	for key, value := range endpoints {
		parseNestedEndpoints(root, key, value)
	}

	return root
}

func parseNestedEndpoints(parent *DirectoryInfo, name string, value interface{}) {
	switch v := value.(type) {
	case map[string]interface{}:
		// Check if this contains direct endpoint config
		if isDirectEndpoint(v) {
			// This is a direct endpoint - create as file
			parent.Children[name] = &DirectoryInfo{
				Path:      name,
				IsFile:    true,
				Functions: []FunctionInfo{parseEndpointToFunction(name, v)},
				Children:  make(map[string]*DirectoryInfo),
			}
		} else {
			// This is nested structure - create as directory or file
			child := &DirectoryInfo{
				Path:      name,
				IsFile:    false,
				Functions: make([]FunctionInfo, 0),
				Children:  make(map[string]*DirectoryInfo),
			}

			// Check if any direct children are endpoints
			hasDirectEndpoints := false
			for childName, childValue := range v {
				if childMap, ok := childValue.(map[string]interface{}); ok && isDirectEndpoint(childMap) {
					hasDirectEndpoints = true
					child.Functions = append(child.Functions, parseEndpointToFunction(childName, childMap))
				}
			}

			if hasDirectEndpoints {
				child.IsFile = true
			}

			parent.Children[name] = child

			// Recursively parse nested children
			for childName, childValue := range v {
				if childMap, ok := childValue.(map[string]interface{}); ok && !isDirectEndpoint(childMap) {
					parseNestedEndpoints(child, childName, childValue)
				}
			}
		}
	}
}

func isDirectEndpoint(m map[string]interface{}) bool {
	_, hasMethods := m["methods"]
	_, hasGetPath := m["get_path"]
	_, hasPostPath := m["post_path"]
	return hasMethods || hasGetPath || hasPostPath
}

func parseEndpointToFunction(name string, config map[string]interface{}) FunctionInfo {
	function := FunctionInfo{
		Name:     name,
		Endpoint: name,
		Methods:  make([]string, 0),
	}

	if methods, ok := config["methods"].([]interface{}); ok {
		for _, method := range methods {
			if methodStr, ok := method.(string); ok {
				function.Methods = append(function.Methods, strings.ToUpper(strings.TrimSpace(methodStr)))
			}
		}
	} else if methodStr, ok := config["methods"].(string); ok {
		// Handle case where methods is a string like "GET,POST"
		methods := strings.Split(methodStr, ",")
		for _, method := range methods {
			function.Methods = append(function.Methods, strings.ToUpper(strings.TrimSpace(method)))
		}
	}

	return function
}

// Process directory structure dan generate files
func processDirectoryStructure(baseDir string, dirInfo *DirectoryInfo, service Service, globalConfig GlobalConfig, validation *HandlerValidation) error {
	for name, child := range dirInfo.Children {
		currentPath := filepath.Join(baseDir, child.Path)

		if child.IsFile {
			// Process as file
			err := processHandlerFile(currentPath, name, child, service, globalConfig, validation)
			if err != nil {
				return fmt.Errorf("failed to process file %s: %w", name, err)
			}
		} else {
			// Create directory dan process children
			if err := os.MkdirAll(currentPath, 0755); err != nil {
				return fmt.Errorf("failed to create directory %s: %w", currentPath, err)
			}

			err := processDirectoryStructure(currentPath, child, service, globalConfig, validation)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
func processFunctionData(svc Service, grp EndpointGroup, fname string, fcfg FunctionConfig, gc GlobalConfig) TemplateData {
	ed := EndpointData{
		Name:          toCamelCase(fname),
		NameLower:     strings.ToLower(fname),
		NameUpper:     strings.ToUpper(fname),
		NameCamel:     toCamelCase(fname),
		Methods:       fcfg.Methods,
		GetRoutes:     fcfg.GetRoutes,
		PostRoutes:    fcfg.PostRoutes,
		PutRoutes:     fcfg.PutRoutes,
		DeleteRoutes:  fcfg.DeleteRoutes,
		GetPath:       fcfg.Path,
		PostPath:      fcfg.Path,
		PutPath:       fcfg.Path,
		DeletePath:    fcfg.Path,
		Model:         fcfg.Model,
		ResponseModel: fcfg.ResponseModel,
		RequestModel:  fcfg.RequestModel,
		DataModel:     strings.Replace(fcfg.ResponseModel, "Response", "Data", 1),
		Description:   fcfg.Description,
		Summary:       fcfg.Summary,
		Tags:          fcfg.Tags,
		RequireAuth:   fcfg.RequireAuth,
		CacheEnabled:  fcfg.CacheEnabled,
		CacheTTL:      fcfg.CacheTTL,
		PathParams:    extractPathParams(fcfg.Path),
		ModelPackage:  grp.HandlerFolder,
	}
	// set flags
	for _, m := range fcfg.Methods {
		switch strings.ToUpper(m) {
		case "GET":
			ed.HasGet = true
		case "POST":
			ed.HasPost = true
		case "PUT":
			ed.HasPut = true
		case "DELETE":
			ed.HasDelete = true
		}
	}
	return TemplateData{
		ServiceName:  svc.Name,
		ServiceLower: strings.ToLower(svc.Name),
		ServiceUpper: strings.ToUpper(svc.Name),
		Category:     svc.Category,
		Package:      grp.HandlerFolder,
		Description:  svc.Description,
		BaseURL:      svc.BaseURL,
		Timeout:      svc.Timeout,
		RetryCount:   svc.RetryCount,
		Endpoints:    []EndpointData{ed},
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
		ModuleName:   gc.ModuleName,
		HasValidator: true,
		HasLogger:    gc.EnableLogging,
		HasSwagger:   gc.EnableSwagger,
		GlobalConfig: gc,
		HandlerName:  grp.HandlerName,
	}
}

// extractPathParams (jika belum ada)
func extractPathParams(path string) []string {
	var ps []string
	for _, part := range strings.Split(path, "/") {
		if strings.HasPrefix(part, ":") {
			ps = append(ps, strings.TrimPrefix(part, ":"))
		}
	}
	return ps
}

// Process individual handler file - PERBAIKAN: Hapus referensi grp.HandlerName
func processHandlerFile(basePath, fileName string, dirInfo *DirectoryInfo, service Service, globalConfig GlobalConfig, validation *HandlerValidation) error {
	filePath := filepath.Join(filepath.Dir(basePath), fmt.Sprintf("%s.go", fileName))
	fmt.Printf("📄 Processing file: %s\n", filePath)

	// Check if file exists
	fileExists := false
	if _, err := os.Stat(filePath); err == nil {
		fileExists = true
		fmt.Printf(" 📋 File exists, checking functions...\n")
	}

	var existingFunctions map[string]bool
	if fileExists {
		// Parse existing functions - PERBAIKAN: Hanya gunakan 1 parameter
		functions, err := extractExistingFunctions(filePath)
		if err != nil {
			fmt.Printf(" ⚠️ Warning: Could not parse existing file: %v\n", err)
			existingFunctions = make(map[string]bool)
		} else {
			existingFunctions = functions
			fmt.Printf(" ✓ Found %d existing functions\n", len(functions))
		}
	} else {
		existingFunctions = make(map[string]bool)
	}

	// Determine which functions to generate
	functionsToGenerate := make([]FunctionInfo, 0)
	for _, fnInfo := range dirInfo.Functions {
		for _, method := range fnInfo.Methods {
			functionName := generateFunctionName(fnInfo.Name, method)
			if !existingFunctions[functionName] {
				functionsToGenerate = append(functionsToGenerate, FunctionInfo{
					Name:     fnInfo.Name,
					Methods:  []string{method},
					Endpoint: fnInfo.Endpoint,
				})
				validation.NewFunctions = append(validation.NewFunctions, functionName)
				fmt.Printf(" ✅ Will generate: %s\n", functionName)
			} else {
				fmt.Printf(" ⏭️ Already exists: %s\n", functionName)
			}
		}
	}

	// Generate file if needed
	if len(functionsToGenerate) > 0 {
		templateData := prepareTemplateData(fileName, service, globalConfig, functionsToGenerate)

		if fileExists {
			// Merge with existing file
			err := mergeWithExistingFile(filePath, templateData)
			if err != nil {
				return fmt.Errorf("failed to merge file %s: %w", filePath, err)
			}
			validation.UpdatedFiles = append(validation.UpdatedFiles, filePath)
			fmt.Printf(" 📝 Updated existing file\n")
		} else {
			// Create new file
			err := createNewHandlerFile(filePath, templateData)
			if err != nil {
				return fmt.Errorf("failed to create file %s: %w", filePath, err)
			}
			validation.CreatedFiles = append(validation.CreatedFiles, filePath)
			fmt.Printf(" 📁 Created new file\n")
		}
	} else if !fileExists {
		fmt.Printf(" ⏭️ No functions to generate and file doesn't exist\n")
	} else {
		fmt.Printf(" ⏭️ All functions already exist\n")
	}

	return nil
}

// Extract existing functions from Go file
func extractExistingFunctions(filePath string) (map[string]bool, error) {
	fileSet := token.NewFileSet()
	node, err := parser.ParseFile(fileSet, filePath, nil, parser.ParseComments)
	if err != nil {
		return nil, err
	}

	functions := make(map[string]bool)
	ast.Inspect(node, func(n ast.Node) bool {
		if fn, ok := n.(*ast.FuncDecl); ok {
			if fn.Name != nil {
				functions[fn.Name.Name] = true
			}
		}
		return true
	})
	return functions, nil
}

// Generate function name berdasarkan endpoint dan method
func generateFunctionName(endpointName, method string) string {
	switch strings.ToUpper(method) {
	case "GET":
		return fmt.Sprintf("Get%s", strings.Title(endpointName))
	case "POST":
		return fmt.Sprintf("Create%s", strings.Title(endpointName))
	case "PUT":
		return fmt.Sprintf("Update%s", strings.Title(endpointName))
	case "DELETE":
		return fmt.Sprintf("Delete%s", strings.Title(endpointName))
	case "PATCH":
		return fmt.Sprintf("Patch%s", strings.Title(endpointName))
	default:
		return fmt.Sprintf("%s%s", strings.Title(method), strings.Title(endpointName))
	}
}

// Prepare template data
func prepareTemplateData(packageName string, service Service, globalConfig GlobalConfig, functions []FunctionInfo) TemplateData {
	endpoints := make([]EndpointData, 0)

	for _, fnInfo := range functions {
		endpoint := EndpointData{
			Name:          strings.Title(fnInfo.Name),
			NameLower:     strings.ToLower(fnInfo.Name),
			NameUpper:     strings.ToUpper(fnInfo.Name),
			NameCamel:     toCamelCase(fnInfo.Name),
			ModelPackage:  packageName,
			Model:         fmt.Sprintf("%sRequest", strings.Title(fnInfo.Name)),
			ResponseModel: fmt.Sprintf("%sResponse", strings.Title(fnInfo.Name)),
			RequestModel:  fmt.Sprintf("%sRequest", strings.Title(fnInfo.Name)),
			DataModel:     fmt.Sprintf("%sData", strings.Title(fnInfo.Name)),
			Description:   fmt.Sprintf("Handle %s operations", fnInfo.Name),
			Tags:          []string{strings.Title(packageName)},
		}

		// Set paths dan methods
		for _, method := range fnInfo.Methods {
			switch strings.ToUpper(method) {
			case "GET":
				endpoint.HasGet = true
				endpoint.GetPath = fmt.Sprintf("/%s/%s", packageName, fnInfo.Name)
			case "POST":
				endpoint.HasPost = true
				endpoint.PostPath = fmt.Sprintf("/%s", packageName)
			case "PUT":
				endpoint.HasPut = true
				endpoint.PutPath = fmt.Sprintf("/%s/%s", packageName, fnInfo.Name)
			case "DELETE":
				endpoint.HasDelete = true
				endpoint.DeletePath = fmt.Sprintf("/%s/%s", packageName, fnInfo.Name)
			}
		}

		endpoints = append(endpoints, endpoint)
	}

	return TemplateData{
		ServiceName:  service.Name,
		ServiceLower: strings.ToLower(service.Name),
		ServiceUpper: strings.ToUpper(service.Name),
		Category:     service.Category,
		Package:      packageName,
		Description:  service.Description,
		BaseURL:      service.BaseURL,
		Timeout:      getOrDefault(service.Timeout, 30),
		RetryCount:   getOrDefault(service.RetryCount, 3),
		Endpoints:    endpoints,
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
		ModuleName:   globalConfig.ModuleName,
		HasValidator: true,
		HasLogger:    globalConfig.EnableLogging,
		HasSwagger:   globalConfig.EnableSwagger,
		GlobalConfig: globalConfig,
		HandlerName:  strings.Title(packageName),
	}
}

// Check if handler struct already exists in directory
func shouldGenerateHandlerStruct(baseDir, handlerName string) bool {
	structSignature := fmt.Sprintf("type %sHandler struct", handlerName)

	files, err := ioutil.ReadDir(baseDir)
	if err != nil {
		return true // If directory doesn't exist, generate struct
	}

	for _, file := range files {
		if file.IsDir() || !strings.HasSuffix(file.Name(), ".go") {
			continue
		}

		filePath := filepath.Join(baseDir, file.Name())
		content, err := ioutil.ReadFile(filePath)
		if err != nil {
			continue
		}

		if strings.Contains(string(content), structSignature) {
			return false // Struct already exists
		}
	}

	return true // Struct not found, should generate
}

// Check if constructor already exists
func shouldGenerateConstructor(baseDir, handlerName string) bool {
	constructorSignature := fmt.Sprintf("func New%sHandler", handlerName)

	files, err := ioutil.ReadDir(baseDir)
	if err != nil {
		return true
	}

	for _, file := range files {
		if file.IsDir() || !strings.HasSuffix(file.Name(), ".go") {
			continue
		}

		filePath := filepath.Join(baseDir, file.Name())
		content, err := ioutil.ReadFile(filePath)
		if err != nil {
			continue
		}

		if strings.Contains(string(content), constructorSignature) {
			return false
		}
	}

	return true
}

// Generate base handler file (struct + constructor only)
func generateBaseHandlerFile(serviceName string, svc Service, gc GlobalConfig) error {
	baseDir := gc.OutputDir
	if err := os.MkdirAll(baseDir, 0755); err != nil {
		return fmt.Errorf("mkdir %s: %w", baseDir, err)
	}

	baseFileName := fmt.Sprintf("%s_base.go", strings.ToLower(serviceName))
	baseFilePath := filepath.Join(baseDir, baseFileName)

	// Skip if base file already exists
	if _, err := os.Stat(baseFilePath); err == nil {
		fmt.Printf("⏭️ Base handler already exists: %s\n", baseFilePath)
		return nil
	}

	templateData := TemplateData{
		ServiceName:  svc.Name,
		ServiceLower: strings.ToLower(svc.Name),
		ServiceUpper: strings.ToUpper(svc.Name),
		Category:     svc.Category,
		Package:      "handlers",
		Description:  svc.Description,
		BaseURL:      svc.BaseURL,
		Timeout:      svc.Timeout,
		RetryCount:   svc.RetryCount,
		Endpoints:    []EndpointData{}, // Empty - base only
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
		ModuleName:   gc.ModuleName,
		HasValidator: true,
		HasLogger:    gc.EnableLogging,
		HasSwagger:   gc.EnableSwagger,
		GlobalConfig: gc,
	}

	return createBaseHandlerFile(baseFilePath, templateData)
}

// Template untuk base handler (struct + constructor only)
const baseHandlerTemplate = `// Package handlers handles {{.HandlerName}} BPJS services - Base Handler
// Generated on: {{.Timestamp}}
package handlers

import (
	"{{.ModuleName}}/internal/config"
	"{{.ModuleName}}/internal/services/bpjs"
	"{{.ModuleName}}/pkg/logger"

	"github.com/go-playground/validator/v10"
)

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

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

// New{{.HandlerName}}Handler creates a new {{.HandlerName}}Handler
func New{{.HandlerName}}Handler(cfg {{.HandlerName}}HandlerConfig) *{{.HandlerName}}Handler {
	return &{{.HandlerName}}Handler{
		service:   services.NewService(cfg.BpjsConfig),
		validator: cfg.Validator,
		logger:    cfg.Logger,
		config:    cfg.BpjsConfig,
	}
}
`

// Function to create base handler file
func createBaseHandlerFile(filePath string, templateData TemplateData) error {
	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	tmpl := template.New("baseHandler").Funcs(template.FuncMap{
		"title": strings.Title,
	})

	tmpl, err := tmpl.Parse(baseHandlerTemplate)
	if err != nil {
		return err
	}

	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	return tmpl.Execute(file, templateData)
}

// Template untuk handler file lengkap (untuk file baru)
const handlerTemplate = `// Package {{.Package}} handles {{.HandlerName}} BPJS services
// Generated on: {{.Timestamp}}
package handlers

import (
	"context"
	"encoding/json"
	"net/http"
    "strings"
	"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"
)
// {{.HandlerName}}Handler handles {{.HandlerName}} BPJS services
type {{.HandlerName}}Handler struct {
	service   services.VClaimService
	validator *validator.Validate
	logger    logger.Logger
	config    config.BpjsConfig
}
// {{.HandlerName}}HandlerConfig contains configuration for {{.HandlerName}}Handler
type {{.HandlerName}}HandlerConfig struct {
	BpjsConfig config.BpjsConfig
	Logger     logger.Logger
	Validator  *validator.Validate
}
// New{{.HandlerName}}Handler creates a new {{.HandlerName}}Handler
func New{{.HandlerName}}Handler(cfg {{.HandlerName}}HandlerConfig) *{{.HandlerName}}Handler {
	return &{{.HandlerName}}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         {{index .Tags 0}}
// @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       {{.GetRoutes}} [get]
func (h *{{$.HandlerName}}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}}
    resp, err := h.service.GetRawResponse(ctx, endpoint)
    {{else}}
    resp, 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 the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID
    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasPost}}
// Create{{.Name}} godoc
// @Summary Create new {{.Name}}
// @Description Create new {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @Accept json
// @Produce json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param X-Request-ID header string false "Request ID for tracking"
// @Param request body {{.ModelPackage}}.{{.RequestModel}} true "{{.Name}} data"
// @Success 201 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully created {{.Name}}"
// @Failure 400 {object} models.ErrorResponseBpjs "Bad request"
// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized"
// @Failure 409 {object} models.ErrorResponseBpjs "Conflict"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PostRoutes}} [post]
func (h *{{$.HandlerName}}Handler) Create{{.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 Create{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PostPath}}",
    })
    {{end}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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
    }

    {{if .PathParams}}
    // 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}}

    endpoint := "{{.PostPath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    {{end}}

    // Call service method
    var response {{.ModelPackage}}.{{.ResponseModel}}
    {{if .PathParams}}
    resp, err := h.service.PostRawResponse(ctx, endpoint, req)
    {{else}}
    resp, err := h.service.PostRawResponse(ctx, "{{.PostPath}}", req)
    {{end}}
    if err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{
            "error":      err.Error(),
            "request_id": requestID,
        })
        {{end}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} already exists or conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusCreated, response)
}
{{end}}


{{if .HasPut}}
// Update{{.Name}} godoc
// @Summary Update existing {{.Name}}
// @Description Update existing {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @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}}.{{.RequestModel}} true "{{.Name}} update data"
// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully updated {{.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 409 {object} models.ErrorResponseBpjs "Conflict - update conflict occurred"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PutRoutes}} [put]
func (h *{{$.HandlerName}}Handler) Update{{.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 Update{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PutPath}}",
        {{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}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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}}
    resp, err := h.service.PutRawResponse(ctx, endpoint, req)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "Update conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasDelete}}
// Delete{{.Name}} godoc
// @Summary Delete existing {{.Name}}
// @Description Delete existing {{.Name}} from BPJS system
// @Tags {{index .Tags 0}}
// @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 {{.DeleteRoutes}} [delete]
func (h *{{$.HandlerName}}Handler) Delete{{.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 Delete{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.DeletePath}}",
        {{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 := "{{.DeletePath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    resp, err := h.service.DeleteRawResponse(ctx, endpoint)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    } else {
        // For delete operations, sometimes there's no data in response
        response.Data = nil
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

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

// Template untuk menambah function saja (untuk file yang sudah ada)
const functionsOnlyTemplate = `{{range .Endpoints}}
{{if .HasGet}}
// Get{{.Name}} godoc
// @Summary      Get {{.Name}} data
// @Description  {{.Description}}
// @Tags         {{index .Tags 0}}
// @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       {{.GetRoutes}} [get]
func (h *{{$.HandlerName}}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}}
    resp, err := h.service.GetRawResponse(ctx, endpoint)
    {{else}}
    resp, 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 the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID
    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasPost}}
// Create{{.Name}} godoc
// @Summary Create new {{.Name}}
// @Description Create new {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @Accept json
// @Produce json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param X-Request-ID header string false "Request ID for tracking"
// @Param request body {{.ModelPackage}}.{{.RequestModel}} true "{{.Name}} data"
// @Success 201 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully created {{.Name}}"
// @Failure 400 {object} models.ErrorResponseBpjs "Bad request"
// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized"
// @Failure 409 {object} models.ErrorResponseBpjs "Conflict"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PostRoutes}} [post]
func (h *{{$.HandlerName}}Handler) Create{{.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 Create{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PostPath}}",
    })
    {{end}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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
    }

    {{if .PathParams}}
    // 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}}

    endpoint := "{{.PostPath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    {{end}}

    // Call service method
    var response {{.ModelPackage}}.{{.ResponseModel}}
    {{if .PathParams}}
    resp, err := h.service.PostRawResponse(ctx, endpoint, req)
    {{else}}
    resp, err := h.service.PostRawResponse(ctx, "{{.PostPath}}", req)
    {{end}}
    if err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{
            "error":      err.Error(),
            "request_id": requestID,
        })
        {{end}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} already exists or conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusCreated, response)
}
{{end}}


{{if .HasPut}}
// Update{{.Name}} godoc
// @Summary Update existing {{.Name}}
// @Description Update existing {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @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}}.{{.RequestModel}} true "{{.Name}} update data"
// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully updated {{.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 409 {object} models.ErrorResponseBpjs "Conflict - update conflict occurred"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PutRoutes}} [put]
func (h *{{$.HandlerName}}Handler) Update{{.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 Update{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PutPath}}",
        {{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}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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}}
    resp, err := h.service.PutRawResponse(ctx, endpoint, req)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "Update conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasDelete}}
// Delete{{.Name}} godoc
// @Summary Delete existing {{.Name}}
// @Description Delete existing {{.Name}} from BPJS system
// @Tags {{index .Tags 0}}
// @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 {{.DeleteRoutes}} [delete]
func (h *{{$.HandlerName}}Handler) Delete{{.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 Delete{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.DeletePath}}",
        {{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 := "{{.DeletePath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    resp, err := h.service.DeleteRawResponse(ctx, endpoint)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    } else {
        // For delete operations, sometimes there's no data in response
        response.Data = nil
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

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

// Create new handler file
func createNewHandlerFile(filePath string, templateData TemplateData) error {
	// Create directory
	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	// Parse template
	tmpl := template.New("handler").Funcs(template.FuncMap{
		"title": strings.Title,
		"index": func(slice []string, i int) string {
			if i >= 0 && i < len(slice) {
				return slice[i]
			}
			return ""
		},
	})

	tmpl, err := tmpl.Parse(handlerTemplate)
	if err != nil {
		return err
	}

	// Create file
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	// Execute template
	return tmpl.Execute(file, templateData)
}

// Merge dengan existing file
func mergeWithExistingFile(filePath string, templateData TemplateData) error {
	// Read existing content
	existingContent, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}

	// Generate new functions
	newFunctions, err := generateNewFunctionsOnly(templateData)
	if err != nil {
		return err
	}

	// Merge content
	mergedContent := mergeGoFileContent(string(existingContent), newFunctions)

	// Write back
	return ioutil.WriteFile(filePath, []byte(mergedContent), 0644)
}

func generateNewFunctionsOnly(templateData TemplateData) (string, error) {
	funcTemplate := `
{{range .Endpoints}}
{{if .HasGet}}
// Get{{.Name}} godoc
// @Summary      Get {{.Name}} data
// @Description  {{.Description}}
// @Tags         {{index .Tags 0}}
// @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       {{.GetRoutes}} [get]
func (h *{{$.HandlerName}}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}}
    resp, err := h.service.GetRawResponse(ctx, endpoint)
    {{else}}
    resp, 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 the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID
    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasPost}}
// Create{{.Name}} godoc
// @Summary Create new {{.Name}}
// @Description Create new {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @Accept json
// @Produce json {{if .RequireAuth}}
// @Security     ApiKeyAuth {{end}}
// @Param X-Request-ID header string false "Request ID for tracking"
// @Param request body {{.ModelPackage}}.{{.RequestModel}} true "{{.Name}} data"
// @Success 201 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully created {{.Name}}"
// @Failure 400 {object} models.ErrorResponseBpjs "Bad request"
// @Failure 401 {object} models.ErrorResponseBpjs "Unauthorized"
// @Failure 409 {object} models.ErrorResponseBpjs "Conflict"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PostRoutes}} [post]
func (h *{{$.HandlerName}}Handler) Create{{.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 Create{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PostPath}}",
    })
    {{end}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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
    }

    {{if .PathParams}}
    // 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}}

    endpoint := "{{.PostPath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    {{end}}

    // Call service method
    var response {{.ModelPackage}}.{{.ResponseModel}}
    {{if .PathParams}}
    resp, err := h.service.PostRawResponse(ctx, endpoint, req)
    {{else}}
    resp, err := h.service.PostRawResponse(ctx, "{{.PostPath}}", req)
    {{end}}
    if err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to create {{.Name}}", map[string]interface{}{
            "error":      err.Error(),
            "request_id": requestID,
        })
        {{end}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} already exists or conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusCreated, response)
}
{{end}}


{{if .HasPut}}
// Update{{.Name}} godoc
// @Summary Update existing {{.Name}}
// @Description Update existing {{.Name}} in BPJS system
// @Tags {{index .Tags 0}}
// @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}}.{{.RequestModel}} true "{{.Name}} update data"
// @Success 200 {object} {{.ModelPackage}}.{{.ResponseModel}} "Successfully updated {{.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 409 {object} models.ErrorResponseBpjs "Conflict - update conflict occurred"
// @Failure 500 {object} models.ErrorResponseBpjs "Internal server error"
// @Router {{.PutRoutes}} [put]
func (h *{{$.HandlerName}}Handler) Update{{.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 Update{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.PutPath}}",
        {{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}}

    // Bind and validate request body
    var req {{.ModelPackage}}.{{.RequestModel}}
    if err := c.ShouldBindJSON(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Failed to bind 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 structure
    if err := h.validator.Struct(&req); err != nil {
        {{if $.HasLogger}}
        h.logger.Error("Request 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}}
    resp, err := h.service.PutRawResponse(ctx, endpoint, req)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        if strings.Contains(err.Error(), "409") || strings.Contains(err.Error(), "conflict") {
            c.JSON(http.StatusConflict, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "Update conflict occurred",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

    c.JSON(http.StatusOK, response)
}
{{end}}

{{if .HasDelete}}
// Delete{{.Name}} godoc
// @Summary Delete existing {{.Name}}
// @Description Delete existing {{.Name}} from BPJS system
// @Tags {{index .Tags 0}}
// @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 {{.DeleteRoutes}} [delete]
func (h *{{$.HandlerName}}Handler) Delete{{.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 Delete{{.Name}} request", map[string]interface{}{
        "request_id": requestID,
        "endpoint":   "{{.DeletePath}}",
        {{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 := "{{.DeletePath}}"
    {{range .PathParams}}
    endpoint = strings.Replace(endpoint, ":{{.}}", {{.}}, 1)
    {{end}}
    resp, err := h.service.DeleteRawResponse(ctx, endpoint)
    {{else}}
    resp, 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}}
        
        // Handle specific BPJS errors
        if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
            c.JSON(http.StatusNotFound, models.ErrorResponseBpjs{
                Status:    "error",
                Message:   "{{.Name}} not found",
                RequestID: requestID,
            })
            return
        }
        
        c.JSON(http.StatusInternalServerError, models.ErrorResponseBpjs{
            Status:    "error",
            Message:   "Internal server error",
            RequestID: requestID,
        })
        return
    }

    // Map the raw response
    response.MetaData = resp.MetaData
    if resp.Response != nil {
        response.Data = &{{.ModelPackage}}.{{.DataModel}}{}
        if respStr, ok := resp.Response.(string); ok {
            // Decrypt the response string
            consID, secretKey, _, tstamp, _ := h.config.SetHeader()
            decryptedResp, err := services.ResponseVclaim(respStr, consID+secretKey+tstamp)
            if err != nil {
                {{if $.HasLogger}}
                h.logger.Error("Failed to decrypt response", map[string]interface{}{
                    "error":      err.Error(),
                    "request_id": requestID,
                })
                {{end}}
            } else {
                json.Unmarshal([]byte(decryptedResp), response.Data)
            }
        } else if respMap, ok := resp.Response.(map[string]interface{}); ok {
            // Response is already unmarshaled JSON
            if dataMap, exists := respMap["{{.ModelPackage}}"]; exists {
                dataBytes, _ := json.Marshal(dataMap)
                json.Unmarshal(dataBytes, response.Data)
            } else {
                // Try to unmarshal the whole response
                respBytes, _ := json.Marshal(resp.Response)
                json.Unmarshal(respBytes, response.Data)
            }
        }
    } else {
        // For delete operations, sometimes there's no data in response
        response.Data = nil
    }

    // Ensure response has proper fields
    response.Status = "success"
    response.RequestID = requestID

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

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

	tmpl := template.New("functions")
	tmpl, err := tmpl.Parse(funcTemplate)
	if err != nil {
		return "", err
	}

	var result strings.Builder
	err = tmpl.Execute(&result, templateData)
	if err != nil {
		return "", err
	}

	// Remove duplicate function definitions by simple regex grouping
	// This is a simple approach to avoid duplicate functions in merged content
	funcRegex := regexp.MustCompile(`(?s)(func \(h \*\w+Handler\) Get\w+\(c \*gin.Context\) \{.*?\})`)
	matches := funcRegex.FindAllString(result.String(), -1)
	uniqueFuncs := make(map[string]bool)
	var uniqueResult strings.Builder
	for _, m := range matches {
		if !uniqueFuncs[m] {
			uniqueFuncs[m] = true
			uniqueResult.WriteString(m)
			uniqueResult.WriteString("\n\n")
		}
	}

	// If no matches found, return original result
	if uniqueResult.Len() == 0 {
		return result.String(), nil
	}

	return uniqueResult.String(), nil
}

func mergeGoFileContent(existingContent, newFunctions string) string {
	// Find last closing brace
	re := regexp.MustCompile(`}\s*$`)
	lastBraceIndex := re.FindStringIndex(existingContent)

	if lastBraceIndex == nil {
		return existingContent + "\n" + newFunctions
	}

	before := existingContent[:lastBraceIndex[0]]
	after := existingContent[lastBraceIndex[0]:]

	return before + "\n" + newFunctions + "\n" + after
}

// Main function
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]
	}

	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 with Validation...")
	fmt.Printf("📁 Config file: %s\n", configFile)
	if targetService != "" {
		fmt.Printf("🎯 Target service: %s\n", targetService)
	}

	generated := 0
	errors := 0

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

		err := generateHandlerWithValidation(serviceName, service, config.Global)
		if err != nil {
			fmt.Printf("❌ Error generating handler for %s: %v\n", serviceName, err)
			errors++
			continue
		}

		generated++
	}

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

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

// Helper functions
func loadConfig(filename string) (*ServiceConfig, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	var config ServiceConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}

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

	return &config, nil
}

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

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

// Generate routes file untuk service
func generateRoutes(serviceName string, svc Service, gc GlobalConfig) error {
	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", svc.Name)) {
		fmt.Printf("⚠️ Routes for %s already registered in main routes file\n", svc.Name)
		return nil
	}

	var imports []string
	var allRoutes []string

	// ✅ PERBAIKAN: Gunakan groupName dengan benar
	// ✅ VALIDASI: Track handler folder yang sudah diimport
	processedFolders := make(map[string]bool)
	for groupName, grp := range svc.Endpoints {
		// Import berdasarkan handler folder
		// imports = append(imports, fmt.Sprintf("\t%sHandlers \"%s/internal/handlers/%s\"",
		// 	grp.HandlerFolder, gc.ModuleName, grp.HandlerFolder))
		if !processedFolders[grp.HandlerFolder] {
			importLine := fmt.Sprintf("\t%sHandlers \"%s/internal/handlers/%s\"",
				grp.HandlerFolder, gc.ModuleName, grp.HandlerFolder)
			imports = append(imports, importLine)
			processedFolders[grp.HandlerFolder] = true
			fmt.Printf("✅ Added import: %sHandlers\n", grp.HandlerFolder)
		} else {
			fmt.Printf("⚠️ Skipped duplicate import for folder: %s\n", grp.HandlerFolder)
		}

		var routesCode strings.Builder

		// Gunakan groupName untuk comment dan identifier
		routesCode.WriteString(fmt.Sprintf("\n\t// %s (%s) routes\n", grp.Description, groupName))

		// Handler instantiation menggunakan HandlerName dari config
		routesCode.WriteString(fmt.Sprintf("\t%sHandler := %sHandlers.New%sHandler(%sHandlers.%sHandlerConfig{\n",
			strings.ToLower(grp.HandlerName),
			grp.HandlerFolder,
			grp.HandlerName,
			grp.HandlerFolder,
			grp.HandlerName))

		routesCode.WriteString("\t\tBpjsConfig: cfg.Bpjs,\n")
		routesCode.WriteString("\t\tLogger:     *logger.Default(),\n")
		routesCode.WriteString("\t\tValidator:  validator.New(),\n")
		routesCode.WriteString("\t})\n")

		// ✅ GUNAKAN groupName untuk route group path
		routesCode.WriteString(fmt.Sprintf("\t%sGroup := v1.Group(\"/%s\")\n",
			strings.ToLower(grp.HandlerName), groupName)) // ← Gunakan groupName di sini

		// Process functions
		for fname, fcfg := range grp.Functions {
			td := processFunctionData(svc, grp, fname, fcfg, gc)

			for _, endpoint := range td.Endpoints {
				handlerVar := strings.ToLower(grp.HandlerName) + "Handler"
				groupVar := strings.ToLower(grp.HandlerName) + "Group"

				// ✅ MODIFIKASI: Loop through methods dan gunakan specific routes
				for _, method := range fcfg.Methods {
					var cleanPath string

					// ✅ Pilih path berdasarkan method
					switch strings.ToUpper(method) {
					case "GET":
						cleanPath = fcfg.GetRoutes
						if cleanPath == "" {
							cleanPath = fcfg.GetPath // fallback ke get_path
						}
					case "POST":
						cleanPath = fcfg.PostRoutes
						if cleanPath == "" {
							cleanPath = fcfg.PostPath // fallback ke post_path
						}
					case "PUT":
						cleanPath = fcfg.PutRoutes
						if cleanPath == "" {
							cleanPath = fcfg.PutPath // fallback ke put_path
						}
					case "DELETE":
						cleanPath = fcfg.DeleteRoutes
						if cleanPath == "" {
							cleanPath = fcfg.DeletePath // fallback ke delete_path
						}
					default:
						fmt.Printf("⚠️ Unsupported HTTP method: %s for function %s\n", method, fname)
						continue
					}

					// ✅ Final fallback ke path jika specific route kosong
					if cleanPath == "" {
						cleanPath = fcfg.Path
					}

					// ✅ Bersihkan path - hapus prefix groupName jika ada
					if strings.HasPrefix(cleanPath, "/"+groupName) {
						cleanPath = strings.TrimPrefix(cleanPath, "/"+groupName)
					}
					if cleanPath == "" {
						cleanPath = "/"
					}

					// ✅ Generate route berdasarkan method
					switch strings.ToUpper(method) {
					case "GET":
						routesCode.WriteString(fmt.Sprintf("\t%s.GET(\"%s\", %s.Get%s)\n",
							groupVar, cleanPath, handlerVar, endpoint.Name))
					case "POST":
						routesCode.WriteString(fmt.Sprintf("\t%s.POST(\"%s\", %s.Create%s)\n",
							groupVar, cleanPath, handlerVar, endpoint.Name))
					case "PUT":
						routesCode.WriteString(fmt.Sprintf("\t%s.PUT(\"%s\", %s.Update%s)\n",
							groupVar, cleanPath, handlerVar, endpoint.Name))
					case "DELETE":
						routesCode.WriteString(fmt.Sprintf("\t%s.DELETE(\"%s\", %s.Delete%s)\n",
							groupVar, cleanPath, handlerVar, endpoint.Name))
					}
				}
			}
		}

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

	// ✅ PERBAIKAN: Insert imports setelah "api-service/internal/database"
	if len(imports) > 0 {
		// ✅ PERBAIKAN: Hilangkan newline di awal, langsung import lines saja
		importSection := strings.Join(imports, "\n") + "\n"

		// ✅ PERBAIKAN: Cari posisi setelah "api-service/internal/database"
		databaseImportMarker := fmt.Sprintf("\"%s/internal/database\"", gc.ModuleName)
		if strings.Contains(routesContentStr, databaseImportMarker) {
			// Temukan posisi marker
			markerPos := strings.Index(routesContentStr, databaseImportMarker)
			// Temukan akhir baris dari marker
			endOfLinePos := strings.Index(routesContentStr[markerPos:], "\n") + markerPos
			// Insert import section setelah baris marker
			routesContentStr = routesContentStr[:endOfLinePos+1] + importSection + routesContentStr[endOfLinePos+1:]
			fmt.Printf("✅ Inserted imports after database import\n")
		} else {
			// Fallback: Insert setelah "import (" jika marker tidak ditemukan
			importMarker := "import ("
			if strings.Contains(routesContentStr, importMarker) {
				importIndex := strings.Index(routesContentStr, importMarker) + len(importMarker)
				routesContentStr = routesContentStr[:importIndex] + "\n" + importSection + routesContentStr[importIndex:]
				fmt.Printf("⚠️ Database import not found, inserted after import (\n")
			} else {
				fmt.Printf("⚠️ Could not find import section to insert imports\n")
			}
		}
	}

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

	insertionPoint := strings.Index(routesContentStr, publishedRoutesMarker) + len(publishedRoutesMarker)
	newRoutesContent := routesContentStr[:insertionPoint] + "\n" + strings.Join(allRoutes, "\n") + "\n" + routesContentStr[insertionPoint:]

	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", svc.Name)
	return nil
}
