// The ablyagent program generates code for the ablyagent package using
// protocol/agents.json.
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"text/template"
)

var (
	path = flag.String("path", "./protocol/agents.json", "Path to agents.json")
	out  = flag.String("out", "ablyagent.go", "File to write the generated code to")
)

// Data represents the source data in protocol/agents.json
type Data struct {
	Agents []struct {
		Identifier string `json:"identifier"`
		Type       string `json:"type"`
		Versioned  bool   `json:"versioned"`
	} `json:"agents"`
	AblyLibMappings map[string]string `json:"ablyLibMappings"`
}

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

func run() error {
	// load agents.json
	f, err := os.Open(*path)
	if err != nil {
		return err
	}
	defer f.Close()
	var data Data
	if err := json.NewDecoder(f).Decode(&data); err != nil {
		return fmt.Errorf("error parsing %s: %w", *path, err)
	}

	// render the template with the loaded agents
	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, &data); err != nil {
		return err
	}
	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		return err
	}

	// write the generated code to the output file
	fmt.Println("Writing generated ablyagent code to", *out)
	return ioutil.WriteFile(*out, formatted, 0644)
}

var tmpl = template.Must(template.New("generated.go").Parse(`
// Code generated by github.com/ably/ably-common/go/cmd/ablyagent; DO NOT EDIT.

package ablyagent

// Agent is an agent identifier that can appear in the Ably-Agent HTTP header
// and be tracked as a metric.
//
// See protocol/agents.json.
type Agent struct {
	Identifier string
	Type       string
	Versioned  bool
}

// AblyLibMapping is an agent identifier that can appear in the X-Ably-Lib HTTP
// header and be converted to its equivalent Ably-Agent value.
//
// See protocol/agents.json.
type AblyLibMapping struct {
	Lib   string
	Agent string
}

// Agents is the "agents" list from protocol/agents.json.
var Agents = []*Agent{
	{{- range .Agents }}
	{
		Identifier: "{{ .Identifier }}",
		Type:       "{{ .Type }}",
		Versioned:  {{ .Versioned }},
	},
	{{- end }}
}

// AblyLibMappings is the "ablyLibMappings" list from protocol/agents.json.
var AblyLibMappings = []*AblyLibMapping{
	{{- range $lib, $agent := .AblyLibMappings }}
	{
		Lib:   "{{ $lib }}",
		Agent: "{{ $agent }}",
	},
	{{- end }}
}
`[1:]))
