package main

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

// BpjsHandlerData contains template data for BPJS handler generation
type BpjsHandlerData struct {
	Name           string
	NameLower      string
	NameUpper      string
	Category       string
	CategoryPath   string
	ModuleName     string
	HasGet         bool
	HasPost        bool
	HasPut         bool
	HasDelete      bool
	GetEndpoint    string
	PostEndpoint   string
	PutEndpoint    string
	DeleteEndpoint string
	Timestamp      string
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run generate-bpjs-handler.go [category/]entity [methods]")
		fmt.Println("Examples:")
		fmt.Println(" go run generate-bpjs-handler.go vclaim/peserta get post put delete")
		fmt.Println(" go run generate-bpjs-handler.go eclaim/sep get post")
		fmt.Println(" go run generate-bpjs-handler.go peserta get")
		os.Exit(1)
	}

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

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

	// Format names
	entityName = strings.Title(entityName) // PascalCase entity name
	entityLower := strings.ToLower(entityName)
	entityUpper := strings.ToUpper(entityName)

	data := BpjsHandlerData{
		Name:         entityName,
		NameLower:    entityLower,
		NameUpper:    entityUpper,
		Category:     category,
		CategoryPath: category,
		ModuleName:   "api-service",
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
	}

	// Set methods and endpoints based on arguments
	for _, m := range methods {
		switch strings.ToLower(m) {
		case "get":
			data.HasGet = true
			data.GetEndpoint = fmt.Sprintf("/%s/{id}", entityUpper)
		case "post":
			data.HasPost = true
			data.PostEndpoint = fmt.Sprintf("/%s/2.0/insert", entityUpper)
		case "put":
			data.HasPut = true
			data.PutEndpoint = fmt.Sprintf("/%s/2.0/update", entityUpper)
		case "delete":
			data.HasDelete = true
			data.DeleteEndpoint = fmt.Sprintf("/%s/2.0/delete", entityUpper)
		}
	}

	// Create directories with dynamic logic (sama seperti generate-handler.go)
	var handlerDir, modelDir string
	if category != "" {
		// Dengan kategori: internal/handlers/category/
		handlerDir = filepath.Join("internal", "handlers", category)
		modelDir = filepath.Join("internal", "models", category)
	} else {
		// Tanpa kategori: langsung internal/handlers/
		handlerDir = filepath.Join("internal", "handlers")
		modelDir = filepath.Join("internal", "models")
	}

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

	// Generate files
	generateBpjsHandlerFile(data, handlerDir)
	generateBpjsModelFile(data, modelDir)
	updateBpjsRoutesFile(data)

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

// ================= HANDLER GENERATION =====================

func generateBpjsHandlerFile(data BpjsHandlerData, handlerDir string) {
	// Build import path based on category (sama seperti generate-handler.go)
	var modelsImportPath string
	if data.Category != "" {
		modelsImportPath = data.ModuleName + "/internal/models/" + data.Category
	} else {
		modelsImportPath = data.ModuleName + "/internal/models"
	}

	handlerContent := `package handlers

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"` + data.ModuleName + `/internal/config"
	models "` + modelsImportPath + `"
	services "` + data.ModuleName + `/internal/services/bpjs"

	"github.com/gin-gonic/gin"
)

// ` + data.Name + `Handler handles ` + data.NameLower + ` BPJS services
type ` + data.Name + `Handler struct {
	service services.VClaimService
}

// New` + data.Name + `Handler creates a new ` + data.Name + `Handler
func New` + data.Name + `Handler(cfg config.BpjsConfig) *` + data.Name + `Handler {
	return &` + data.Name + `Handler{
		service: services.NewService(cfg),
	}
}`

	// Add methods based on flags
	if data.HasPost {
		handlerContent += generateBpjsCreateMethod(data)
	}

	if data.HasPut {
		handlerContent += generateBpjsUpdateMethod(data)
	}

	if data.HasDelete {
		handlerContent += generateBpjsDeleteMethod(data)
	}

	if data.HasGet {
		handlerContent += generateBpjsGetMethod(data)
	}

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

func generateBpjsCreateMethod(data BpjsHandlerData) string {
	// Build route path based on category (dynamic, tidak hardcode bpjs/)
	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	// Tag untuk swagger
	var tagName string
	if data.Category != "" {
		tagName = data.Category + "-" + data.NameLower
	} else {
		tagName = data.NameLower
	}

	return `

// Create` + data.Name + ` godoc
// @Summary Create a new ` + data.NameUpper + `
// @Description Create a new ` + data.Name + ` in BPJS system
// @Tags ` + tagName + `
// @Accept json
// @Produce json
// @Param request body models.` + data.Name + `PostRequest true "` + data.Name + ` creation request"
// @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` created successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /api/v1/` + routePath + ` [post]
func (h *` + data.Name + `Handler) Create` + data.Name + `(c *gin.Context) {
	var req models.` + data.Name + `PostRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body", "message": err.Error()})
		return
	}

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

	var result map[string]interface{}
	if err := h.service.Post(ctx, "` + data.PostEndpoint + `", req, &result); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "create failed", "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, models.` + data.Name + `Response{
		Message: "` + data.Name + ` berhasil dibuat",
		Data:    result,
	})
}`
}

func generateBpjsUpdateMethod(data BpjsHandlerData) string {
	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	var tagName string
	if data.Category != "" {
		tagName = data.Category + "-" + data.NameLower
	} else {
		tagName = data.NameLower
	}

	return `

// Update` + data.Name + ` godoc
// @Summary Update an existing ` + data.NameUpper + `
// @Description Update an existing ` + data.Name + ` in BPJS system
// @Tags ` + tagName + `
// @Accept json
// @Produce json
// @Param request body models.` + data.Name + `PutRequest true "` + data.Name + ` update request"
// @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` updated successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /api/v1/` + routePath + ` [put]
func (h *` + data.Name + `Handler) Update` + data.Name + `(c *gin.Context) {
	var req models.` + data.Name + `PutRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body", "message": err.Error()})
		return
	}

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

	var result map[string]interface{}
	if err := h.service.Put(ctx, "` + data.PutEndpoint + `", req, &result); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed", "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, models.` + data.Name + `Response{
		Message: "` + data.Name + ` berhasil diperbarui",
		Data:    result,
	})
}`
}

func generateBpjsDeleteMethod(data BpjsHandlerData) string {
	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	var tagName string
	if data.Category != "" {
		tagName = data.Category + "-" + data.NameLower
	} else {
		tagName = data.NameLower
	}

	return `

// Delete` + data.Name + ` godoc
// @Summary Delete an existing ` + data.NameUpper + `
// @Description Delete a ` + data.Name + ` by ID
// @Tags ` + tagName + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID"
// @Param user query string true "User"
// @Success 200 {object} models.` + data.Name + `Response "` + data.Name + ` deleted successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /api/v1/` + routePath + `/{id} [delete]
func (h *` + data.Name + `Handler) Delete` + data.Name + `(c *gin.Context) {
	id := c.Param("id")
	user := c.Query("user")
	if id == "" || user == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id & user required"})
		return
	}

	body := models.` + data.Name + `DeleteRequest{}
	body.T` + data.Name + `.ID = id
	body.T` + data.Name + `.User = user

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

	if err := h.service.Delete(ctx, "` + data.DeleteEndpoint + `", body); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed", "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, models.` + data.Name + `Response{
		Message: "` + data.Name + ` berhasil dihapus",
		Data:    nil,
	})
}`
}

func generateBpjsGetMethod(data BpjsHandlerData) string {
	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	var tagName string
	if data.Category != "" {
		tagName = data.Category + "-" + data.NameLower
	} else {
		tagName = data.NameLower
	}

	return `

// Get` + data.Name + ` godoc
// @Summary Get an existing ` + data.NameUpper + `
// @Description Retrieve a ` + data.Name + ` by ID
// @Tags ` + tagName + `
// @Accept json
// @Produce json
// @Param id path string true "` + data.Name + ` ID"
// @Success 200 {object} models.` + data.Name + `Response "Data ` + data.Name + ` retrieved successfully"
// @Failure 400 {object} gin.H "Invalid request"
// @Failure 500 {object} gin.H "Internal server error"
// @Router /api/v1/` + routePath + `/{id} [get]
func (h *` + data.Name + `Handler) Get` + data.Name + `(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id required"})
		return
	}

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

	endpoint := fmt.Sprintf("` + strings.Replace(data.GetEndpoint, "{id}", "%s", 1) + `", id)
	var result map[string]interface{}
	if err := h.service.Get(ctx, endpoint, &result); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "fetch failed", "message": err.Error()})
		return
	}

	c.JSON(http.StatusOK, models.` + data.Name + `Response{
		Message: "Data ` + data.Name + ` berhasil diambil",
		Data:    result,
	})
}`
}

// ================= MODEL GENERATION =====================

func generateBpjsModelFile(data BpjsHandlerData, modelDir string) {
	modelContent := `package models

// ` + data.Name + ` BPJS Models
// Generated at: ` + data.Timestamp + `
// Category: ` + data.Category + `

// Common Response Structure
type ` + data.Name + `Response struct {
	Message string                 ` + "`json:\"message\"`" + `
	Data    map[string]interface{} ` + "`json:\"data,omitempty\"`" + `
}

type ` + data.Name + `RawResponse struct {
	MetaData struct {
		Code    string ` + "`json:\"code\"`" + `
		Message string ` + "`json:\"message\"`" + `
	} ` + "`json:\"metaData\"`" + `
	Response interface{} ` + "`json:\"response\"`" + `
}`

	if data.HasPost {
		modelContent += `

// ` + data.Name + ` POST Request Structure
type ` + data.Name + `PostRequest struct {
	T` + data.Name + ` ` + data.Name + `Post ` + "`json:\"t_" + data.NameLower + "\" binding:\"required\"`" + `
}

type ` + data.Name + `Post struct {
	// Add your specific fields here based on BPJS API requirements
	NoKartu      string ` + "`json:\"noKartu\" binding:\"required\"`" + `
	TglLayanan   string ` + "`json:\"tglLayanan\" binding:\"required\"`" + `
	JnsPelayanan string ` + "`json:\"jnsPelayanan\" binding:\"required\"`" + `
	User         string ` + "`json:\"user\" binding:\"required\"`" + `
}`
	}

	if data.HasPut {
		modelContent += `

// ` + data.Name + ` PUT Request Structure
type ` + data.Name + `PutRequest struct {
	T` + data.Name + ` ` + data.Name + `Put ` + "`json:\"t_" + data.NameLower + "\" binding:\"required\"`" + `
}

type ` + data.Name + `Put struct {
	ID           string ` + "`json:\"id\" binding:\"required\"`" + `
	NoKartu      string ` + "`json:\"noKartu\"`" + `
	TglLayanan   string ` + "`json:\"tglLayanan\"`" + `
	JnsPelayanan string ` + "`json:\"jnsPelayanan\"`" + `
	User         string ` + "`json:\"user\" binding:\"required\"`" + `
}`
	}

	if data.HasDelete {
		modelContent += `

// ` + data.Name + ` DELETE Request Structure
type ` + data.Name + `DeleteRequest struct {
	T` + data.Name + ` struct {
		ID   string ` + "`json:\"id\" binding:\"required\"`" + `
		User string ` + "`json:\"user\" binding:\"required\"`" + `
	} ` + "`json:\"t_" + data.NameLower + "\" binding:\"required\"`" + `
}`
	}

	// Add helper structures
	modelContent += `

// Common Helper Structures
type Flag struct {
	Flag string ` + "`json:\"flag\" binding:\"required\"`" + `
}

type Poli struct {
	Tujuan    string ` + "`json:\"tujuan\"`" + `
	Eksekutif string ` + "`json:\"eksekutif\" binding:\"required\"`" + `
}

// Validation helpers
func IsValidStatus(status string) bool {
	validStatuses := []string{"active", "inactive", "pending", "processed"}
	for _, v := range validStatuses {
		if v == status {
			return true
		}
	}
	return false
}`

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

// ================= ROUTES GENERATION =====================

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

	routesContent := string(content)

	// Build import path berdasarkan category (sama seperti generate-handler.go)
	var importPath, importAlias string
	if data.Category != "" {
		importPath = fmt.Sprintf("%s/internal/handlers/%s", data.ModuleName, data.Category)
		importAlias = data.NameLower + "Handlers"
	} else {
		importPath = fmt.Sprintf("%s/internal/handlers", data.ModuleName)
		importAlias = data.NameLower + "Handlers"
	}

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

	// Build routes
	newRoutes := fmt.Sprintf("\t\t// %s endpoints\n", data.Name)
	if data.Category != "" {
		newRoutes = fmt.Sprintf("\t\t// %s %s endpoints\n", data.Category, data.Name)
	}
	newRoutes += fmt.Sprintf("\t\t%sHandler := %s.New%sHandler(config.LoadConfig().Bpjs)\n",
		data.NameLower, importAlias, data.Name)

	// Build route paths berdasarkan category (dynamic, tidak hardcode)
	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	if data.HasGet {
		newRoutes += fmt.Sprintf("\t\tv1.GET(\"/%s/:id\", %sHandler.Get%s)\n",
			routePath, data.NameLower, data.Name)
	}

	if data.HasPost {
		newRoutes += fmt.Sprintf("\t\tv1.POST(\"/%s\", %sHandler.Create%s)\n",
			routePath, data.NameLower, data.Name)
	}

	if data.HasPut {
		newRoutes += fmt.Sprintf("\t\tv1.PUT(\"/%s\", %sHandler.Update%s)\n",
			routePath, data.NameLower, data.Name)
	}

	if data.HasDelete {
		newRoutes += fmt.Sprintf("\t\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n",
			routePath, data.NameLower, data.Name)
	}

	newRoutes += "\n"

	// Insert routes
	insertMarker := "\t\tprotected := v1.Group(\"/\")"
	if strings.Contains(routesContent, insertMarker) {
		if !strings.Contains(routesContent, fmt.Sprintf("New%sHandler", data.Name)) {
			routesContent = strings.Replace(routesContent, insertMarker,
				newRoutes+insertMarker, 1)
		} else {
			fmt.Printf("✅ Routes for %s already exist, skipping...\n", data.Name)
			return
		}
	}

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

	if data.Category != "" {
		fmt.Printf("✅ Updated routes.go with %s %s endpoints\n", data.Category, data.Name)
	} else {
		fmt.Printf("✅ Updated routes.go with %s endpoints\n", data.Name)
	}
}

func printBpjsRoutesSample(data BpjsHandlerData) {
	var importAlias string
	if data.Category != "" {
		importAlias = data.NameLower + "Handlers"
	} else {
		importAlias = data.NameLower + "Handlers"
	}

	var routePath string
	if data.Category != "" {
		routePath = data.Category + "/" + data.NameLower
	} else {
		routePath = data.NameLower
	}

	if data.Category != "" {
		fmt.Printf(`
// %s %s endpoints
%sHandler := %s.New%sHandler(config.LoadConfig().Bpjs)
`, data.Category, data.Name, data.NameLower, importAlias, data.Name)
	} else {
		fmt.Printf(`
// %s endpoints
%sHandler := %s.New%sHandler(config.LoadConfig().Bpjs)
`, data.Name, data.NameLower, importAlias, data.Name)
	}

	if data.HasGet {
		fmt.Printf("\tv1.GET(\"/%s/:id\", %sHandler.Get%s)\n", routePath, data.NameLower, data.Name)
	}
	if data.HasPost {
		fmt.Printf("\tv1.POST(\"/%s\", %sHandler.Create%s)\n", routePath, data.NameLower, data.Name)
	}
	if data.HasPut {
		fmt.Printf("\tv1.PUT(\"/%s\", %sHandler.Update%s)\n", routePath, data.NameLower, data.Name)
	}
	if data.HasDelete {
		fmt.Printf("\tv1.DELETE(\"/%s/:id\", %sHandler.Delete%s)\n", routePath, data.NameLower, data.Name)
	}

	fmt.Println()
}

// ================= UTILITY FUNCTIONS =====================

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

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