package buckets

import (
	"errors"
	"fmt"
	"idun/pkg/storage"
	"sort"
	"time"
)

type Bucket interface {
	AddFile(storage.File) error
	Execute() error
	ToString() string
	GetFilesToDelete() ([]storage.File, error)
}

type UnlimitBucket struct {
	start time.Time
	end   time.Time
}

func (u UnlimitBucket) AddFile(f storage.File) error {
	t, err := f.GetTime()
	if err != nil {
		return err
	}

	if t.Before(u.start) || t.After(u.end) {
		return storage.ErrFileNotInBucketTime
	}

	return nil
}

func (u UnlimitBucket) Execute() error {
	return nil
}
func (b UnlimitBucket) GetFilesToDelete() ([]storage.File, error) {
	return []storage.File{}, nil
}

func (u UnlimitBucket) ToString() string {
	return fmt.Sprintf("%v to %v (Unlimit)", u.start, u.end)
}

type DeleteBucket struct {
	start time.Time
	end   time.Time
	files []storage.File
}

func (d *DeleteBucket) AddFile(f storage.File) error {
	t, err := f.GetTime()
	if err != nil {
		return err
	}

	if t.Before(d.start) || t.After(d.end) {
		return storage.ErrFileNotInBucketTime
	}
	d.files = append(d.files, f)
	return nil
}
func (d DeleteBucket) Execute() error {
	return nil
}

func (d DeleteBucket) ToString() string {
	return fmt.Sprintf("%v to %v (Delete)", d.start, d.end)
}

func (b DeleteBucket) GetFilesToDelete() ([]storage.File, error) {
	return b.files, nil
}

type TimeBucket struct {
	files []storage.File
	start time.Time
	end   time.Time
}

func (b TimeBucket) ToString() string {
	return fmt.Sprintf("%v to %v", b.start, b.end)
}

func (b *TimeBucket) AddFile(f storage.File) error {
	t, err := f.GetTime()
	if err != nil {
		return err
	}

	if t.Before(b.start) || t.After(b.end) {
		return storage.ErrFileNotInBucketTime
	}
	b.files = append(b.files, f)
	return nil
}

func (b TimeBucket) Execute() error {
	if len(b.files) <= 1 {
		return nil
	}

	return nil
}

func (b TimeBucket) GetFilesToDelete() ([]storage.File, error) {
	toDelete := []storage.File{}
	sort.SliceStable(b.files, func(i, j int) bool {
		t1, _ := b.files[i].GetTime()
		t2, _ := b.files[j].GetTime()
		return t1.After(t2)
	})

	for i, f := range b.files {
		if i == 0 {
			continue
		}
		toDelete = append(toDelete, f)
	}

	return toDelete, nil
}

func NewTimeBucket(start time.Time, end time.Time) (TimeBucket, error) {
	if start.After(end) {
		return TimeBucket{}, errors.New("start time must be before end time")
	}

	t := TimeBucket{
		start: start,
		end:   end,
	}

	return t, nil
}
