package filestore_test

import (
	"context"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"strings"
	"testing"

	"git.netflux.io/rob/clipper/filestore"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestFileStoreGetObject(t *testing.T) {
	baseURL, err := url.Parse("/")
	require.NoError(t, err)
	store, err := filestore.NewFileSystemStore("testdata/", baseURL)
	require.NoError(t, err)
	reader, err := store.GetObject(context.Background(), "file.txt")
	require.NoError(t, err)
	defer reader.Close()

	content, err := ioutil.ReadAll(reader)
	require.NoError(t, err)
	assert.Equal(t, "hello world", string(content))
}

func TestFileStoreGetObjectWithRange(t *testing.T) {
	testCases := []struct {
		name        string
		start, end  int64
		wantErr     string
		wantContent string
	}{
		{
			name:        "happy path, complete object",
			start:       0,
			end:         12,
			wantContent: "hello world",
		},
		{
			name:        "happy path, partial object",
			start:       6,
			end:         10,
			wantContent: "worl",
		},
		{
			name:        "empty range",
			start:       1,
			end:         1,
			wantContent: "",
		},
		{
			name:    "bad range",
			start:   -10,
			end:     0,
			wantErr: "error seeking in file: seek testdata/file.txt: invalid argument",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			baseURL, err := url.Parse("/")
			require.NoError(t, err)
			store, err := filestore.NewFileSystemStore("testdata/", baseURL)
			require.NoError(t, err)
			reader, err := store.GetObjectWithRange(context.Background(), "file.txt", tc.start, tc.end)

			if tc.wantErr == "" {
				defer reader.Close()

				require.NoError(t, err)
				content, readErr := ioutil.ReadAll(reader)
				require.NoError(t, readErr)
				assert.Equal(t, tc.wantContent, string(content))
			} else {
				assert.EqualError(t, err, tc.wantErr)
			}
		})
	}
}

func TestFileStoreGetURL(t *testing.T) {
	testCases := []struct {
		name    string
		baseURL string
		key     string
		wantURL string
	}{
		{
			name:    "URL with host and no path, no trailing slash",
			baseURL: "https://foo.example.com",
			key:     "bar",
			wantURL: "https://foo.example.com/bar",
		},
		{
			name:    "URL with host and no path, trailing slash",
			baseURL: "https://foo.example.com/",
			key:     "bar",
			wantURL: "https://foo.example.com/bar",
		},
		{
			name:    "URL with host and path, no trailing slash",
			baseURL: "https://foo.example.com/bar",
			key:     "baz",
			wantURL: "https://foo.example.com/bar/baz",
		},
		{
			name:    "URL with host and path, trailing slash",
			baseURL: "https://foo.example.com/bar/",
			key:     "baz",
			wantURL: "https://foo.example.com/bar/baz",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			baseURL, err := url.Parse(tc.baseURL)
			require.NoError(t, err)
			store, err := filestore.NewFileSystemStore("testdata/", baseURL)
			require.NoError(t, err)

			url, err := store.GetURL(context.Background(), tc.key)
			require.NoError(t, err)
			assert.Equal(t, tc.wantURL, url)
		})
	}
}

func TestFileStorePutObject(t *testing.T) {
	rootPath := t.TempDir()

	testCases := []struct {
		name      string
		key       string
		content   string
		wantCount int64
		wantErr   string
	}{
		{
			name:      "happy path, no created directory",
			key:       "foo.txt",
			content:   "hello world",
			wantCount: 11,
		},
		{
			name:      "happy path, with sub-directories",
			key:       "foo/bar/baz.txt",
			content:   "hello world",
			wantCount: 11,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			baseURL, err := url.Parse("/")
			require.NoError(t, err)
			store, err := filestore.NewFileSystemStore(rootPath, baseURL)
			require.NoError(t, err)

			n, err := store.PutObject(context.Background(), tc.key, strings.NewReader(tc.content), "text/plain")
			require.NoError(t, err)

			content, err := os.ReadFile(path.Join(rootPath, tc.key))
			require.NoError(t, err)

			assert.Equal(t, tc.wantCount, n)
			assert.Equal(t, tc.content, string(content))
		})
	}
}
