package main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"git.gensokyo.uk/yonah/nixbuild"
	"hakurei.app/command"
)

type commandHandlerError string

func (c commandHandlerError) Error() string { return string(c) }

func main() {
	var (
		flagNixOS   bool
		flagVerbose bool
		flagJSON    bool
	)
	c := command.New(os.Stderr, log.Printf, "nixbuild", func(args []string) error {
		log.SetFlags(0)
		log.SetPrefix("nixbuild: ")
		nixbuild.Stdout = os.Stdout
		if flagVerbose {
			nixbuild.Stderr = os.Stderr
		}
		return nil
	}).
		Flag(&flagNixOS, "nixos", command.BoolFlag(false), "Interpret input as NixOS flake installable").
		Flag(&flagVerbose, "v", command.BoolFlag(false), "Connect nix stderr").
		Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")

	c.Command("instantiated", "Evaluate an installable and output all derivations it instantiated", func(args []string) error {
		if len(args) != 1 {
			return commandHandlerError("instantiated requires exactly 1 argument")
		}

		installable := args[0]
		if flagNixOS {
			fields := strings.SplitN(installable, "#", 2)
			switch len(fields) {
			case 2:
				installable = nixbuild.NixOSInstallable(fields[0], fields[1])
			case 1:
				hostname, err := os.Hostname()
				if err != nil {
					return commandHandlerError(fmt.Sprintf("cannot get hostname: %v", err))
				}
				installable = nixbuild.NixOSInstallable(fields[0], hostname)
			default:
				return commandHandlerError("unexpected installable")
			}
		}

		ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
		defer stop()

		if v, err := nixbuild.EvalInstantiated(ctx, installable); err != nil {
			return commandHandlerError(fmt.Sprintf("cannot evaluate for instantiated derivations: %v", err))
		} else if flagJSON {
			if err = json.NewEncoder(os.Stdout).Encode(v); err != nil {
				return commandHandlerError(fmt.Sprintf("cannot serialise JSON: %v", err))
			}
			return nil
		} else {
			for _, drv := range v {
				if strings.HasSuffix(drv, ".drv") {
					fmt.Println(drv + "^*")
				} else {
					fmt.Println(drv)
				}
			}
			return nil
		}
	})

	c.MustParse(os.Args[1:], func(err error) {
		var ce commandHandlerError
		if errors.As(err, &ce) {
			log.Fatal(ce.Error())
			return
		}
		log.Fatalf("cannot parse command: %v", err)
	})
}
