package websvc

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"

	"github.com/AdguardTeam/golibs/log"
)

// JSON Utilities

// jsonTime is a time.Time that can be decoded from JSON and encoded into JSON
// according to our API conventions.
type jsonTime time.Time

// type check
var _ json.Marshaler = jsonTime{}

// nsecPerMsec is the number of nanoseconds in a millisecond.
const nsecPerMsec = float64(time.Millisecond / time.Nanosecond)

// MarshalJSON implements the json.Marshaler interface for jsonTime.  err is
// always nil.
func (t jsonTime) MarshalJSON() (b []byte, err error) {
	msec := float64(time.Time(t).UnixNano()) / nsecPerMsec
	b = strconv.AppendFloat(nil, msec, 'f', 3, 64)

	return b, nil
}

// type check
var _ json.Unmarshaler = (*jsonTime)(nil)

// UnmarshalJSON implements the json.Marshaler interface for *jsonTime.
func (t *jsonTime) UnmarshalJSON(b []byte) (err error) {
	if t == nil {
		return fmt.Errorf("json time is nil")
	}

	msec, err := strconv.ParseFloat(string(b), 64)
	if err != nil {
		return fmt.Errorf("parsing json time: %w", err)
	}

	*t = jsonTime(time.Unix(0, int64(msec*nsecPerMsec)).UTC())

	return nil
}

// writeJSONResponse encodes v into w and logs any errors it encounters.  r is
// used to get additional information from the request.
func writeJSONResponse(w io.Writer, r *http.Request, v interface{}) {
	err := json.NewEncoder(w).Encode(v)
	if err != nil {
		log.Error("websvc: writing resp to %s %s: %s", r.Method, r.URL.Path, err)
	}
}