#!/usr/bin/env bash

set -euo pipefail

location_input="${1:-Los_Angeles}"
location_query="${location_input//_/ }"
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/waybar"
cache_key="${location_input//[^[:alnum:]_.-]/_}"
geo_cache_file="$cache_dir/weather-geo-${cache_key}.json"
weather_cache_file="$cache_dir/weather-${cache_key}.json"

json_escape() {
  jq -Rsa .
}

emit_json() {
  local text="$1"
  local tooltip="$2"

  printf '{"text":%s,"tooltip":%s}\n' \
    "$(printf '%s' "$text" | json_escape)" \
    "$(printf '%s' "$tooltip" | json_escape)"
}

emit_cached_or_error() {
  if [[ -f "$weather_cache_file" ]]; then
    cat "$weather_cache_file"
    return
  fi

  emit_json "weather unavailable" "weather service unavailable"
}

url_encode() {
  jq -rn --arg value "$1" '$value | @uri'
}

weather_icon() {
  local code="$1"
  local is_day="$2"

  case "$code" in
    0) [[ "$is_day" == "1" ]] && printf '☀' || printf '☾' ;;
    1 | 2) [[ "$is_day" == "1" ]] && printf '⛅' || printf '☁' ;;
    3) printf '☁' ;;
    45 | 48) printf '🌫' ;;
    51 | 53 | 55 | 56 | 57) printf '🌦' ;;
    61 | 63 | 65 | 66 | 67 | 80 | 81 | 82) printf '🌧' ;;
    71 | 73 | 75 | 77 | 85 | 86) printf '🌨' ;;
    95 | 96 | 99) printf '⛈' ;;
    *) printf '☁' ;;
  esac
}

weather_description() {
  case "$1" in
    0) printf 'Clear sky' ;;
    1) printf 'Mainly clear' ;;
    2) printf 'Partly cloudy' ;;
    3) printf 'Overcast' ;;
    45 | 48) printf 'Fog' ;;
    51) printf 'Light drizzle' ;;
    53) printf 'Drizzle' ;;
    55) printf 'Dense drizzle' ;;
    56 | 57) printf 'Freezing drizzle' ;;
    61) printf 'Slight rain' ;;
    63) printf 'Rain' ;;
    65) printf 'Heavy rain' ;;
    66 | 67) printf 'Freezing rain' ;;
    71) printf 'Slight snow' ;;
    73) printf 'Snow' ;;
    75) printf 'Heavy snow' ;;
    77) printf 'Snow grains' ;;
    80) printf 'Rain showers' ;;
    81) printf 'Rain showers' ;;
    82) printf 'Heavy rain showers' ;;
    85 | 86) printf 'Snow showers' ;;
    95) printf 'Thunderstorm' ;;
    96 | 99) printf 'Thunderstorm with hail' ;;
    *) printf 'Weather unavailable' ;;
  esac
}

load_geocode() {
  if [[ -f "$geo_cache_file" ]]; then
    cat "$geo_cache_file"
    return
  fi

  local encoded_query response
  encoded_query="$(url_encode "$location_query")"
  response="$(
    curl \
      --silent \
      --show-error \
      --fail \
      --max-time 10 \
      "https://geocoding-api.open-meteo.com/v1/search?name=${encoded_query}&count=1&language=en&format=json" 2>/dev/null || true
  )"

  if jq -e '.results[0] | .name and .latitude and .longitude and .timezone' >/dev/null <<<"$response"; then
    jq -c '.results[0] | {name, admin1, country, latitude, longitude, timezone}' <<<"$response" | tee "$geo_cache_file"
    return
  fi

  return 1
}

fetch_weather() {
  local latitude="$1"
  local longitude="$2"
  local timezone="$3"

  curl \
    --silent \
    --show-error \
    --fail \
    --max-time 10 \
    "https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,is_day&temperature_unit=fahrenheit&wind_speed_unit=mph&timezone=$(url_encode "$timezone")" 2>/dev/null || true
}

format_weather() {
  local place_json="$1"
  local weather_json="$2"
  local name region place code is_day icon description temperature feels_like humidity wind text tooltip

  name="$(jq -r '.name' <<<"$place_json")"
  region="$(jq -r '.admin1 // empty' <<<"$place_json")"
  place="$name"
  if [[ -n "$region" ]]; then
    place+=", $region"
  fi

  code="$(jq -r '.current.weather_code' <<<"$weather_json")"
  is_day="$(jq -r '.current.is_day' <<<"$weather_json")"
  icon="$(weather_icon "$code" "$is_day")"
  description="$(weather_description "$code")"
  temperature="$(jq -r '.current.temperature_2m | round | "\(.)"' <<<"$weather_json")"
  feels_like="$(jq -r '.current.apparent_temperature | round | "\(.)"' <<<"$weather_json")"
  humidity="$(jq -r '.current.relative_humidity_2m | round | "\(.)"' <<<"$weather_json")"
  wind="$(jq -r '.current.wind_speed_10m | round | "\(.)"' <<<"$weather_json")"

  text="${icon} ${temperature}°F"
  tooltip="${place}: ${description}. Feels like ${feels_like}°F, humidity ${humidity}%, wind ${wind} mph"

  emit_json "$text" "$tooltip"
}

mkdir -p "$cache_dir"

if ! place_json="$(load_geocode)"; then
  emit_cached_or_error
  exit 0
fi

latitude="$(jq -r '.latitude' <<<"$place_json")"
longitude="$(jq -r '.longitude' <<<"$place_json")"
timezone="$(jq -r '.timezone' <<<"$place_json")"

weather_json="$(fetch_weather "$latitude" "$longitude" "$timezone")"

if jq -e '.current | .temperature_2m and .relative_humidity_2m and .apparent_temperature and .weather_code and .wind_speed_10m and .is_day' >/dev/null <<<"$weather_json"; then
  format_weather "$place_json" "$weather_json" | tee "$weather_cache_file"
  exit 0
fi

emit_cached_or_error
