// Package generate provides code generation commands for micro
package gen

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/urfave/cli/v2"
	"go-micro.dev/v5/cmd"
)

var handlerTemplate = `package handler

import (
	"context"

	log "go-micro.dev/v5/logger"
)

type {{.Name}} struct{}

func New{{.Name}}() *{{.Name}} {
	return &{{.Name}}{}
}

{{range .Methods}}
// {{.Name}} handles {{.Name}} requests
func (h *{{$.Name}}) {{.Name}}(ctx context.Context, req *{{.RequestType}}, rsp *{{.ResponseType}}) error {
	log.Infof("Received {{$.Name}}.{{.Name}} request")
	// TODO: implement
	return nil
}
{{end}}
`

var endpointTemplate = `package handler

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

	log "go-micro.dev/v5/logger"
)

// {{.Name}}Request is the request for {{.Name}}
type {{.Name}}Request struct {
	// Add request fields here
}

// {{.Name}}Response is the response for {{.Name}}
type {{.Name}}Response struct {
	// Add response fields here
}

// {{.Name}} handles HTTP {{.Method}} requests to /{{.Path}}
func {{.Name}}(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	log.Infof("Received {{.Name}} request")

	var req {{.Name}}Request
	if r.Method != http.MethodGet {
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	// TODO: implement handler logic
	_ = ctx
	_ = req

	rsp := {{.Name}}Response{}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(rsp)
}
`

var modelTemplate = `package model

import (
	"context"
	"time"
)

// {{.Name}} represents a {{lower .Name}} in the system
type {{.Name}} struct {
	ID        string    ` + "`json:\"id\"`" + `
	CreatedAt time.Time ` + "`json:\"created_at\"`" + `
	UpdatedAt time.Time ` + "`json:\"updated_at\"`" + `
	// Add your fields here
}

// {{.Name}}Repository defines the interface for {{lower .Name}} storage
type {{.Name}}Repository interface {
	Create(ctx context.Context, m *{{.Name}}) error
	Get(ctx context.Context, id string) (*{{.Name}}, error)
	Update(ctx context.Context, m *{{.Name}}) error
	Delete(ctx context.Context, id string) error
	List(ctx context.Context, offset, limit int) ([]*{{.Name}}, error)
}
`

type handlerData struct {
	Name    string
	Methods []methodData
}

type methodData struct {
	Name         string
	RequestType  string
	ResponseType string
}

type endpointData struct {
	Name   string
	Method string
	Path   string
}

type modelData struct {
	Name string
}

func generateHandler(c *cli.Context) error {
	name := c.Args().First()
	if name == "" {
		return fmt.Errorf("handler name required: micro generate handler <name>")
	}

	name = strings.Title(strings.ToLower(name))

	// Parse methods if provided
	methods := []methodData{}
	for _, m := range c.StringSlice("method") {
		methods = append(methods, methodData{
			Name:         strings.Title(m),
			RequestType:  strings.Title(m) + "Request",
			ResponseType: strings.Title(m) + "Response",
		})
	}

	if len(methods) == 0 {
		methods = []methodData{
			{Name: "Handle", RequestType: "Request", ResponseType: "Response"},
		}
	}

	data := handlerData{
		Name:    name,
		Methods: methods,
	}

	return generateFile("handler", strings.ToLower(name)+".go", handlerTemplate, data)
}

func generateEndpoint(c *cli.Context) error {
	name := c.Args().First()
	if name == "" {
		return fmt.Errorf("endpoint name required: micro generate endpoint <name>")
	}

	data := endpointData{
		Name:   strings.Title(strings.ToLower(name)),
		Method: strings.ToUpper(c.String("method")),
		Path:   c.String("path"),
	}

	if data.Path == "" {
		data.Path = strings.ToLower(name)
	}

	return generateFile("handler", strings.ToLower(name)+"_endpoint.go", endpointTemplate, data)
}

func generateModel(c *cli.Context) error {
	name := c.Args().First()
	if name == "" {
		return fmt.Errorf("model name required: micro generate model <name>")
	}

	data := modelData{
		Name: strings.Title(strings.ToLower(name)),
	}

	return generateFile("model", strings.ToLower(name)+".go", modelTemplate, data)
}

func generateFile(dir, filename, tmplStr string, data interface{}) error {
	// Create directory if it doesn't exist
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("failed to create directory %s: %w", dir, err)
	}

	filepath := filepath.Join(dir, filename)

	// Check if file exists
	if _, err := os.Stat(filepath); err == nil {
		return fmt.Errorf("file %s already exists", filepath)
	}

	fn := template.FuncMap{
		"title": strings.Title,
		"lower": strings.ToLower,
	}

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

	f, err := os.Create(filepath)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer f.Close()

	if err := tmpl.Execute(f, data); err != nil {
		return fmt.Errorf("failed to execute template: %w", err)
	}

	fmt.Printf("Created %s\n", filepath)
	return nil
}

func init() {
	cmd.Register(&cli.Command{
		Name:    "generate",
		Usage:   "Generate code scaffolding (like Rails generators)",
		Aliases: []string{"gen"},
		Subcommands: []*cli.Command{
			{
				Name:   "handler",
				Usage:  "Generate a handler: micro g handler <name>",
				Action: generateHandler,
				Flags: []cli.Flag{
					&cli.StringSliceFlag{
						Name:    "method",
						Aliases: []string{"m"},
						Usage:   "Methods to generate (can be repeated)",
					},
				},
			},
			{
				Name:   "endpoint",
				Usage:  "Generate an HTTP endpoint: micro g endpoint <name>",
				Action: generateEndpoint,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "method",
						Aliases: []string{"m"},
						Usage:   "HTTP method (GET, POST, etc.)",
						Value:   "POST",
					},
					&cli.StringFlag{
						Name:    "path",
						Aliases: []string{"p"},
						Usage:   "URL path for the endpoint",
					},
				},
			},
			{
				Name:   "model",
				Usage:  "Generate a model: micro g model <name>",
				Action: generateModel,
			},
		},
	})
}
