package main

import (
	"bytes"
	"context"
	_ "embed"
	"fmt"
	"go/format"
	"go/types"
	"html/template"
	"log"
	"os"
	"sort"

	"golang.org/x/tools/go/packages"
)

//go:embed object.gotmpl
var objectGoTpl string

type TplState struct {
	ResourceNames []string
}

// main will generate a file that lists all rbac objects.
// This is to provide an "AllResources" function that is always
// in sync.
func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	path := "."
	if len(os.Args) > 1 {
		path = os.Args[1]
	}

	cfg := &packages.Config{
		Mode:    packages.NeedTypes | packages.NeedName | packages.NeedTypesInfo | packages.NeedDeps,
		Tests:   false,
		Context: ctx,
	}

	pkgs, err := packages.Load(cfg, path)
	if err != nil {
		log.Fatalf("Failed to load package: %s", err.Error())
	}

	if len(pkgs) != 1 {
		log.Fatalf("Expected 1 package, got %d", len(pkgs))
	}

	rbacPkg := pkgs[0]
	if rbacPkg.Name != "rbac" {
		log.Fatalf("Expected rbac package, got %q", rbacPkg.Name)
	}

	tpl, err := template.New("object.gotmpl").Parse(objectGoTpl)
	if err != nil {
		log.Fatalf("Failed to parse templates: %s", err.Error())
	}

	var out bytes.Buffer
	err = tpl.Execute(&out, TplState{
		ResourceNames: allResources(rbacPkg),
	})

	if err != nil {
		log.Fatalf("Execute template: %s", err.Error())
	}

	formatted, err := format.Source(out.Bytes())
	if err != nil {
		log.Fatalf("Format template: %s", err.Error())
	}

	_, _ = fmt.Fprint(os.Stdout, string(formatted))
}

func allResources(pkg *packages.Package) []string {
	var resources []string
	names := pkg.Types.Scope().Names()
	for _, name := range names {
		obj, ok := pkg.Types.Scope().Lookup(name).(*types.Var)
		if ok && obj.Type().String() == "github.com/coder/coder/v2/coderd/rbac.Object" {
			resources = append(resources, obj.Name())
		}
	}
	sort.Strings(resources)
	return resources
}
