package dyn

import (
	"fmt"
	"time"
)

// Time represents a time-like primitive value.
//
// It represents a timestamp and includes the original string value
// that was parsed to create the timestamp. This makes it possible
// to coalesce a value that YAML interprets as a timestamp back into
// a string without losing information.
type Time struct {
	t time.Time
	s string
}

// NewTime creates a new Time from the given string.
func NewTime(str string) (Time, error) {
	// Try a couple of layouts
	for _, layout := range []string{
		"2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields.
		"2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t".
		"2006-1-2 15:4:5.999999999",       // space separated with no time zone
		"2006-1-2",                        // date only
	} {
		t, terr := time.Parse(layout, str)
		if terr == nil {
			return Time{t: t, s: str}, nil
		}
	}

	return Time{}, fmt.Errorf("invalid time value: %q", str)
}

// MustTime creates a new Time from the given string.
// It panics if the string cannot be parsed.
func MustTime(str string) Time {
	t, err := NewTime(str)
	if err != nil {
		panic(err)
	}
	return t
}

// FromTime creates a new Time from the given time.Time.
// It uses the RFC3339Nano format for its string representation.
// This guarantees that it can roundtrip into a string without losing information.
func FromTime(t time.Time) Time {
	return Time{t: t, s: t.Format(time.RFC3339Nano)}
}

// Time returns the time.Time value.
func (t Time) Time() time.Time {
	return t.t
}

// String returns the original string value that was parsed to create the timestamp.
func (t Time) String() string {
	return t.s
}