// forecast.go presents hourly forecast payloads in metric and US shapes.
// Layer: adapters/inbound/httpapi/presenter forecast payload mapping.
package presenter

import (
	"time"

	"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)

// WeatherForecastRunUS is the US-customary response shape for hourly forecasts.
type WeatherForecastRunUS struct {
	LocationID    string                    `json:"locationId,omitempty" xml:"locationId,omitempty"`
	LocationName  string                    `json:"locationName,omitempty" xml:"locationName,omitempty"`
	IssuedAt      time.Time                 `json:"issuedAt" xml:"issuedAt"`
	UpdatedAt     *time.Time                `json:"updatedAt,omitempty" xml:"updatedAt,omitempty"`
	Product       model.ForecastProduct     `json:"product" xml:"product"`
	Latitude      *float64                  `json:"latitude,omitempty" xml:"latitude,omitempty"`
	Longitude     *float64                  `json:"longitude,omitempty" xml:"longitude,omitempty"`
	ElevationFeet *float64                  `json:"elevationFeet,omitempty" xml:"elevationFeet,omitempty"`
	Periods       []WeatherForecastPeriodUS `json:"periods" xml:"periods"`
}

// WeatherForecastPeriodUS is the US-customary response shape for forecast periods.
type WeatherForecastPeriodUS struct {
	StartTime                         time.Time     `json:"startTime" xml:"startTime"`
	EndTime                           time.Time     `json:"endTime" xml:"endTime"`
	Name                              string        `json:"name,omitempty" xml:"name,omitempty"`
	IsDay                             *bool         `json:"isDay,omitempty" xml:"isDay,omitempty"`
	ConditionCode                     model.WMOCode `json:"conditionCode" xml:"conditionCode"`
	ConditionText                     string        `json:"conditionText,omitempty" xml:"conditionText,omitempty"`
	ProviderRawDescription            string        `json:"providerRawDescription,omitempty" xml:"providerRawDescription,omitempty"`
	TextDescription                   string        `json:"textDescription,omitempty" xml:"textDescription,omitempty"`
	DetailedText                      string        `json:"detailedText,omitempty" xml:"detailedText,omitempty"`
	IconURL                           string        `json:"iconUrl,omitempty" xml:"iconUrl,omitempty"`
	TemperatureF                      *float64      `json:"temperatureF,omitempty" xml:"temperatureF,omitempty"`
	TemperatureFMin                   *float64      `json:"temperatureFMin,omitempty" xml:"temperatureFMin,omitempty"`
	TemperatureFMax                   *float64      `json:"temperatureFMax,omitempty" xml:"temperatureFMax,omitempty"`
	DewpointF                         *float64      `json:"dewpointF,omitempty" xml:"dewpointF,omitempty"`
	RelativeHumidityPercent           *float64      `json:"relativeHumidityPercent,omitempty" xml:"relativeHumidityPercent,omitempty"`
	WindDirectionDegrees              *float64      `json:"windDirectionDegrees,omitempty" xml:"windDirectionDegrees,omitempty"`
	WindSpeedMph                      *float64      `json:"windSpeedMph,omitempty" xml:"windSpeedMph,omitempty"`
	WindGustMph                       *float64      `json:"windGustMph,omitempty" xml:"windGustMph,omitempty"`
	BarometricPressureInHg            *float64      `json:"barometricPressureInHg,omitempty" xml:"barometricPressureInHg,omitempty"`
	VisibilityMiles                   *float64      `json:"visibilityMiles,omitempty" xml:"visibilityMiles,omitempty"`
	ApparentTemperatureF              *float64      `json:"apparentTemperatureF,omitempty" xml:"apparentTemperatureF,omitempty"`
	CloudCoverPercent                 *float64      `json:"cloudCoverPercent,omitempty" xml:"cloudCoverPercent,omitempty"`
	ProbabilityOfPrecipitationPercent *float64      `json:"probabilityOfPrecipitationPercent,omitempty" xml:"probabilityOfPrecipitationPercent,omitempty"`
	PrecipitationAmountIn             *float64      `json:"precipitationAmountIn,omitempty" xml:"precipitationAmountIn,omitempty"`
	SnowfallDepthIn                   *float64      `json:"snowfallDepthIn,omitempty" xml:"snowfallDepthIn,omitempty"`
	UVIndex                           *float64      `json:"uvIndex,omitempty" xml:"uvIndex,omitempty"`
}

func ForecastPayload(run *model.WeatherForecastRun, units Units, precision int, tz *time.Location) any {
	if run == nil {
		return nil
	}
	if units == UnitsUS {
		out := WeatherForecastRunUS{
			LocationID:    run.LocationID,
			LocationName:  run.LocationName,
			IssuedAt:      inLocationTime(run.IssuedAt, tz),
			UpdatedAt:     inLocationTimePtr(run.UpdatedAt, tz),
			Product:       run.Product,
			Latitude:      copyFloat64Ptr(run.Latitude),
			Longitude:     copyFloat64Ptr(run.Longitude),
			ElevationFeet: roundedPtr(scalePtr(run.ElevationMeters, metersToFeetFactor), precision),
			Periods:       make([]WeatherForecastPeriodUS, 0, len(run.Periods)),
		}
		for _, p := range run.Periods {
			out.Periods = append(out.Periods, WeatherForecastPeriodUS{
				StartTime:                         inLocationTime(p.StartTime, tz),
				EndTime:                           inLocationTime(p.EndTime, tz),
				Name:                              p.Name,
				IsDay:                             copyBoolPtr(p.IsDay),
				ConditionCode:                     p.ConditionCode,
				ConditionText:                     p.ConditionText,
				ProviderRawDescription:            p.ProviderRawDescription,
				TextDescription:                   p.TextDescription,
				DetailedText:                      p.DetailedText,
				IconURL:                           p.IconURL,
				TemperatureF:                      roundedPtr(celsiusToFahrenheitPtr(p.TemperatureC), precision),
				TemperatureFMin:                   roundedPtr(celsiusToFahrenheitPtr(p.TemperatureCMin), precision),
				TemperatureFMax:                   roundedPtr(celsiusToFahrenheitPtr(p.TemperatureCMax), precision),
				DewpointF:                         roundedPtr(celsiusToFahrenheitPtr(p.DewpointC), precision),
				RelativeHumidityPercent:           roundedPtr(copyFloat64Ptr(p.RelativeHumidityPercent), precision),
				WindDirectionDegrees:              roundedPtr(copyFloat64Ptr(p.WindDirectionDegrees), precision),
				WindSpeedMph:                      roundedPtr(scalePtr(p.WindSpeedKmh, kmhToMphFactor), precision),
				WindGustMph:                       roundedPtr(scalePtr(p.WindGustKmh, kmhToMphFactor), precision),
				BarometricPressureInHg:            roundedPtr(scalePtr(p.BarometricPressurePa, paToInHgFactor), precision),
				VisibilityMiles:                   roundedPtr(scalePtr(p.VisibilityMeters, metersToMilesFactor), precision),
				ApparentTemperatureF:              roundedPtr(celsiusToFahrenheitPtr(p.ApparentTemperatureC), precision),
				CloudCoverPercent:                 roundedPtr(copyFloat64Ptr(p.CloudCoverPercent), precision),
				ProbabilityOfPrecipitationPercent: roundedPtr(copyFloat64Ptr(p.ProbabilityOfPrecipitationPercent), precision),
				PrecipitationAmountIn:             roundedPtr(scalePtr(p.PrecipitationAmountMm, mmToInchesFactor), precision),
				SnowfallDepthIn:                   roundedPtr(scalePtr(p.SnowfallDepthMM, mmToInchesFactor), precision),
				UVIndex:                           roundedPtr(copyFloat64Ptr(p.UVIndex), precision),
			})
		}
		return out
	}

	out := model.WeatherForecastRun{
		LocationID:      run.LocationID,
		LocationName:    run.LocationName,
		IssuedAt:        inLocationTime(run.IssuedAt, tz),
		UpdatedAt:       inLocationTimePtr(run.UpdatedAt, tz),
		Product:         run.Product,
		Latitude:        copyFloat64Ptr(run.Latitude),
		Longitude:       copyFloat64Ptr(run.Longitude),
		ElevationMeters: roundedPtr(copyFloat64Ptr(run.ElevationMeters), precision),
		Periods:         make([]model.WeatherForecastPeriod, 0, len(run.Periods)),
	}

	for _, p := range run.Periods {
		out.Periods = append(out.Periods, model.WeatherForecastPeriod{
			StartTime:                         inLocationTime(p.StartTime, tz),
			EndTime:                           inLocationTime(p.EndTime, tz),
			Name:                              p.Name,
			IsDay:                             copyBoolPtr(p.IsDay),
			ConditionCode:                     p.ConditionCode,
			ConditionText:                     p.ConditionText,
			ProviderRawDescription:            p.ProviderRawDescription,
			TextDescription:                   p.TextDescription,
			DetailedText:                      p.DetailedText,
			IconURL:                           p.IconURL,
			TemperatureC:                      roundedPtr(copyFloat64Ptr(p.TemperatureC), precision),
			TemperatureCMin:                   roundedPtr(copyFloat64Ptr(p.TemperatureCMin), precision),
			TemperatureCMax:                   roundedPtr(copyFloat64Ptr(p.TemperatureCMax), precision),
			DewpointC:                         roundedPtr(copyFloat64Ptr(p.DewpointC), precision),
			RelativeHumidityPercent:           roundedPtr(copyFloat64Ptr(p.RelativeHumidityPercent), precision),
			WindDirectionDegrees:              roundedPtr(copyFloat64Ptr(p.WindDirectionDegrees), precision),
			WindSpeedKmh:                      roundedPtr(copyFloat64Ptr(p.WindSpeedKmh), precision),
			WindGustKmh:                       roundedPtr(copyFloat64Ptr(p.WindGustKmh), precision),
			BarometricPressurePa:              roundedPtr(copyFloat64Ptr(p.BarometricPressurePa), precision),
			VisibilityMeters:                  roundedPtr(copyFloat64Ptr(p.VisibilityMeters), precision),
			ApparentTemperatureC:              roundedPtr(copyFloat64Ptr(p.ApparentTemperatureC), precision),
			CloudCoverPercent:                 roundedPtr(copyFloat64Ptr(p.CloudCoverPercent), precision),
			ProbabilityOfPrecipitationPercent: roundedPtr(copyFloat64Ptr(p.ProbabilityOfPrecipitationPercent), precision),
			PrecipitationAmountMm:             roundedPtr(copyFloat64Ptr(p.PrecipitationAmountMm), precision),
			SnowfallDepthMM:                   roundedPtr(copyFloat64Ptr(p.SnowfallDepthMM), precision),
			UVIndex:                           roundedPtr(copyFloat64Ptr(p.UVIndex), precision),
		})
	}
	return &out
}
