//go:build !windows

package runnerv2service

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/proto"

	runnerv2 "github.com/runmedev/runme/v3/api/gen/proto/go/runme/runner/v2"
	"github.com/runmedev/runme/v3/internal/testutils"
)

// todo(sebastian): port test cases from v1
func TestRunnerService_ResolveProgram(t *testing.T) {
	t.Parallel()

	_, _, lis, stop := startRunnerServiceServer(t)
	t.Cleanup(stop)

	_, client := testutils.NewGRPCClientWithT(t, lis, runnerv2.NewRunnerServiceClient)

	testCases := []struct {
		name    string
		request *runnerv2.ResolveProgramRequest
	}{
		{
			name: "WithScript",
			request: &runnerv2.ResolveProgramRequest{
				Env:        []string{"TEST_RESOLVED=value"},
				LanguageId: "bash",
				Source: &runnerv2.ResolveProgramRequest_Script{
					Script: "export TEST_RESOLVED=default\nexport TEST_UNRESOLVED",
				},
			},
		},
		{
			name: "WithCommands",
			request: &runnerv2.ResolveProgramRequest{
				Env:        []string{"TEST_RESOLVED=value"},
				LanguageId: "bash",
				Source: &runnerv2.ResolveProgramRequest_Commands{
					Commands: &runnerv2.ResolveProgramCommandList{
						Lines: []string{"export TEST_RESOLVED=default", "export TEST_UNRESOLVED"},
					},
				},
			},
		},
		{
			name: "WithAdditionalEnv",
			request: &runnerv2.ResolveProgramRequest{
				Env:        []string{"TEST_RESOLVED=value", "TEST_EXTRA=value"},
				LanguageId: "bash",
				Source: &runnerv2.ResolveProgramRequest_Commands{
					Commands: &runnerv2.ResolveProgramCommandList{
						Lines: []string{"export TEST_RESOLVED=default", "export TEST_UNRESOLVED"},
					},
				},
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			resp, err := client.ResolveProgram(context.Background(), tc.request)
			require.NoError(t, err)
			require.Len(t, resp.Vars, 2)
			require.Equal(t, resp.Commands, (*runnerv2.ResolveProgramCommandList)(nil))
			require.Greater(t, len(resp.Script), 0)
			require.EqualValues(
				t,
				&runnerv2.ResolveProgramResponse_VarResult{
					Name:          "TEST_RESOLVED",
					OriginalValue: "default",
					ResolvedValue: "value",
					Status:        runnerv2.ResolveProgramResponse_STATUS_RESOLVED,
				},
				resp.Vars[0],
			)
			require.EqualValues(
				t,
				&runnerv2.ResolveProgramResponse_VarResult{
					Name:   "TEST_UNRESOLVED",
					Status: runnerv2.ResolveProgramResponse_STATUS_UNSPECIFIED,
				},
				resp.Vars[1],
			)
		})
	}
}

func TestRunnerService_ResolveProgram_CommandsWithNewLines(t *testing.T) {
	// TODO(adamb): enable it when we find a solution for merging commands and splitting them back.
	t.Skip("the problem is unknown and needs to be fixed")

	_, _, lis, stop := startRunnerServiceServer(t)
	t.Cleanup(stop)
	_, client := testutils.NewGRPCClientWithT(t, lis, runnerv2.NewRunnerServiceClient)

	request := &runnerv2.ResolveProgramRequest{
		Env:        []string{"FILE_NAME=my-file.txt"},
		LanguageId: "bash",
		Source: &runnerv2.ResolveProgramRequest_Commands{
			Commands: &runnerv2.ResolveProgramCommandList{
				Lines: []string{
					"export FILE_NAME=default.txt",
					"cat >\"$FILE_NAME\" <<EOF",
					"Some content with\nnew line",
					"EOF",
				},
			},
		},
	}

	resp, err := client.ResolveProgram(context.Background(), request)
	require.NoError(t, err)
	require.Len(t, resp.Vars, 1)
	require.Equal(t, resp.Commands, (*runnerv2.ResolveProgramCommandList)(nil))
	require.Greater(t, len(resp.Script), 0)
	require.True(
		t,
		proto.Equal(
			&runnerv2.ResolveProgramResponse_VarResult{
				Name:          "FILE_NAME",
				Status:        runnerv2.ResolveProgramResponse_STATUS_RESOLVED,
				OriginalValue: "default.txt",
				ResolvedValue: "my-file.txt",
			},
			resp.Vars[0],
		),
	)
	require.EqualValues(
		t,
		[]string{
			"#",
			"# FILE_NAME set in managed env store",
			"# \"export FILE_NAME=default.txt\"",
			"",
			"cat >\"$FILE_NAME\" <<EOF",
			"Some content with\nnew line",
			"EOF",
		},
		resp.Commands.Lines,
	)
}

func TestRunnerService_ResolveProgram_OnlyShellLanguages(t *testing.T) {
	t.Parallel()

	_, _, lis, stop := startRunnerServiceServer(t)
	t.Cleanup(stop)

	_, client := testutils.NewGRPCClientWithT(t, lis, runnerv2.NewRunnerServiceClient)

	t.Run("Javascript passed as script", func(t *testing.T) {
		t.Parallel()
		script := "console.log('test');"
		request := &runnerv2.ResolveProgramRequest{
			Env:        []string{"TEST_RESOLVED=value"},
			LanguageId: "javascript",
			Source: &runnerv2.ResolveProgramRequest_Script{
				Script: script,
			},
		}

		resp, err := client.ResolveProgram(context.Background(), request)
		require.NoError(t, err)
		require.Len(t, resp.Vars, 0)
		require.Equal(t, script, resp.Script)
	})

	t.Run("Python passed as commands", func(t *testing.T) {
		t.Parallel()
		script := "print('test')"
		request := &runnerv2.ResolveProgramRequest{
			LanguageId: "py",
			Source: &runnerv2.ResolveProgramRequest_Commands{
				Commands: &runnerv2.ResolveProgramCommandList{Lines: []string{script}},
			},
		}

		resp, err := client.ResolveProgram(context.Background(), request)
		require.NoError(t, err)
		require.Len(t, resp.Vars, 0)
		require.Equal(t, script, resp.Script)
	})
}
