package main

import (
	"bytes"
	"os"
	"strings"
	"testing"
)

func TestPrintHelpIncludesBuildPushPullAndUnpack(t *testing.T) {
	var buf bytes.Buffer
	printHelp(&buf)

	got := buf.String()
	for _, want := range []string{
		"conch build [buildah-bud-args...]",
		"conch push [options] <local-image> <remote-image>",
		"conch pull [options] <image-name>",
		"conch unpack [options] <image-name>",
		"Subcommands:",
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("help output missing %q:\n%s", want, got)
		}
	}
}

func TestPrintPushHelpIncludesExample(t *testing.T) {
	var buf bytes.Buffer
	printPushHelp(&buf)

	got := buf.String()
	for _, want := range []string{
		"conch push [options] <local-image> <remote-image>",
		"buildah manifest push --all",
		"--plain-http",
		"--tls-verify bool",
		"buildah login",
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("push help output missing %q:\n%s", want, got)
		}
	}
}

func TestParsePushArgs(t *testing.T) {
	tests := []struct {
		name       string
		args       []string
		wantLocal  string
		wantRemote string
		wantTLS    bool
		wantErr    bool
	}{
		{
			name:       "default",
			args:       []string{"localhost/demo:latest", "hub.oepkgs.net/conch/demo:latest"},
			wantLocal:  "localhost/demo:latest",
			wantRemote: "hub.oepkgs.net/conch/demo:latest",
			wantTLS:    true,
		},
		{
			name:       "plain http",
			args:       []string{"--plain-http", "localhost/demo:latest", "conch.example.com/conch/demo:latest"},
			wantLocal:  "localhost/demo:latest",
			wantRemote: "conch.example.com/conch/demo:latest",
			wantTLS:    false,
		},
		{
			name:       "tls verify false",
			args:       []string{"--tls-verify=false", "localhost/demo:latest", "conch.example.com/conch/demo:latest"},
			wantLocal:  "localhost/demo:latest",
			wantRemote: "conch.example.com/conch/demo:latest",
			wantTLS:    false,
		},
		{
			name:    "missing image",
			args:    []string{"localhost/demo:latest"},
			wantErr: true,
		},
		{
			name:    "unknown option",
			args:    []string{"--user", "demo:demo", "localhost/demo:latest", "remote/demo:latest"},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := parsePushArgs(tt.args)
			if (err != nil) != tt.wantErr {
				t.Fatalf("parsePushArgs() error = %v, wantErr %v", err, tt.wantErr)
			}
			if tt.wantErr {
				return
			}
			if got.localImage != tt.wantLocal || got.remoteImage != tt.wantRemote || got.tlsVerify != tt.wantTLS {
				t.Fatalf("parsePushArgs() = %#v", got)
			}
		})
	}
}

func TestBuildPushCommandArgs(t *testing.T) {
	got := buildPushCommandArgs(pushOptions{
		localImage:  "localhost/demo:latest",
		remoteImage: "conch.example.com/conch/demo:latest",
		tlsVerify:   false,
	})
	want := []string{
		"manifest", "push", "--all", "--tls-verify=false",
		"localhost/demo:latest",
		"docker://conch.example.com/conch/demo:latest",
	}
	if strings.Join(got, "\x00") != strings.Join(want, "\x00") {
		t.Fatalf("buildPushCommandArgs() = %#v, want %#v", got, want)
	}
}

func TestPrintPullHelpIncludesExample(t *testing.T) {
	var buf bytes.Buffer
	printPullHelp(&buf)

	got := buf.String()
	for _, want := range []string{
		"conch pull [options] <image-name>",
		"containerd namespace",
		"config file path",
		"--plain-http",
		"--user string",
		"--kernel-plain-http",
		"--kernel-user string",
		"hub.oepkgs.net/conch/sandbox-snapshot:latest",
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("pull help output missing %q:\n%s", want, got)
		}
	}
}

func TestParseRegistryUser(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		wantUser string
		wantPass string
		wantErr  bool
	}{
		{name: "empty", input: "", wantUser: "", wantPass: ""},
		{name: "valid", input: "example-user:example-password", wantUser: "example-user", wantPass: "example-password"},
		{name: "missing colon", input: "conch", wantErr: true},
		{name: "missing password", input: "conch:", wantErr: true},
		{name: "missing username", input: ":secret", wantErr: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			user, pass, err := parseRegistryUser(tt.input)
			if (err != nil) != tt.wantErr {
				t.Fatalf("parseRegistryUser() error = %v, wantErr %v", err, tt.wantErr)
			}
			if tt.wantErr {
				return
			}
			if user != tt.wantUser || pass != tt.wantPass {
				t.Fatalf("parseRegistryUser() = (%q, %q), want (%q, %q)", user, pass, tt.wantUser, tt.wantPass)
			}
		})
	}
}

func TestPrintUnpackHelpIncludesExample(t *testing.T) {
	var buf bytes.Buffer
	printUnpackHelp(&buf)

	got := buf.String()
	for _, want := range []string{
		"conch unpack [options] <image-name>",
		"containerd namespace",
		"containerd socket address",
		"config file path",
		"conch unpack -n default hub.oepkgs.net/conch/conch-index:v0.1",
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("unpack help output missing %q:\n%s", want, got)
		}
	}
}

func TestResolveContainerdRuntimeUsesConfigAndOverrides(t *testing.T) {
	t.Setenv("CONTAINERD_ADDRESS", "")

	dir := t.TempDir()
	cfgPath := dir + "/config.yaml"
	if err := os.WriteFile(cfgPath, []byte(`
containerd:
  socket: /run/containerd/custom.sock
  default_namespace: team-a
`), 0o644); err != nil {
		t.Fatalf("write config: %v", err)
	}

	cfg, err := loadConchConfig(cfgPath)
	if err != nil {
		t.Fatalf("load config: %v", err)
	}
	addr, ns := resolveContainerdRuntime(cfg, "", "")
	if addr != "/run/containerd/custom.sock" {
		t.Fatalf("addr = %q, want %q", addr, "/run/containerd/custom.sock")
	}
	if ns != "team-a" {
		t.Fatalf("namespace = %q, want %q", ns, "team-a")
	}

	addr, ns = resolveContainerdRuntime(cfg, "/run/containerd/override.sock", "override-ns")
	if addr != "/run/containerd/override.sock" {
		t.Fatalf("override addr = %q, want %q", addr, "/run/containerd/override.sock")
	}
	if ns != "override-ns" {
		t.Fatalf("override namespace = %q, want %q", ns, "override-ns")
	}
}

func TestPrintBuildHelpIncludesUsageAndEnv(t *testing.T) {
	var buf bytes.Buffer
	printBuildHelp(&buf)

	got := buf.String()
	for _, want := range []string{
		"conch build [buildah-bud-args...]",
		"Forward arguments to `buildah bud`",
		"--config string",
		"`KERNEL`, `INDEX`, and `SNAP`",
		"CONCH_BUILDAH_BIN",
	} {
		if !strings.Contains(got, want) {
			t.Fatalf("build help output missing %q:\n%s", want, got)
		}
	}
}

func TestParseBuildConfigArg(t *testing.T) {
	tests := []struct {
		name       string
		args       []string
		wantConfig string
		wantArgs   []string
		wantErr    bool
	}{
		{
			name:       "long separate",
			args:       []string{"--config", "/tmp/config.yaml", "-f", "Dockerfile", "-t", "demo:latest", "."},
			wantConfig: "/tmp/config.yaml",
			wantArgs:   []string{"-f", "Dockerfile", "-t", "demo:latest", "."},
		},
		{
			name:       "long equals",
			args:       []string{"--config=/tmp/config.yaml", "-f", "Dockerfile", "."},
			wantConfig: "/tmp/config.yaml",
			wantArgs:   []string{"-f", "Dockerfile", "."},
		},
		{
			name:       "short equals",
			args:       []string{"-config=/tmp/config.yaml", "."},
			wantConfig: "/tmp/config.yaml",
			wantArgs:   []string{"."},
		},
		{
			name:    "missing value",
			args:    []string{"--config"},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotConfig, gotArgs, err := parseBuildConfigArg(tt.args)
			if (err != nil) != tt.wantErr {
				t.Fatalf("parseBuildConfigArg() error = %v, wantErr %v", err, tt.wantErr)
			}
			if tt.wantErr {
				return
			}
			if gotConfig != tt.wantConfig {
				t.Fatalf("config: got %q want %q", gotConfig, tt.wantConfig)
			}
			if strings.Join(gotArgs, "\x00") != strings.Join(tt.wantArgs, "\x00") {
				t.Fatalf("args: got %#v want %#v", gotArgs, tt.wantArgs)
			}
		})
	}
}