package cli

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/kopia/kopia/repo/blob/sftp"
	"github.com/kopia/kopia/repo/blob/sharded"
)

func TestSFTPOptions(t *testing.T) {
	td := t.TempDir()

	myKeyFile := filepath.Join(td, "my-key")
	myKnownHostsFile := filepath.Join(td, "my-known-hosts")

	require.NoError(t, os.WriteFile(myKeyFile, []byte("fake-key-data"), 0o600))
	require.NoError(t, os.WriteFile(myKnownHostsFile, []byte("fake-known-hosts-data"), 0o600))

	cases := []struct {
		input   storageSFTPFlags
		want    *sftp.Options
		wantErr string
	}{
		// 0
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					KnownHostsFile: "my-known-hosts",
					Keyfile:        "my-key",
				},
			},
			want: &sftp.Options{
				Host:           "some-host",
				Port:           222,
				Username:       "user",
				KnownHostsFile: mustFileAbs(t, "my-known-hosts"),
				Keyfile:        mustFileAbs(t, "my-key"),
			},
		},
		// 1
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					Keyfile:        "no-such-file",
					KnownHostsFile: myKnownHostsFile,
				},
				embedCredentials: true,
			},
			wantErr: "unable to read key file",
		},
		// 2
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					Keyfile:        myKeyFile,
					KnownHostsFile: "no-such-file",
				},
				embedCredentials: true,
			},
			wantErr: "unable to read known hosts file",
		},
		// 3
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					KnownHostsFile: "my-known-hosts",
				},
			},
			wantErr: "must provide either --sftp-password, --keyfile or --key-data",
		},
		// 4
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:     "some-host",
					Port:     222,
					Username: "user",
					Keyfile:  "my-key",
				},
			},
			wantErr: "must provide either --known-hosts or --known-hosts-data",
		},
		// 5
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					KnownHostsFile: myKnownHostsFile,
					Keyfile:        myKeyFile,
				},
				embedCredentials: true,
			},
			want: &sftp.Options{
				Host:           "some-host",
				Port:           222,
				Username:       "user",
				KeyData:        "fake-key-data",
				KnownHostsData: "fake-known-hosts-data",
			},
		},
		// 6
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					KnownHostsFile: "my-known-hosts",
					Keyfile:        "my-key",
				},
				connectFlat: true,
			},
			want: &sftp.Options{
				Host:           "some-host",
				Port:           222,
				Username:       "user",
				KnownHostsFile: mustFileAbs(t, "my-known-hosts"),
				Keyfile:        mustFileAbs(t, "my-key"),
				Options: sharded.Options{
					DirectoryShards: []int{},
				},
			},
		},
		// 7
		{
			input: storageSFTPFlags{
				options: sftp.Options{
					Host:           "some-host",
					Port:           222,
					Username:       "user",
					Password:       "my-password",
					KnownHostsFile: "my-known-hosts",
				},
				connectFlat: true,
			},
			want: &sftp.Options{
				Host:           "some-host",
				Port:           222,
				Username:       "user",
				KnownHostsFile: mustFileAbs(t, "my-known-hosts"),
				Password:       "my-password",
				Options: sharded.Options{
					DirectoryShards: []int{},
				},
			},
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("case-%v", i), func(t *testing.T) {
			got, err := tc.input.getOptions(2)
			if tc.wantErr == "" {
				require.NoError(t, err)
				require.Equal(t, tc.want, got)
			} else {
				require.Error(t, err)
				require.Contains(t, err.Error(), tc.wantErr)
			}
		})
	}
}

func mustFileAbs(t *testing.T, fname string) string {
	t.Helper()

	result, err := filepath.Abs(fname)
	require.NoError(t, err)

	return result
}
