package rfctime

import (
	"encoding/json"
	"errors"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/bsoncodec"
	"go.mongodb.org/mongo-driver/bson/bsonrw"
	"go.mongodb.org/mongo-driver/bson/bsontype"
	"gogs.mikescher.com/BlackForestBytes/goext/timeext"
	"reflect"
	"time"
)

type SecondsF64 time.Duration

func (d SecondsF64) Duration() time.Duration {
	return time.Duration(d)
}

func (d SecondsF64) String() string {
	return d.Duration().String()
}

func (d SecondsF64) Nanoseconds() int64 {
	return d.Duration().Nanoseconds()
}

func (d SecondsF64) Microseconds() int64 {
	return d.Duration().Microseconds()
}

func (d SecondsF64) Milliseconds() int64 {
	return d.Duration().Milliseconds()
}

func (d SecondsF64) Seconds() float64 {
	return d.Duration().Seconds()
}

func (d SecondsF64) Minutes() float64 {
	return d.Duration().Minutes()
}

func (d SecondsF64) Hours() float64 {
	return d.Duration().Hours()
}

func (d *SecondsF64) UnmarshalJSON(data []byte) error {
	var secs float64 = 0
	if err := json.Unmarshal(data, &secs); err != nil {
		return err
	}
	*d = SecondsF64(timeext.FromSeconds(secs))
	return nil
}

func (d SecondsF64) MarshalJSON() ([]byte, error) {
	secs := d.Seconds()
	return json.Marshal(secs)
}

func (d *SecondsF64) UnmarshalBSONValue(bt bsontype.Type, data []byte) error {
	if bt == bson.TypeNull {
		// we can't set nil in UnmarshalBSONValue (so we use default(struct))
		// Use mongoext.CreateGoExtBsonRegistry if you need to unmarsh pointer values
		// https://stackoverflow.com/questions/75167597
		// https://jira.mongodb.org/browse/GODRIVER-2252
		*d = SecondsF64(0)
		return nil
	}
	if bt != bson.TypeDouble {
		return errors.New(fmt.Sprintf("cannot unmarshal %v into SecondsF64", bt))
	}
	var secValue float64
	err := bson.RawValue{Type: bt, Value: data}.Unmarshal(&secValue)
	if err != nil {
		return err
	}
	*d = SecondsF64(int64(secValue * float64(time.Second)))
	return nil
}

func (d SecondsF64) MarshalBSONValue() (bsontype.Type, []byte, error) {
	return bson.MarshalValue(d.Seconds())
}

func (d SecondsF64) DecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
	if val.Kind() == reflect.Ptr && val.IsNil() {
		if !val.CanSet() {
			return errors.New("ValueUnmarshalerDecodeValue")
		}
		val.Set(reflect.New(val.Type().Elem()))
	}

	tp, src, err := bsonrw.Copier{}.CopyValueToBytes(vr)
	if err != nil {
		return err
	}

	if val.Kind() == reflect.Ptr && len(src) == 0 {
		val.Set(reflect.Zero(val.Type()))
		return nil
	}

	err = d.UnmarshalBSONValue(tp, src)
	if err != nil {
		return err
	}

	if val.Kind() == reflect.Ptr {
		val.Set(reflect.ValueOf(&d))
	} else {
		val.Set(reflect.ValueOf(d))
	}

	return nil
}

func NewSecondsF64(t time.Duration) SecondsF64 {
	return SecondsF64(t)
}