package buckets

import (
	"github.com/stretchr/testify/assert"
	_ "github.com/stretchr/testify/assert"
	"idun/pkg/storage"
	"testing"
	"time"
)

type TestFile struct {
	name string
	time time.Time
}

func (f TestFile) GetName() (string, error) {
	return f.name, nil
}

func (f TestFile) GetTime() (time.Time, error) {
	return f.time, nil
}

func TestUnlimitBucket(t *testing.T) {
	tt := []struct {
		Name  string
		Files []TestFile
	}{
		{
			Name:  "No Files, No Deletion",
			Files: []TestFile{},
		},
		{
			Name: "One File, no deletion",
			Files: []TestFile{
				{
					time: time.Date(2023, 9, 27, 15, 0, 0, 0, time.UTC),
				},
			},
		},
	}

	for _, ts := range tt {
		t.Run(ts.Name, func(t *testing.T) {
			bucket := UnlimitBucket{}
			bucket.start = time.Time{}
			bucket.end = time.Now()

			for _, f := range ts.Files {
				err := bucket.AddFile(f)
				assert.Nil(t, err, "should add file without error")
			}
		})
	}
}

func TestCreateTimeBucket(t *testing.T) {
	t1 := time.Date(2023, 9, 27, 15, 0, 0, 0, time.UTC)
	t2 := t1.Add(1 * time.Hour)

	_, err := NewTimeBucket(t1, t2)
	assert.Nil(t, err, "should create time bucket without error")

	_, err = NewTimeBucket(t2, t1)
	assert.Equal(t, "start time must be before end time", err.Error(), "should return error if start is behind end time")
}

func TestAddFileToTimeBucket(t *testing.T) {
	t1 := time.Date(2023, 9, 27, 15, 0, 0, 0, time.UTC)
	t2 := t1.Add(1 * time.Hour)

	b, err := NewTimeBucket(t1, t2)
	assert.Nil(t, err, "should be able to create bucket")
	tt := []struct {
		Name   string
		File   storage.File
		ExpErr error
	}{
		{
			Name:   "File inside the Bucket Time",
			File:   TestFile{time: t1.Add(1 * time.Hour)},
			ExpErr: nil,
		},
		{
			Name:   "File after the Bucket Time",
			File:   TestFile{time: t1.Add(12 * time.Hour)},
			ExpErr: storage.ErrFileNotInBucketTime,
		},
		{
			Name:   "File after the Bucket Time",
			File:   TestFile{time: t1.Add(-12 * time.Hour)},
			ExpErr: storage.ErrFileNotInBucketTime,
		},
	}

	for _, ts := range tt {
		t.Run(ts.Name, func(t *testing.T) {
			err := b.AddFile(ts.File)
			assert.Equal(t, ts.ExpErr, err, "error should match")
		})
	}
}

func TestGetFilesToDelete(t *testing.T) {
	t1 := time.Date(2023, 9, 27, 15, 0, 0, 0, time.UTC)
	t2 := t1.Add(1 * time.Hour)

	tt := []struct {
		Name              string
		Files             []TestFile
		ToDeleteFileNames []string
	}{
		{
			Name:              "No Files, No Deletion",
			Files:             []TestFile{},
			ToDeleteFileNames: []string{},
		},
		{
			Name: "One File, no deletion",
			Files: []TestFile{
				{
					time: t1.Add(5 * time.Minute),
				},
			},
			ToDeleteFileNames: []string{},
		},
		{
			Name: "Two File, one deletion",
			Files: []TestFile{
				{
					name: "abc",
					time: t1.Add(5 * time.Minute),
				},
				{
					name: "des",
					time: t1.Add(15 * time.Minute),
				},
			},
			ToDeleteFileNames: []string{"abc"},
		},
		{
			Name: "Three File, two deletion",
			Files: []TestFile{
				{
					name: "abc",
					time: t1.Add(5 * time.Minute),
				},
				{
					name: "des",
					time: t1.Add(15 * time.Minute),
				},
				{
					name: "fff",
					time: t1.Add(14 * time.Minute),
				},
			},
			ToDeleteFileNames: []string{"fff", "abc"},
		},
	}

	for _, ts := range tt {
		t.Run(ts.Name, func(t *testing.T) {
			bucket, err := NewTimeBucket(t1, t2)
			assert.Nil(t, err, "should be able to create bucket")

			for _, f := range ts.Files {
				err := bucket.AddFile(f)
				assert.Nil(t, err, "should add file without error")
			}

			toDelte, err := bucket.GetFilesToDelete()
			assert.Nil(t, err, "should not return error")

			filenames := []string{}
			for _, f := range toDelte {
				name, err := f.GetName()
				assert.Nil(t, err, "should not return error")
				filenames = append(filenames, name)
			}

			assert.Equal(t, ts.ToDeleteFileNames, filenames, "should return right list of files to delete")
		})
	}
}