package main

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

	"gopkg.in/yaml.v3"
)

// ErrorCode represents a single error code definition
type ErrorCode struct {
	Code    string `yaml:"code"`
	Message string `yaml:"message"`
}

// ErrorCodes represents the structure of the YAML file
type ErrorCodes struct {
	Errors []ErrorCode `yaml:"errors"`
}

// Template for generating the Go code
const errorCodesTemplate = `// Code generated by tools/generate_error_codes.go; DO NOT EDIT.
package errsystem

var (
{{- range .Errors }}
	{{ .VarName }} = errorType{
		Code:    "{{ .Code }}",
		Message: "{{ .Message }}",
	}
{{- end }}
)
`

// ErrorCodeWithVarName extends ErrorCode with a variable name
type ErrorCodeWithVarName struct {
	ErrorCode
	VarName string
}

// GenerateVarName creates a Go variable name from an error code and message
func GenerateVarName(code string, message string) string {
	// Convert message to camel case for variable name
	// e.g., "Failed to delete agents" -> "DeleteAgents"
	words := strings.Fields(message)
	var camelCase string

	// Skip common verbs and prepositions
	skipWords := map[string]bool{
		"failed": true,
		"unable": true,
		"cannot": true,
		"error":  true,
		"to":     true,
		"the":    true,
		"a":      true,
		"an":     true,
		"in":     true,
		"on":     true,
		"at":     true,
		"with":   true,
		"for":    true,
	}

	for _, word := range words {
		if !skipWords[strings.ToLower(word)] && len(word) > 0 {
			camelCase += strings.ToUpper(word[:1]) + strings.ToLower(word[1:])
		}
	}

	// If no words remain after filtering, use the code number
	if camelCase == "" {
		parts := strings.Split(code, "-")
		if len(parts) == 2 {
			return "Err" + parts[1]
		}
		return "Err" + code
	}

	return "Err" + camelCase
}

func main() {
	// Determine the working directory and project root
	workingDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error getting working directory: %v", err)
	}

	// Find the project root (where error_codes.yaml is located)
	// This handles both running from project root and from within a package via go:generate
	var projectRoot string

	// Check if we're running from the tools directory
	if filepath.Base(workingDir) == "tools" {
		projectRoot = filepath.Dir(workingDir)
	} else if filepath.Base(workingDir) == "errsystem" {
		// We're likely running from the errsystem package via go:generate
		projectRoot = filepath.Dir(filepath.Dir(workingDir))
	} else {
		// Assume we're at the project root
		projectRoot = workingDir
	}

	// Paths relative to project root
	yamlPath := filepath.Join(projectRoot, "error_codes.yaml")
	outputPath := filepath.Join(projectRoot, "internal", "errsystem", "errorcodes.go")

	// Read the YAML file
	yamlFile, err := os.ReadFile(yamlPath)
	if err != nil {
		log.Fatalf("Error reading YAML file %s: %v", yamlPath, err)
	}

	// Parse the YAML
	var errorCodes ErrorCodes
	err = yaml.Unmarshal(yamlFile, &errorCodes)
	if err != nil {
		log.Fatalf("Error parsing YAML: %v", err)
	}

	// Create a slice of ErrorCodeWithVarName
	var errorCodesWithVarNames []ErrorCodeWithVarName
	for _, ec := range errorCodes.Errors {
		errorCodesWithVarNames = append(errorCodesWithVarNames, ErrorCodeWithVarName{
			ErrorCode: ec,
			VarName:   GenerateVarName(ec.Code, ec.Message),
		})
	}

	// Create a template
	tmpl, err := template.New("errorCodes").Parse(errorCodesTemplate)
	if err != nil {
		log.Fatalf("Error creating template: %v", err)
	}

	// Ensure the directory exists
	dir := filepath.Dir(outputPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		log.Fatalf("Error creating directory: %v", err)
	}

	// Create the output file
	outputFile, err := os.Create(outputPath)
	if err != nil {
		log.Fatalf("Error creating output file: %v", err)
	}
	defer outputFile.Close()

	// Execute the template
	err = tmpl.Execute(outputFile, struct {
		Errors []ErrorCodeWithVarName
	}{
		Errors: errorCodesWithVarNames,
	})
	if err != nil {
		log.Fatalf("Error executing template: %v", err)
	}

	fmt.Printf("Successfully generated %s\n", outputPath)
}
