package qs

import (
	"fmt"
	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/search/query"
	"strconv"
	"time"
)

// helper stuff for setting up range queries

// returns time, precision
// TODO: support other precisions? "YYYY-MM", "YYYY-MM-DDThh:mm:ss" etc?
func parseTime(in string, loc *time.Location) (time.Time, string) {
	t, err := time.ParseInLocation("2006-01-02", in, loc)
	if err != nil {
		return time.Time{}, ""
	}
	return t, "day"
}

type rangeParams struct {
	min, max                   *string
	minInclusive, maxInclusive *bool
	// need location for parsing times
	loc *time.Location
}

func newRangeParams(minVal, maxVal string, minInc, maxInc bool, loc *time.Location) *rangeParams {
	rp := &rangeParams{}
	if minVal != "" {
		rp.min = &minVal
		rp.minInclusive = &minInc
	}
	if maxVal != "" {
		rp.max = &maxVal
		rp.maxInclusive = &maxInc
	}
	if loc == nil {
		loc = time.UTC
	}
	rp.loc = loc
	return rp
}

func (rp *rangeParams) numericArgs() (bool, *float64, *float64) {
	var f1, f2 *float64
	if rp.min != nil {
		f, err := strconv.ParseFloat(*rp.min, 64)
		if err != nil {
			return false, nil, nil
		}
		f1 = &f
	}
	if rp.max != nil {
		f, err := strconv.ParseFloat(*rp.max, 64)
		if err != nil {
			return false, nil, nil
		}
		f2 = &f
	}
	return true, f1, f2
}

func (rp *rangeParams) dateArgs() (bool, time.Time, time.Time) {
	var truthy bool = true
	var falsey bool = false
	var t1, t2 time.Time
	var prec string
	if rp.min != nil {
		t1, prec = parseTime(*rp.min, rp.loc)
		switch prec {
		case "day":
			if !*rp.minInclusive {
				// add 1 day and make inclusive
				t1 = t1.AddDate(0, 0, 1)
				rp.minInclusive = &truthy
			}
		default:
			return false, t1, t2
		}
	}
	if rp.max != nil {
		t2, prec = parseTime(*rp.max, rp.loc)
		switch prec {
		case "day":
			if *rp.maxInclusive {
				// add 1 day and change to exclusive
				t2 = t2.AddDate(0, 0, 1)
				rp.maxInclusive = &falsey
			}
		default:
			return false, t1, t2
		}
	}

	return true, t1, t2
}

// try and build a query from the given params
func (rp *rangeParams) generate() (query.Query, error) {
	if rp.min == nil && rp.max == nil {
		return nil, fmt.Errorf("empty range")
	}
	isNumeric, f1, f2 := rp.numericArgs()
	if isNumeric {
		return bleve.NewNumericRangeInclusiveQuery(f1, f2, rp.minInclusive, rp.maxInclusive), nil
	}

	isDate, t1, t2 := rp.dateArgs()
	if isDate {
		return bleve.NewDateRangeInclusiveQuery(t1, t2, rp.minInclusive, rp.maxInclusive), nil
	}
	return nil, fmt.Errorf("not numeric")

}
