package buckets

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

type Config struct {
	Unlimit int `yaml:"unlimit"`
	Hourly  int `yaml:"hourly"`
	Daily   int `yaml:"daily"`
	Weekly  int `yaml:"weekly"`
	Monthly int `yaml:"monthly"`
}

func InsertFilesInBuckets(buckets *[]Bucket, files []storage.File) error {
	for _, f := range files {
		for _, b := range *buckets {
			err := b.AddFile(f)
			if err != nil {
				if errors.Is(err, storage.ErrFileNotInBucketTime) {
					continue
				}
				return err
			}
			break
		}
	}

	return nil
}

func GenerateBuckets(fromTime time.Time, config Config, location *time.Location) ([]Bucket, error) {
	end := fromTime
	plan := []Bucket{}
	//Unlimit Bucket for the current hour
	untilStart := time.Date(fromTime.Year(), fromTime.Month(), fromTime.Day(), fromTime.Hour(), 0, 0, 0, location)
	unlimitBucket := UnlimitBucket{
		start: untilStart,
		end:   fromTime,
	}
	end = untilStart
	plan = append(plan, &unlimitBucket)

	//Todo: Make just one unlimit bucket for the complete time
	//Unlimit Buckets
	for i := 0; i < config.Unlimit; i++ {
		unlimitBucket := UnlimitBucket{
			start: end.Add(-1 * time.Hour),
			end:   end.Add(-1),
		}
		end = end.Add(-1 * time.Hour)
		plan = append(plan, &unlimitBucket)
	}

	//Hour Buckets
	for i := 0; i < config.Hourly; i++ {
		timebucket := TimeBucket{
			start: end.Add(-1 * time.Hour),
			end:   end.Add(-1),
		}
		end = end.Add(-1 * time.Hour)
		plan = append(plan, &timebucket)
	}

	//Hour Backups until middnight, if dayly, weekly or monthly is active
	if config.Daily > 0 || config.Weekly > 0 || config.Monthly > 0 {
		for {
			timebucket := TimeBucket{
				start: end.Add(-1 * time.Hour),
				end:   end.Add(-1),
			}
			end = end.Add(-1 * time.Hour)
			plan = append(plan, &timebucket)
			if end.Hour() == 0 {
				break
			}
		}
	}

	//Daily
	for i := 0; i < config.Daily; i++ {
		timebucket := TimeBucket{
			start: end.Add(-24 * time.Hour),
			end:   end.Add(-1),
		}
		end = end.Add(-24 * time.Hour)
		plan = append(plan, &timebucket)
	}

	//Daily Backups until (inc) monday, if weekly or monthly backups ar active
	if config.Weekly > 0 || config.Monthly > 0 {
		for {
			timebucket := TimeBucket{
				start: end.Add(-24 * time.Hour),
				end:   end.Add(-1),
			}
			end = end.Add(-24 * time.Hour)
			plan = append(plan, &timebucket)
			if end.Weekday() == time.Monday {
				break
			}
		}
	}

	//Weekly
	for i := 0; i < config.Weekly; i++ {
		timebucket := TimeBucket{
			start: end.Add(-24 * 7 * time.Hour),
			end:   end.Add(-1),
		}
		end = end.Add(-24 * 7 * time.Hour)
		plan = append(plan, &timebucket)
	}

	//Daily Backups until (inc) monday, if weekly or monthly backups ar active
	if config.Monthly > 0 {
		for {
			lastrun := false
			start := end.Add(-24 * 7 * time.Hour)
			if start.Month() != end.Month() {
				start = time.Date(end.Year(), end.Month(), 1, 0, 0, 0, 0, location)
				lastrun = true
			}
			timebucket := TimeBucket{
				start: start,
				end:   end.Add(-1),
			}
			end = start
			plan = append(plan, &timebucket)
			if lastrun {
				break
			}
		}
	}

	//Monthly
	for i := 0; i < config.Monthly; i++ {
		timebucket := TimeBucket{
			start: end.AddDate(0, -1, 0),
			end:   end.Add(-1),
		}
		end = end.AddDate(0, -1, 0)
		plan = append(plan, &timebucket)
	}

	//Add Delete all Backups older than the last plan
	deleteBucket := DeleteBucket{
		start: time.Time{},
		end:   end.Add(-1),
	}

	plan = append(plan, &deleteBucket)

	return plan, nil
}
