package fs

import (
	"encoding/json"
	"fmt"
	"time"
)

// Time is a time.Time with some more parsing options
type Time time.Time

// For overriding in unittests.
var (
	timeNowFunc = time.Now
)

// Turn Time into a string
func (t Time) String() string {
	if !t.IsSet() {
		return "off"
	}
	return time.Time(t).Format(time.RFC3339Nano)
}

// IsSet returns if the time is not zero
func (t Time) IsSet() bool {
	return !time.Time(t).IsZero()
}

// ParseTime parses a time or duration string as a Time.
func ParseTime(date string) (t time.Time, err error) {
	if date == "off" {
		return time.Time{}, nil
	}

	now := timeNowFunc()

	// Attempt to parse as a text time
	t, err = parseTimeDates(date)
	if err == nil {
		return t, nil
	}

	// Attempt to parse as a time.Duration offset from now
	d, err := time.ParseDuration(date)
	if err == nil {
		return now.Add(-d), nil
	}

	d, err = parseDurationSuffixes(date)
	if err == nil {
		return now.Add(-d), nil
	}

	return t, err
}

// Set a Time
func (t *Time) Set(s string) error {
	parsedTime, err := ParseTime(s)
	if err != nil {
		return err
	}
	*t = Time(parsedTime)
	return nil
}

// Type of the value
func (t Time) Type() string {
	return "Time"
}

// UnmarshalJSON makes sure the value can be parsed as a string in JSON
func (t *Time) UnmarshalJSON(in []byte) error {
	var s string
	err := json.Unmarshal(in, &s)
	if err != nil {
		return err
	}

	return t.Set(s)
}

// MarshalJSON marshals as a time.Time value
func (t Time) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Time(t))
}

// Scan implements the fmt.Scanner interface
func (t *Time) Scan(s fmt.ScanState, ch rune) error {
	token, err := s.Token(true, func(rune) bool { return true })
	if err != nil {
		return err
	}
	return t.Set(string(token))
}