package nix_test

import (
	"errors"
	"io"
	"os"
	"slices"
	"strings"
	"syscall"
	"testing"

	"gensokyo.uk/nix"
	"hakurei.app/command"
)

func init() {
	stubCommandInit = append(stubCommandInit, func(c command.Command) {
		var (
			flagBuildKeepGoing bool
			flagBuildNoLink    bool
			flagBuildStdin     bool
			flagBuildQuiet     bool
			flagBuildPBL       bool
			flagBuildVerbose   bool
		)
		c.NewCommand(nix.CommandBuild, "emit samples for various `nix build` cases", func(args []string) error {
			switch {
			default:
				return os.ErrInvalid

			case len(args) == 7 && slices.Equal(args[1:], []string{
				nix.FlagOption, nix.OptionEvalCache, nix.ValueFalse,
				nix.FlagDryRun, nix.FlagPrintBuildLogs, nix.FlagDebug,
			}): // InstantiatedEvaluator
				return stubInstantiatedEvaluator(args)

			case flagBuildKeepGoing, flagBuildNoLink, flagBuildStdin: // Build
				switch {
				default:
					return syscall.ENOTRECOVERABLE

				case flagBuildQuiet && !flagBuildPBL && !flagBuildVerbose:
					return checkStdin(os.Stdin, "quiet")

				case !flagBuildQuiet && flagBuildPBL && !flagBuildVerbose:
					return checkStdin(os.Stdin, "logs")

				case flagBuildQuiet && !flagBuildPBL && flagBuildVerbose:
					return checkStdin(os.Stdin, "quiet", "verbose")

				case !flagBuildQuiet && flagBuildPBL && flagBuildVerbose:
					return checkStdin(os.Stdin, "logs", "verbose")
				}
			}
		}).
			Flag(&flagBuildKeepGoing, trimFlagName(nix.FlagKeepGoing), command.BoolFlag(false), nix.FlagKeepGoing).
			Flag(&flagBuildNoLink, trimFlagName(nix.FlagNoLink), command.BoolFlag(false), nix.FlagNoLink).
			Flag(&flagBuildStdin, trimFlagName(nix.FlagStdin), command.BoolFlag(false), nix.FlagStdin).
			Flag(&flagBuildQuiet, trimFlagName(nix.FlagQuiet), command.BoolFlag(false), nix.FlagQuiet).
			Flag(&flagBuildPBL, trimFlagName(nix.FlagPrintBuildLogs), command.BoolFlag(false), nix.FlagPrintBuildLogs).
			Flag(&flagBuildVerbose, trimFlagName(nix.FlagVerbose), command.BoolFlag(false), nix.FlagVerbose)
	})
}

func TestBuild(t *testing.T) {
	stubNixCommand(t)
	check := func(stdout, stderr io.Writer, v ...string) {
		t.Run(strings.Join(v, " "), func(t *testing.T) {
			if err := nix.Build(
				newStubContext(t.Context(), nil, stdout, stderr),
				slices.Values(v),
			); err != nil {
				t.Errorf("Build: error = %v", err)
			}
		})
	}

	check(nil, nil, "quiet")
	check(os.Stdout, nil, "logs")
	check(nil, os.Stderr, "quiet", "verbose")
	check(os.Stdout, os.Stderr, "logs", "verbose")
}

func TestBuildBadCommand(t *testing.T) {
	wantErr := os.ErrNotExist
	breakNixCommand(t)
	if err := nix.Build(
		nix.New(t.Context(), nil, nil, nil),
		nil,
	); !errors.Is(err, wantErr) {
		t.Errorf("Build: error = %v, want %v", err, wantErr)
	}
}
