package nix_test

import (
	"encoding/json"
	"os"
	"os/exec"
	"reflect"
	"slices"
	"syscall"
	"testing"

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

func init() {
	stubCommandInit = append(stubCommandInit, func(c command.Command) {
		c.NewCommand(nix.CommandDerivation, "emit samples for various `nix derivation` cases", func(args []string) error {
			if len(args) == 0 {
				return syscall.ENOSYS
			}

			switch args[0] {
			default:
				return syscall.EINVAL

			case nix.CommandDerivationShow:
				if len(args) != 2 {
					return syscall.ENOSYS
				}

				switch args[1] {
				default:
					return syscall.EINVAL

				case nix.FlagStdin:
					var testName string
					if want, err := nix.ReadStdin(os.Stdin); err != nil {
						return err
					} else {
						for n, w := range instWant {
							if slices.Equal(w, want) {
								testName = n
								break
							}
						}

						if testName == "" {
							return syscall.ENOSYS
						}
					}
					if sample, ok := drvShow[testName]; !ok {
						return syscall.ENOSYS
					} else {
						_, err := os.Stdout.Write(sample)
						return err
					}
				}
			}
		})
	})
}

func TestDerivationShow(t *testing.T) {
	stubNixCommand(t)

	testCases := []struct {
		name string
	}{
		{"getchoo atlas"},
		{"getchoo glados"},
		{"pluiedev pappardelle"},
	}
	for _, tc := range testCases {
		got, err := nix.DerivationShow(
			newStubContext(t.Context(), nil, os.Stdout, os.Stderr),
			slices.Values(instWant[tc.name]),
		)
		if err != nil {
			t.Fatalf("DerivationShow: error = %v", err)
		}

		var want nix.DerivationMap
		if err = json.Unmarshal(drvShow[tc.name], &want); err != nil {
			t.Fatalf("cannot unmarshal: %v", err)
		}

		if !reflect.DeepEqual(got, want) {
			t.Fatalf("DerivationShow: %#v, want %#v", got, want)
		}
	}
}

func TestDerivationShowAlreadySet(t *testing.T) {
	stubNixCommand(t)
	if _, err := nix.DerivationShow(
		newStubContextCommand(func(cmd *exec.Cmd) { cmd.Stdout = os.Stdout }, t.Context(), nil, os.Stdout, os.Stderr),
		nil,
	); err == nil {
		t.Errorf("DerivationShow unexpectedly succeeded")
	}
}

func BenchmarkShowUnmarshal(b *testing.B) {
	var v nix.DerivationMap
	data := drvShow["pluiedev pappardelle"]
	for b.Loop() {
		if err := json.Unmarshal(data, &v); err != nil {
			b.Fatalf("Unmarshal: error = %v", err)
		}
	}
}
