defmodule Microwaveprop.Commercial.PollWorker do
  @moduledoc false
  use Oban.Worker, queue: :commercial, max_attempts: 1

  alias Microwaveprop.Commercial
  alias Microwaveprop.Commercial.SnmpClient
  alias Microwaveprop.Weather
  alias Microwaveprop.Weather.IemClient

  require Logger

  @impl Oban.Worker
  def perform(%Oban.Job{}) do
    links = Commercial.enabled_links()

    poll_and_record(links, &SnmpClient.poll/3)
    fetch_weather(links)

    :ok
  end

  def poll_and_record(links, poll_fn) do
    now = DateTime.utc_now()
    Enum.each(links, &poll_link(&1, poll_fn, now))
  end

  defp poll_link(link, poll_fn, now) do
    case poll_fn.(link.host, link.community, link.radio_type) do
      {:ok, data} ->
        save_sample(link, data, now)

      {:error, reason} ->
        Logger.warning("SNMP poll failed for #{link.label} (#{link.host}): #{inspect(reason)}")
    end
  end

  defp save_sample(link, data, now) do
    attrs =
      data
      |> Map.put(:link_id, link.id)
      |> Map.put(:sampled_at, now)

    case Commercial.create_sample(attrs) do
      {:ok, _sample} ->
        Logger.info("Recorded sample for #{link.label}")

      {:error, changeset} ->
        Logger.warning("Failed to save sample for #{link.label}: #{inspect(changeset.errors)}")
    end
  end

  defp fetch_weather(links) do
    stations =
      links
      |> Enum.map(& &1.weather_station)
      |> Enum.reject(&is_nil/1)
      |> Enum.uniq()

    now = DateTime.utc_now()
    start_dt = DateTime.add(now, -3600, :second)

    Enum.each(stations, &fetch_station_weather(&1, start_dt, now))
  end

  defp fetch_station_weather(station_code, start_dt, now) do
    {:ok, station} =
      Weather.find_or_create_station(%{
        station_code: station_code,
        station_type: "asos",
        name: station_code,
        lat: 0.0,
        lon: 0.0
      })

    case IemClient.fetch_asos(station_code, start_dt, now) do
      {:ok, rows} ->
        ingest_asos(station, station_code, rows)

      {:error, reason} ->
        Logger.warning("ASOS fetch failed for #{station_code}: #{inspect(reason)}")
    end
  end

  defp ingest_asos(station, station_code, rows) do
    rows
    |> Enum.filter(& &1.observed_at)
    |> Enum.each(&Weather.upsert_surface_observation(station, &1))

    Logger.info("Fetched #{length(rows)} ASOS observations for #{station_code}")
  end
end
