/**
 * Which source answers, for which place, how often it is asked, and what happens
 * when it does not.
 *
 * Four rules, in order of how much trouble getting them wrong causes:
 *
 * 1. **This never throws.** `current()` always resolves to a `WeatherBody`.
 * 2. **A dead upstream serves the last good observation** for that place, and
 *    only falls back to the clear day if there has never been one. Ten-minute-old
 *    weather is better than no weather and much better than a 503.
 * 3. **Nothing is fetched until somebody asks.** A box nobody visits makes no
 *    outbound requests, which matters when the source is a public good with a
 *    rate limit and a fair-use policy.
 * 4. **One cache entry per region, and the regions come from the environment.**
 *    San Francisco's fog and Los Angeles's sun are two different observations
 *    and the old single-origin cache could only hold one of them. The keys are
 *    region ids and nothing a caller sends becomes one — `routes/weather.ts`
 *    resolves the query to a configured region first, which is what bounds both
 *    this map and the number of stations `nws.ts` will ever look up.
 *
 * Rules 1–3 live in `upstream.ts` now, because flights wanted exactly the same
 * three.
 */

import type { Config } from "../config.ts";
import type { Region } from "../regions.ts";
import type { WeatherBody } from "../../../src/server/wire.ts";
import { createUpstream } from "../upstream.ts";
import { fetchMetno } from "./metno.ts";
import { fetchNws } from "./nws.ts";
import { fetchOpenMeteo } from "./openmeteo.ts";
import { clearDay } from "./synthetic.ts";

export interface WeatherService {
  current(region: Region): Promise<WeatherBody>;
}

export interface WeatherLog {
  warn(msg: string): void;
}

export function createWeatherService(config: Config, log: WeatherLog): WeatherService {
  const { source, contact, ttlSeconds } = config.weather;
  const upstream = createUpstream<WeatherBody>({ label: `weather:${source}`, ttlSeconds, log });

  function fetchFor(region: Region): Promise<WeatherBody | null> {
    // The coordinate that goes upstream is the *region centre*, never the one
    // the caller sent. See the top of `regions.ts` for why that distinction is
    // the whole abuse story on this endpoint.
    const { lat, lng } = region;
    return source === "nws"
      ? fetchNws(lat, lng, contact)
      : source === "metno"
        ? fetchMetno(lat, lng, contact)
        : source === "openmeteo"
          ? fetchOpenMeteo(lat, lng)
          : Promise.resolve(null);
  }

  return {
    async current(region: Region): Promise<WeatherBody> {
      if (source === "none") return clearDay(region.lat, region.lng);
      const body = await upstream.get(region.id, () => fetchFor(region));
      return body ?? clearDay(region.lat, region.lng);
    },
  };
}
