// query_bind.go binds endpoint query parameters into typed request config.
// Layer: adapters/inbound/httpapi request binding.
package httpapi

import (
	"net/http"
	"strings"
	"time"

	"gitea.maximumdirect.net/ejr/feedapi/bind"
	"gitea.maximumdirect.net/ejr/weatherapi/internal/adapters/inbound/httpapi/presenter"
)

type queryRequest struct {
	Units presenter.Units
}

type precisionQueryRequest struct {
	Units     presenter.Units
	Precision int
	Timezone  *time.Location
}

func bindQuery(r *http.Request) (queryRequest, error) {
	normalizeCommonQueryValue(r, "units")
	normalizeCommonQueryValue(r, "format")

	common, err := bind.CommonQueryParams(r, bind.QueryPolicy{
		AllowUnits:    true,
		AllowFormat:   true,
		DefaultUnits:  string(presenter.UnitsMetric),
		RejectUnknown: true,
	})
	if err != nil {
		return queryRequest{}, err
	}

	units := presenter.Units(strings.ToLower(strings.TrimSpace(common.Units)))
	if units == "" {
		units = presenter.UnitsMetric
	}
	return queryRequest{Units: units}, nil
}

func bindPrecisionQuery(r *http.Request) (precisionQueryRequest, error) {
	return bindPrecisionQueryInternal(r, false)
}

func bindForecastPrecisionQuery(r *http.Request) (precisionQueryRequest, error) {
	return bindPrecisionQueryInternal(r, true)
}

func bindPrecisionQueryInternal(r *http.Request, allowTimezone bool) (precisionQueryRequest, error) {
	normalizeCommonQueryValue(r, "units")
	normalizeCommonQueryValue(r, "format")
	normalizeCommonQueryValue(r, "precision")

	allowedExtra := []string{"precision"}
	if allowTimezone {
		allowedExtra = append(allowedExtra, "tz", "TZ")
	}

	common, err := bind.CommonQueryParams(r, bind.QueryPolicy{
		AllowUnits:    true,
		AllowFormat:   true,
		DefaultUnits:  string(presenter.UnitsMetric),
		RejectUnknown: true,
	}, allowedExtra...)
	if err != nil {
		return precisionQueryRequest{}, err
	}

	precision, err := bind.OptionalInt(r, "precision", 0)
	if err != nil {
		return precisionQueryRequest{}, err
	}
	if err := bind.MinInt(precision, 0, "precision"); err != nil {
		return precisionQueryRequest{}, err
	}
	if err := bind.MaxInt(precision, 2, "precision"); err != nil {
		return precisionQueryRequest{}, err
	}

	units := presenter.Units(strings.ToLower(strings.TrimSpace(common.Units)))
	if units == "" {
		units = presenter.UnitsMetric
	}

	var tz *time.Location
	if allowTimezone {
		tz, err = parseTimezoneQuery(r)
		if err != nil {
			return precisionQueryRequest{}, err
		}
	}

	return precisionQueryRequest{
		Units:     units,
		Precision: precision,
		Timezone:  tz,
	}, nil
}
