package main

import (
	"encoding/json"
	"fmt"
	"os/exec"

	"gopkg.in/yaml.v3"
)

type NixFlakeOutput struct {
	Apps                 map[string]interface{} `json:"apps"`
	Checks               map[string]interface{} `json:"checks"`
	DarwinConfigurations ArchTypes              `json:"darwinConfigurations"`
	DevShells            map[string]interface{} `json:"devShells"`
	HomeConfigurations   map[string]interface{} `json:"homeConfigurations"`
	NixosConfigurations  map[string]interface{} `json:"nixosConfigurations"`
	Packages             ArchTypes              `json:"packages"`
}

type ArchTypes struct {
	X86_64    map[string]map[string]string `json:"x86_64-linux"`
	Aarch64   map[string]map[string]string `json:"aarch64-linux"`
	DarwinX86 map[string]map[string]string `json:"x86_64-darwin"`
	DarwinArm map[string]map[string]string `json:"aarch64-darwin"`
}

func GetNixFlakeOutput() (*NixFlakeOutput, error) {
	cmd := exec.Command("nix", "flake", "show", "--json")
	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	var flakeOutput NixFlakeOutput
	err = json.Unmarshal(output, &flakeOutput)
	if err != nil {
		return nil, err
	}

	return &flakeOutput, nil
}

type GitlabCIJob struct {
	Stage  string   `json:"stage"`
	Tags   []string `json:"tags"`
	Script []string `json:"script"`
}

func NewGitlabCIJob(target string) GitlabCIJob {
	return GitlabCIJob{
		Stage: "build",
		Tags:  []string{"nix"},
		Script: []string{
			"nix build -L '.#" + target + "'",
		},
	}
}

func main() {
	flakeOutput, err := GetNixFlakeOutput()
	if err != nil {
		panic(err)
	}

	// Some boilerplate that Go requires us to have
	pipeline := make(map[string]interface{})
	pipeline["stages"] = []string{"build"}
	pipeline["Run flake check"] = GitlabCIJob{
		Stage: "build",
		Tags:  []string{"nix"},
		Script: []string{
			"nix flake check --no-build",
		},
	}
	// Since this is kinda a one-off thing, I can use this directly
	// homeConfigurations are treated like they are not known as a
	// flake output type. Therefore, it just informs you that it is
	// part of the output, but does not evaluate deeper to tell you
	// what the name of it is. I will just do this manually for now.
	pipeline["Build Home config "] = NewGitlabCIJob("homeConfigurations.\"gregory.hellings\".activationPackage")

	if flakeOutput.Apps != nil {
		for appName := range flakeOutput.Apps {
			pipeline[appName] = NewGitlabCIJob("apps." + appName)
		}
	}

	if flakeOutput.Packages.X86_64 != nil {
		for pkgName := range flakeOutput.Packages.X86_64 {
			if flakeOutput.Packages.X86_64[pkgName]["type"] == "derivation" {
				pipeline["Build package "+pkgName] = NewGitlabCIJob("packages.x86_64-linux." + pkgName)
			}
		}
	}

	if flakeOutput.NixosConfigurations != nil {
		for configName := range flakeOutput.NixosConfigurations {
			pipeline["Build NixOS config "+configName] = NewGitlabCIJob("nixosConfigurations." + configName + ".config.system.build.toplevel")
		}
	}

	yamlData, err := yaml.Marshal(pipeline)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%s\n", yamlData)
}
