# Presenters and Templates

This document describes payload shaping in
`internal/adapters/inbound/httpapi/presenter` and its relationship to text
templates. Public response fields are documented in [`docs/api.md`](../api.md).

## Purpose

Presenters translate repository/application read models into response-ready
payloads. They own unit conversion, numeric rounding, timezone conversion,
optional field preservation, and copy semantics.

## Inputs and Outputs

Inputs:

- weatherfeeder model values returned by the repository;
- `app.CurrentConditions` values returned by the application service;
- requested unit mode;
- requested precision;
- optional timezone location.

Outputs:

- JSON/XML/text-ready payload structs or canonical model copies;
- nil payloads for nil inputs.

## Boundaries

Presenters may:

- convert metric values to US-customary response fields;
- round numeric values that are presentation values;
- convert timestamps to a requested timezone;
- copy slices and pointers before changing presentation values;
- add template-only helper fields when they are not serialized.

Presenters must not:

- execute SQL;
- call services or repositories;
- parse HTTP query parameters;
- load templates;
- change canonical repository models in place.

## Config Fields Used

Presenters do not read config directly. They receive unit, precision, and
timezone selections from HTTP binders. Text output depends indirectly on
`templates.base_dir` because feedapi loads templates from that configured
directory.

## External Adapters Used

- `weatherfeeder/model` for canonical weather payloads.
- `weatherfeeder/standards` for current-condition text from WMO codes.
- `internal/app` for the current-conditions read model.
- Feedapi templates indirectly consume presenter output during text rendering.

## State

Presenters are stateless. Helper functions allocate copied values and return
new payloads for each request.

## Unit Conversion

Metric mode generally returns weatherfeeder canonical model shapes or
metric-named fields. US mode uses explicit US response structs for observations
and forecasts and US-specific fields in current conditions.

Conversion constants live in `constants.go`:

- Celsius to Fahrenheit;
- kilometers per hour to miles per hour;
- meters to miles or feet;
- pascals to inches of mercury;
- millimeters to inches.

Non-unit fields such as percentages, directions, text, IDs, and ordering values
keep their existing values.

## Precision

`roundedPtr` and `roundFloat` round numeric presentation values. Precision `0`
rounds to whole numbers. Positive precision uses powers of ten. Nil numeric
pointers remain nil.

Latitude and longitude are copied but not rounded by forecast presenters.

## Timezone Conversion

`inLocationTime` and `inLocationTimePtr` convert timestamps only when a
timezone is supplied. Without a timezone, timestamps are preserved as returned
by the repository.

Timezone conversion is applied by forecast, discussion, weather story, and
outlook presenters. Observations and current conditions do not currently
receive timezone input from their routes.

## Optional Fields and Copy Semantics

Presenter helpers copy pointer values before changing them. Slices are copied
before inclusion where needed. This preserves nil/omitempty behavior and avoids
mutating repository-returned values.

Nil input payloads return nil. Endpoint handlers wrap those nil payloads in a
response envelope so renderers can produce `data: null`.

## Endpoint Families

- Observations: metric copy or US response shape, including present-weather
  slice copy.
- Current conditions: single response shape with either metric or US unit
  fields populated, plus a template-only day/night text helper.
- Alerts: pass-through of canonical alert runs, with nil preserved.
- Forecasts: metric copy or US response shape, period copy, unit conversion,
  precision, and timezone conversion.
- Discussions: full or focused payload shapes, section copy, key-message copy,
  timezone conversion.
- Weather stories: run/story copy and timezone conversion.
- Convective outlooks: canonical model copy, pointer and geometry copy,
  run-level discussion copy, and timezone conversion for run, outlook, and
  discussion timestamps. Outlook polygon prose is not handled by the presenter;
  prose is carried by run-level discussions. `units` is accepted by routes but
  ignored by the presenter because outlook fields are not unit-bearing.

## Templates

Text templates consume the same envelope data produced by presenters. Template
files live under `templates/` and are loaded by feedapi from `templates.base_dir`.

Presenter changes can break text output even when JSON and XML still compile.
Check template field references before renaming or removing presenter fields.

## Tests to Inspect Before Changing

- `internal/adapters/inbound/httpapi/presenter/payload_test.go`
- `internal/adapters/inbound/httpapi/endpoints_test.go` for rendered text and
  envelope behavior
- affected template files under `templates/`

## Invariants

- Keep unit conversion and rounding out of handlers and repositories.
- Preserve nil pointer and optional field behavior.
- Copy before converting, rounding, or timezone-shifting values.
- Keep text-template helper fields out of JSON/XML when they are not API fields.
- Update `docs/api.md` and templates when payload shape changes.
