package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"go/format"
	"go/scanner"
	"log"
	"os"
	"strings"
	"text/template"
)

type options struct {
	jsonSource string
	template   string
	output     string
}

const version = "0.3.0"

func main() {
	log.SetFlags(log.Llongfile)
	opts := &options{
		template: errorsTemplate,
	}
	flag.StringVar(&opts.jsonSource, "json", "", `path to the ably-common/protocol/errors.json`)
	flag.StringVar(&opts.output, "o", "", "file to write output")
	flag.Parse()
	if opts.jsonSource == "" {
		flag.PrintDefaults()
		return
	}
	sourceFile, err := os.ReadFile(opts.jsonSource)
	if err != nil {
		log.Fatal(err)
	}

	codeToDesc := make(map[int]string)
	err = json.Unmarshal(sourceFile, &codeToDesc)
	if err != nil {
		log.Fatal(err)
	}
	tpl, err := template.New("errors").Parse(opts.template)
	if err != nil {
		log.Fatal(err)
	}
	var buf bytes.Buffer
	err = tpl.Execute(&buf, map[string]interface{}{
		"codes":   codeToDesc,
		"version": version,
	})
	if err != nil {
		log.Fatal(err)
	}
	goFile, err := format.Source(buf.Bytes())
	if err != nil {
		var sErr scanner.ErrorList
		if errors.As(err, &sErr) {
			// Try to print out the line that caused the error.
			lines := strings.Split(buf.String(), "\n")
			pos := (sErr)[0].Pos
			log.Println("Error in line", pos.Line, lines[pos.Line])
		}
		log.Fatal(err)
	}
	if opts.output != "" {
		err = os.WriteFile(opts.output, goFile, 0600)
		if err != nil {
			log.Fatal(err)
		}
	}
}

const errorsTemplate = `// Code generated by errors-const v{{.version}} tool DO NOT EDIT.

package ably

// ErrorCode is the type for predefined Ably error codes.
type ErrorCode int

func (c ErrorCode) String() string {
	switch c {
	case 0:
		return "(error code not set)"
	{{- range $code, $desc :=  .codes}}
	case {{ $code}}: return "{{ $desc}}"
	{{- end}}
	}
	return ""
}
`
