// Copyright 2023 Chaos Mesh Authors.
//
// 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"
	"fmt"
	"os"
	"text/template"

	"github.com/pkg/errors"
)

// binaryGeneratedMkTemplate is the template for the file binary.generated.mk, use binaryGeneratedMkOptions as the context.
const binaryGeneratedMkTemplate = `# Generated by ./cmd/generate-makefile. DO NOT EDIT.

##@ Generated targets in binary.generated.mk

{{ .Content -}}

.PHONY: clean-binary
clean-binary:
{{- range .Recipes }}
	rm -f {{ .OutputPath }}
{{- end }}
`

// binaryRecipeTemplate is the template for one target, use binaryRecipeOptions as the context.
const binaryRecipeTemplate = `.PHONY: {{ .TargetName }}
{{ .TargetName }}: SHELL:=$(RUN_IN_BUILD_SHELL)
{{ .TargetName }}: image-build-env {{ StringsJoin .DependencyTargets " " }} ## {{ .Comment }}
{{- if .UseCGO }}
	$(CGO) build -ldflags "$(LDFLAGS)" -tags "${BUILD_TAGS}" -o {{ .OutputPath }} {{ .SourcePath }}
{{- else }}
	$(GO) build -ldflags "$(LDFLAGS)" -tags "${BUILD_TAGS}" -o {{ .OutputPath }} {{ .SourcePath }}
{{- end }}

`

type binaryRecipeOptions struct {
	// TargetName is the name of the makefile target.
	TargetName string
	// SourcePath is the path to the source file.
	SourcePath string
	// OutputPath is the path to the output file.
	OutputPath string
	// UseCGO introduces a CGO_ENABLED=1 environment variable to the build command.
	UseCGO bool
	// DependencyTargets are the targets that this target depends on.
	DependencyTargets []string
	// Comment is the comment for the target, do not need to include the leading `##`
	Comment string
}

type binaryGeneratedMkOptions struct {
	Recipes []binaryRecipeOptions
	Content string
}

func renderBinaryGeneratedMk(name string, fileTemplate string, binaryRecipeTemplate string, recipes []binaryRecipeOptions) error {
	targetFile, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return errors.Wrap(err, "open file "+name)
	}
	recipeTemplate, err := template.New(name + " recipe").Funcs(defaultFuncMap).Parse(binaryRecipeTemplate)
	if err != nil {
		return errors.Wrap(err, "parse "+name+" recipe template")
	}

	var buffer bytes.Buffer
	for _, recipe := range recipes {
		err := recipeTemplate.Execute(&buffer, recipe)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("render recipe in "+name+", recipe: %s", recipe.TargetName))
		}
	}
	binaryTemplate, err := template.New(name).Parse(fileTemplate)
	if err != nil {
		return errors.Wrap(err, "parse "+name+" template")
	}
	err = binaryTemplate.Execute(targetFile, binaryGeneratedMkOptions{
		Recipes: recipes,
		Content: buffer.String(),
	})
	if err != nil {
		return errors.Wrap(err, "render "+name)
	}
	return nil
}

// binaryRecipes is the list of binaryRecipes to generate, edit here to build new binaries.
var binaryRecipes = []binaryRecipeOptions{
	{
		TargetName:        "images/chaos-mesh/bin/chaos-controller-manager",
		SourcePath:        "cmd/chaos-controller-manager/main.go",
		OutputPath:        "images/chaos-mesh/bin/chaos-controller-manager",
		UseCGO:            false,
		DependencyTargets: nil,
		Comment:           "Build binary chaos-controller-manager",
	}, {
		TargetName: "images/chaos-daemon/bin/chaos-daemon",
		SourcePath: "cmd/chaos-daemon/main.go",
		OutputPath: "images/chaos-daemon/bin/chaos-daemon",
		UseCGO:     true,
		DependencyTargets: []string{
			"pkg/time/fakeclock/fake_clock_gettime.o",
			"pkg/time/fakeclock/fake_gettimeofday.o",
		},
		Comment: "Build binary chaos-daemon",
	}, {
		TargetName: "images/chaos-dashboard/bin/chaos-dashboard",
		SourcePath: "cmd/chaos-dashboard/main.go",
		OutputPath: "images/chaos-dashboard/bin/chaos-dashboard",
		UseCGO:     true,
		DependencyTargets: []string{
			"ui",
		},
		Comment: "Build binary chaos-dashboard",
	}, {
		TargetName:        "images/chaos-daemon/bin/cdh",
		SourcePath:        "cmd/chaos-daemon-helper/main.go",
		OutputPath:        "images/chaos-daemon/bin/cdh",
		UseCGO:            true,
		DependencyTargets: nil,
		Comment:           "Build binary chaos-daemon-helper",
	},
}
