package cli

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type FlagStringArray []string

func (array *FlagStringArray) String() string {
	return strings.Join(*array, ", ")
}

func (array *FlagStringArray) Set(s string) error {
	*array = append(*array, s)
	return nil
}

type FileList FlagStringArray

func (fileList FileList) Strings() []string {
	return ([]string)(fileList)
}

func (fileList *FileList) String() string {
	return (*FlagStringArray)(fileList).String()
}

func (fileList *FileList) Set(s string) error {
	if _, err := os.Stat(s); os.IsNotExist(err) {
		return fmt.Errorf("file does not exist: %s", s)
	}
	return (*FlagStringArray)(fileList).Set(s)
}

type Period struct {
	Start time.Time
	Stop  time.Time
}

func (period Period) Empty() bool {
	return period.Stop.Sub(period.Start) == 0
}

func (period *Period) String() string {
	return fmt.Sprintf("%s..%s", period.Start, period.Stop)
}

func (period *Period) Set(s string) error {
	ss := strings.Split(s, "..")
	if err := period.setStartStop(ss[0]); err != nil {
		return err
	}

	if len(ss) != 2 {
	} else if err := period.setStop(ss[1]); err != nil {
		return err
	}

	return nil
}

func (period *Period) setStartStop(s string) error {
	stop, err := period.setT(time.Now(), s, &period.Start)
	period.Stop = stop
	return err
}

func (period *Period) setStop(s string) error {
	_, err := period.setT(time.Now(), s, &period.Stop)
	return err
}

func (*Period) setT(now time.Time, s string, t *time.Time) (time.Time, error) {
	if s == "" {
		*t = time.Unix(0, 0)
		return time.Now().AddDate(100, 0, 0), nil
	}
	if submatches := regexp.MustCompile(`[+-]?(?P<years>[0-9]+y)?(?P<months>[0-9]+mo)?(?P<weeks>[0-9]+w)?(?P<days>[0-9]+d)?([0-9]+[a-z])?`).FindStringSubmatch(s); len(submatches) > 0 {
		s2 := s
		result := now
		scalar := time.Duration(1)
		if strings.HasPrefix(s2, "-") {
			scalar = -1
		}

		for i, submatch := range submatches[1 : len(submatches)-1] {
			if len(submatch) == 0 {
				continue
			}

			unit := submatch[len(submatch)-1]
			if submatch[len(submatch)-1] == 'o' { // mo
				submatch = submatch[:len(submatch)-1]
			}

			n, err := strconv.Atoi(submatch[:len(submatch)-1])
			if err != nil {
				return time.Time{}, err
			}

			for i := 0; i < n; i++ {
				switch unit {
				case 'y':
					result = result.AddDate(int(scalar*1), 0, 0)
				case 'o':
					result = result.AddDate(0, int(scalar*1), 0)
				case 'w':
					result = result.AddDate(0, 0, int(scalar*7))
				case 'd':
					result = result.AddDate(0, 0, int(scalar*1))
				}
			}

			s2 = strings.ReplaceAll(s2, submatches[i+1], "")
		}

		if stdDuration := strings.TrimLeft(s2, "+-"); stdDuration != "" {
			if d, err := time.ParseDuration(stdDuration); err == nil {
				result = result.Add(scalar * d)
			}
		}

		if result != now {
			*t = result
			return result.AddDate(100, 0, 0), nil
		}
	}
	if result, err := time.Parse("2006", s); err == nil {
		*t = result
		return result.AddDate(1, 0, 0).Add(-1 * time.Minute), nil
	}
	if result, err := time.Parse("2006-01", s); err == nil {
		*t = result
		return result.AddDate(0, 1, 0).Add(-1 * time.Minute), nil
	}
	if result, err := time.Parse("2006-01-02", s); err == nil {
		*t = result
		return result.AddDate(0, 0, 1).Add(-1 * time.Minute), nil
	}
	return time.Time{}, fmt.Errorf("unimplemented format: %s", s)
}
