package segments

import (
	"testing"

	"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
	"github.com/jandedobbeleer/oh-my-posh/src/segments/options"

	"github.com/stretchr/testify/assert"
	mock_ "github.com/stretchr/testify/mock"
)

func TestDockerContext(t *testing.T) {
	type envVar struct {
		name  string
		value string
	}
	cases := []struct {
		EnvVar          envVar
		Case            string
		Expected        string
		ConfigFile      string
		ExpectedEnabled bool
		HasFiles        bool
	}{
		{Case: "DOCKER_MACHINE_NAME", Expected: "alpine", ExpectedEnabled: true, EnvVar: envVar{name: "DOCKER_MACHINE_NAME", value: "alpine"}},
		{Case: "DOCKER_HOST", Expected: "alpine 2", ExpectedEnabled: true, EnvVar: envVar{name: "DOCKER_HOST", value: "alpine 2"}},
		{Case: "DOCKER_CONTEXT", Expected: "alpine 3", ExpectedEnabled: true, EnvVar: envVar{name: "DOCKER_HOST", value: "alpine 3"}},
		{Case: "DOCKER_CONTEXT - default", ExpectedEnabled: false, EnvVar: envVar{name: "DOCKER_HOST", value: "default"}},
		{Case: "no docker context active", ExpectedEnabled: false},
		{Case: "config file", Expected: "alpine", ExpectedEnabled: true, HasFiles: true, ConfigFile: `{"currentContext": "alpine"}`},
		{Case: "config file - default", ExpectedEnabled: false, HasFiles: true, ConfigFile: `{"currentContext": "default"}`},
		{Case: "config file - broken", ExpectedEnabled: false, HasFiles: true, ConfigFile: `{`},
	}

	for _, tc := range cases {
		docker := &Docker{}
		env := new(mock.Environment)
		docker.Init(options.Map{}, env)

		for _, v := range docker.envVars() {
			var value string
			if v == tc.EnvVar.name {
				value = tc.EnvVar.value
			}
			env.On("Getenv", v).Return(value)
		}

		env.On("Home").Return("")
		env.On("Getenv", "DOCKER_CONFIG").Return("")
		for _, f := range docker.configFiles() {
			env.On("HasFiles", f).Return(tc.HasFiles)
			env.On("FileContent", f).Return(tc.ConfigFile)
		}

		assert.Equal(t, tc.ExpectedEnabled, docker.Enabled(), tc.Case)
		if tc.ExpectedEnabled {
			assert.Equal(t, tc.Expected, renderTemplate(env, "{{ .Context }}", docker), tc.Case)
		}
	}
}

func TestDockerFiles(t *testing.T) {
	cases := []struct {
		Case            string
		ExpectedEnabled bool
		HasFiles        bool
	}{
		{Case: "compose.yml", ExpectedEnabled: true, HasFiles: true},
		{Case: "compose.yaml", ExpectedEnabled: true, HasFiles: true},
		{Case: "docker-compose.yml", ExpectedEnabled: true, HasFiles: true},
		{Case: "docker-compose.yaml", ExpectedEnabled: true, HasFiles: true},
		{Case: "Dockerfile", ExpectedEnabled: true, HasFiles: true},
		{Case: "docker-compose.yml - not found", ExpectedEnabled: false, HasFiles: false},
	}

	for _, tc := range cases {
		docker := &Docker{}
		env := new(mock.Environment)
		props := options.Map{
			DisplayMode:  DisplayModeFiles,
			FetchContext: false,
		}

		docker.Init(props, env)

		env.On("HasFiles", tc.Case).Return(true)
		env.On("HasFiles", mock_.Anything).Return(false)

		assert.Equal(t, tc.ExpectedEnabled, docker.Enabled(), tc.Case)
	}
}

func TestDockerEnvironment(t *testing.T) {
	cases := []struct {
		Case            string
		ExpectedContext string
		Filter          string
		DockerCommand   string
		CommandOutput   string
		ExpectedEnabled bool
		HasCommand      bool
	}{
		{
			Case:            "Docker command not found",
			ExpectedEnabled: false,
			HasCommand:      false,
		},
		{
			Case:            "No running containers",
			ExpectedEnabled: false,
			HasCommand:      true,
			CommandOutput:   "",
		},
		{
			Case:            "One running container",
			ExpectedEnabled: true,
			HasCommand:      true,
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1",
			ExpectedContext: "1 running (c1)",
		},
		{
			Case:            "Multiple running containers",
			ExpectedEnabled: true,
			HasCommand:      true,
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1\nc2\timage2\tcmd2\tcreated2\tstatus2\tports2\tnames2",
			ExpectedContext: "2 running (c1)",
		},
		{
			Case:            "Multiple running containers with windows line endings",
			ExpectedEnabled: true,
			HasCommand:      true,
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1\r\nc2\timage2\tcmd2\tcreated2\tstatus2\tports2\tnames2\r\n",
			ExpectedContext: "2 running (c1)",
		},
		{
			Case:            "Output with empty line",
			ExpectedEnabled: false,
			HasCommand:      true,
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1\n\nc2\timage2\tcmd2\tcreated2\tstatus2\tports2\tnames2",
		},
		{
			Case:            "Filter applied",
			ExpectedEnabled: true,
			HasCommand:      true,
			Filter:          "name=service1",
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1",
			ExpectedContext: "1 running (c1)",
		},
		{
			Case:            "Custom docker command",
			ExpectedEnabled: true,
			HasCommand:      true,
			DockerCommand:   "podman",
			CommandOutput:   "c1\timage1\tcmd1\tcreated1\tstatus1\tports1\tnames1",
			ExpectedContext: "1 running (c1)",
		},
	}

	for _, tc := range cases {
		t.Run(tc.Case, func(t *testing.T) {
			env := new(mock.Environment)
			env.On("Getenv", mock_.Anything).Return("")
			env.On("Home").Return("")
			env.On("FileContent", mock_.Anything).Return("")

			dockerCommand := "docker"
			if len(tc.DockerCommand) > 0 {
				dockerCommand = tc.DockerCommand
			}

			env.On("HasCommand", dockerCommand).Return(tc.HasCommand)

			format := `{{.ID}}\t{{.Image}}\t{{.Command}}\t{{.CreatedAt}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}`
			args := []string{"ps", "--format", format}
			if len(tc.Filter) > 0 {
				args = append(args, "--filter", tc.Filter)
			}

			env.On("RunCommand", dockerCommand, args).Return(tc.CommandOutput, nil)

			props := options.Map{
				DisplayMode: DisplayModeEnvironment,
				Filter:      tc.Filter,
			}
			if len(tc.DockerCommand) > 0 {
				props[DockerCommand] = tc.DockerCommand
			}

			docker := &Docker{}
			docker.Init(props, env)

			assert.Equal(t, tc.ExpectedEnabled, docker.Enabled())
			if tc.ExpectedEnabled {
				template := `{{ len .Containers }} running ({{ (index .Containers 0).ID }})`
				assert.Equal(t, tc.ExpectedContext, renderTemplate(env, template, docker))
			}
		})
	}
}
