//
// Copyright 2020 Chef Software, Inc.
// Author: Salim Afiune <afiune@chef.io>
// Source: https://github.com/afiune/godist
// Documentation: https://godoc.org/github.com/chef/go-libs/distgen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/format"
	"io/ioutil"
	"net/http"
	"os"
	"text/template"
	"time"
)

var (
	globalVariables map[string]string
	tplBuf          bytes.Buffer

	// the file name that this automation will generate (default `dist.go`)
	//
	// *customizable* (args:1)
	goFileName = "dist.go"

	// the go package to use inside the generated file.go (default: `main`)
	//
	// *customizable* (args:2)
	goPackage = "main"

	// a JSON format file where the global variables are defined
	// (by default the automation use the local `glob_dist.json`)
	//
	// *customizable* (args:3)
	globDistJson = "https://raw.github.com/chef/go-libs/master/distgen/glob_dist.json"
	// TODO @afiune use expeditor to run the gocode_generation helper from within an studio

	// a dist template to generate global variables
	// NOTE: @afiune in the future, if there are more customizations we
	// need, we could either modify this template or create new ones
	distTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
//
// file generated at: {{ .Timestamp }}
// using data from:   {{ .URL }}
// and generator:     https://github.com/chef/go-libs/distgen
//

package {{ .GoPackage }}

const (
{{- range $varName, $varValue := .GlobalVariables }}
  {{ printf "%v = %q" $varName $varValue }}
{{- end }}
)
`))
)

func fatal(msg string, err error) {
	fmt.Fprintf(os.Stderr, "%s: %s", msg, err.Error())
	os.Exit(1)
}

func main() {
	if len(os.Args) > 1 {
		goFileName = os.Args[1]
	}

	if len(os.Args) > 2 {
		goPackage = os.Args[2]
	}

	if len(os.Args) > 3 {
		globDistJson = os.Args[3]
	}

	file, ok := os.LookupEnv("DIST_FILE")
	if ok {
		globDistJson = file
	}

	rsp, err := http.Get(globDistJson)
	if err != nil {
		fatal("failed to get global variables file", err)
	}

	jsonRaw, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		fatal(fmt.Sprintf("failed to read body from %s", globDistJson), err)
	}

	err = json.Unmarshal(jsonRaw, &globalVariables)
	if err != nil {
		fatal("failed to unmarshal raw json", err)
	}

	err = distTemplate.Execute(&tplBuf, struct {
		Timestamp       time.Time
		URL             string
		GoPackage       string
		GlobalVariables map[string]string
	}{
		Timestamp:       time.Now(),
		URL:             globDistJson,
		GoPackage:       goPackage,
		GlobalVariables: globalVariables,
	})
	if err != nil {
		fatal("failed to write template", err)
	}

	f, err := os.Create(goFileName)
	if err != nil {
		fatal("failed to create file", err)
	}
	defer f.Close()

	// two reasons why we format the go template:
	// 1) to have a nice format :wink:
	// 2) also to verify that the generate go code is parsable/executable
	formatted, err := format.Source(tplBuf.Bytes())
	if err != nil {
		fatal("unable to format generate go template", err)
	}

	_, err = f.Write(formatted)
	if err != nil {
		fatal("failed to write file", err)
	}
}
