/**
 * api.met.no — the Norwegian Meteorological Institute.
 *
 * The global fallback, for the same reason NWS is the default: it is free,
 * keyless, and its licence (CC BY 4.0) is one a public map can actually satisfy
 * by printing a line of attribution — which is what the `attribution` field on
 * the wire carries, and which the consumer is expected to display.
 *
 * MET requires an identifiable User-Agent and will block callers who do not send
 * one, which is why an empty `TERA_WEATHER_CONTACT` demotes this source in
 * `config.ts` rather than being quietly worked around here.
 */

import { getJson, userAgent } from "../http.ts";
import type { WeatherBody } from "../../../src/server/wire.ts";
import { deriveCondition, finiteOrNull, unitRange } from "./condition.ts";

const BASE = "https://api.met.no/weatherapi/locationforecast/2.0/compact";

interface ForecastResponse {
  properties?: {
    timeseries?: {
      time?: string;
      data?: {
        instant?: { details?: Record<string, number | undefined> };
        next_1_hours?: {
          summary?: { symbol_code?: string };
          details?: { precipitation_amount?: number };
        };
      };
    }[];
  };
}

export async function fetchMetno(
  lat: number,
  lng: number,
  contact: string,
): Promise<WeatherBody | null> {
  // MET asks callers to truncate coordinates to four decimals so their cache
  // works; a request for 37.774929 and one for 37.7749 are the same weather.
  const url = `${BASE}?lat=${lat.toFixed(4)}&lon=${lng.toFixed(4)}`;
  const body = await getJson<ForecastResponse>(url, {
    headers: { "user-agent": userAgent(contact) },
  });

  const entry = body?.properties?.timeseries?.[0];
  const instant = entry?.data?.instant?.details;
  if (entry === undefined || instant === undefined) return null;

  const symbol = entry.data?.next_1_hours?.summary?.symbol_code ?? "";
  const mm = entry.data?.next_1_hours?.details?.precipitation_amount ?? 0;
  // Millimetres in the coming hour, read as an intensity dial: 4 mm/h is
  // thoroughly wet, and the renderer has nothing to do with anything past that.
  const precipitation = unitRange(mm / 4);
  const cloudCover = unitRange((instant["cloud_area_fraction"] ?? 0) / 100);
  const windMs = finiteOrNull(instant["wind_speed"]);

  return {
    observedAt: entry.time ?? new Date().toISOString(),
    source: "metno",
    synthetic: false,
    location: { lat, lng },
    temperatureC: finiteOrNull(instant["air_temperature"]),
    windKph: windMs === null ? null : windMs * 3.6,
    windDirDeg: finiteOrNull(instant["wind_from_direction"]),
    cloudCover,
    precipitation,
    visibilityKm: null,
    condition: deriveCondition({
      cloudCover,
      precipitation,
      visibilityKm: null,
      frozen: symbol.includes("snow"),
      thunder: symbol.includes("thunder"),
    }),
    attribution: ["Weather data from MET Norway (met.no), licensed CC BY 4.0"],
  };
}
