package nextdue

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

type ValueKind string

const (
	ValueKindUnknown ValueKind = ""
	ValueKindHours   ValueKind = "hours"
	ValueKindDate    ValueKind = "date"
)

type Value struct {
	Kind  ValueKind
	Raw   string
	Hours *float64
	Date  *time.Time
}

type ParseError struct {
	Field string
	Raw   string
	Msg   string
}

func (e *ParseError) Error() string {
	if e == nil {
		return ""
	}
	if strings.TrimSpace(e.Field) == "" {
		return e.Msg
	}
	return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}

type Parser struct {
	DateLayout string
}

func NewParser() Parser { return Parser{DateLayout: "2006-01-02"} }

func (p Parser) Parse(field, raw string) (Value, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return Value{Kind: ValueKindUnknown, Raw: ""}, nil
	}
	if d, err := time.Parse(p.dateLayout(), raw); err == nil {
		dd := d.UTC()
		return Value{Kind: ValueKindDate, Raw: raw, Date: &dd}, nil
	}
	if h, err := strconv.ParseFloat(raw, 64); err == nil {
		return Value{Kind: ValueKindHours, Raw: raw, Hours: &h}, nil
	}
	return Value{}, &ParseError{Field: field, Raw: raw, Msg: "must be either an ISO date (YYYY-MM-DD) or a numeric hour value"}
}

func IsUnset(v Value) bool {
	switch v.Kind {
	case ValueKindHours:
		return v.Hours == nil || *v.Hours <= 0
	case ValueKindDate:
		return v.Date == nil || v.Date.IsZero()
	default:
		return true
	}
}

func (p Parser) dateLayout() string {
	if strings.TrimSpace(p.DateLayout) == "" {
		return "2006-01-02"
	}
	return p.DateLayout
}

func NormalizeDate(t time.Time) time.Time {
	return time.Date(t.UTC().Year(), t.UTC().Month(), t.UTC().Day(), 0, 0, 0, 0, time.UTC)
}
