// forecast_endpoint.go defines forecast endpoint behavior.
// Layer: adapters/inbound/httpapi forecast route.
package httpapi

import (
	"context"
	"time"

	"gitea.maximumdirect.net/ejr/feedapi/endpoint"
	"gitea.maximumdirect.net/ejr/feedapi/render"
	"gitea.maximumdirect.net/ejr/feedapi/response"
	"gitea.maximumdirect.net/ejr/weatherapi/internal/adapters/inbound/httpapi/presenter"
	"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)

type forecastDaySlice int

const (
	forecastDaySliceAll forecastDaySlice = iota
	forecastDaySliceToday
	forecastDaySliceTomorrow
)

var forecastNow = time.Now

func forecastDefinitions(svc Service) []endpoint.Definition {
	out := make([]endpoint.Definition, 0, 6)
	out = append(out, forecastDefinitionSet(
		"/forecast/hourly",
		"forecast_hourly.txt.tmpl",
		svc.LatestHourlyForecast,
	)...)
	out = append(out, forecastDefinitionSet(
		"/forecast/narrative",
		"forecast_narrative.txt.tmpl",
		svc.LatestNarrativeForecast,
	)...)
	return out
}

func forecastDefinitionSet(
	basePath string,
	templateName string,
	fetch func(context.Context) (*model.WeatherForecastRun, error),
) []endpoint.Definition {
	return []endpoint.Definition{
		forecastDefinition(basePath, forecastDaySliceAll, templateName, fetch),
		forecastDefinition(basePath+"/today", forecastDaySliceToday, templateName, fetch),
		forecastDefinition(basePath+"/tomorrow", forecastDaySliceTomorrow, templateName, fetch),
	}
}

func forecastDefinition(
	path string,
	daySlice forecastDaySlice,
	templateName string,
	fetch func(context.Context) (*model.WeatherForecastRun, error),
) endpoint.Definition {
	return endpoint.GET(
		path,
		bindForecastPrecisionQuery,
		func(ctx context.Context, req precisionQueryRequest) (any, error) {
			run, err := fetch(ctx)
			if err != nil {
				return nil, err
			}

			if daySlice != forecastDaySliceAll {
				run = filterForecastRunByDaySlice(run, req.Timezone, daySlice)
			}

			return response.Envelope{Data: presenter.ForecastPayload(run, req.Units, req.Precision, req.Timezone)}, nil
		},
		endpoint.WithProduces(render.FormatJSON, render.FormatXML, render.FormatText),
		endpoint.WithTemplate(templateName),
	)
}

func filterForecastRunByDaySlice(run *model.WeatherForecastRun, tz *time.Location, daySlice forecastDaySlice) *model.WeatherForecastRun {
	if run == nil {
		return nil
	}

	loc := tz
	if loc == nil {
		loc = time.UTC
	}

	now := forecastNow().In(loc)
	target := now
	if daySlice == forecastDaySliceTomorrow {
		target = target.AddDate(0, 0, 1)
	}
	year, month, day := target.Date()

	periods := make([]model.WeatherForecastPeriod, 0, len(run.Periods))
	for _, period := range run.Periods {
		start := period.StartTime.In(loc)
		y, m, d := start.Date()
		if y == year && m == month && d == day {
			periods = append(periods, period)
		}
	}

	cloned := *run
	cloned.Periods = periods
	return &cloned
}
