defmodule AprsmeWeb.WeatherLive.CallsignView do
  @moduledoc false
  use AprsmeWeb, :live_view
  use Gettext, backend: AprsmeWeb.Gettext

  import Phoenix.LiveView, only: [push_event: 3, connected?: 1]

  alias Aprsme.Packets
  alias AprsmeWeb.MapLive.PacketUtils
  alias AprsmeWeb.WeatherUnits

  @impl true
  def mount(%{"callsign" => callsign}, _session, socket) do
    normalized_callsign = String.upcase(String.trim(callsign))

    # Subscribe to weather-specific topic for targeted updates
    if connected?(socket) do
      weather_topic = "weather:#{normalized_callsign}"
      Phoenix.PubSub.subscribe(Aprsme.PubSub, weather_topic)
    end

    weather_packet = get_latest_weather_packet(normalized_callsign)
    {start_time, end_time} = default_time_range()
    weather_history = get_weather_history(normalized_callsign, start_time, end_time)

    # Get locale from socket assigns (set by LocaleHook)
    locale = Map.get(socket.assigns, :locale, "en")
    unit_labels = WeatherUnits.unit_labels(locale)

    weather_history_json =
      weather_history
      |> Enum.map(fn pkt ->
        dew_point =
          if is_number(pkt.temperature) and is_number(pkt.humidity) do
            calc_dew_point(pkt.temperature, pkt.humidity)
          end

        # Convert units based on locale
        {temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
        {dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
        {wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
        {rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
        {rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
        {rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)

        %{
          timestamp: pkt.received_at,
          temperature: temp_value,
          dew_point: dew_value,
          humidity: pkt.humidity,
          pressure: pkt.pressure,
          wind_direction: pkt.wind_direction,
          wind_speed: wind_speed_value,
          rain_1h: rain_1h_value,
          rain_24h: rain_24h_value,
          rain_since_midnight: rain_since_midnight_value,
          luminosity: pkt.luminosity
        }
      end)
      |> Jason.encode!()

    # Add chart labels for translation with locale-aware units
    chart_labels = %{
      temp_title: gettext("Temperature & Dew Point (%{unit})", unit: unit_labels.temperature),
      temp_label: gettext("Temperature (%{unit})", unit: unit_labels.temperature),
      dew_label: gettext("Dew Point (%{unit})", unit: unit_labels.temperature),
      humidity_title: gettext("Humidity (%)"),
      humidity_label: gettext("Humidity (%)"),
      pressure_title: gettext("Pressure (mb)"),
      pressure_label: gettext("Pressure (mb)"),
      wind_title: gettext("Wind (%{unit})", unit: unit_labels.wind_speed),
      wind_label: gettext("Wind Speed (%{unit})", unit: unit_labels.wind_speed),
      gust_label: gettext("Wind Gust (%{unit})", unit: unit_labels.wind_speed),
      time: gettext("Time"),
      degf: unit_labels.temperature,
      percent: gettext("%"),
      mb: gettext("mb")
    }

    socket =
      socket
      |> assign(:callsign, normalized_callsign)
      |> assign(:weather_packet, weather_packet)
      |> assign(:page_title, "Weather for #{normalized_callsign}")
      |> assign(:weather_history, weather_history)
      |> assign(:weather_history_json, weather_history_json)
      |> assign(:chart_labels, chart_labels)

    {:ok, socket}
  end

  @impl true
  def handle_info({:weather_packet, _packet}, socket) do
    # Update weather data when new weather packet arrives for this callsign
    weather_packet = get_latest_weather_packet(socket.assigns.callsign)
    {start_time, end_time} = default_time_range()
    weather_history = get_weather_history(socket.assigns.callsign, start_time, end_time)

    # Get locale from socket assigns
    locale = Map.get(socket.assigns, :locale, "en")

    weather_history_json =
      weather_history
      |> Enum.map(fn pkt ->
        dew_point =
          if is_number(pkt.temperature) and is_number(pkt.humidity) do
            calc_dew_point(pkt.temperature, pkt.humidity)
          end

        # Convert units based on locale
        {temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
        {dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
        {wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
        {rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
        {rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
        {rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)

        %{
          timestamp: pkt.received_at,
          temperature: temp_value,
          dew_point: dew_value,
          humidity: pkt.humidity,
          pressure: pkt.pressure,
          wind_direction: pkt.wind_direction,
          wind_speed: wind_speed_value,
          rain_1h: rain_1h_value,
          rain_24h: rain_24h_value,
          rain_since_midnight: rain_since_midnight_value,
          luminosity: pkt.luminosity
        }
      end)
      |> Jason.encode!()

    socket =
      socket
      |> assign(:weather_packet, weather_packet)
      |> assign(:weather_history, weather_history)
      |> assign(:weather_history_json, weather_history_json)

    # Push event to update all charts with new data
    socket = push_event(socket, "update_weather_charts", %{weather_history: weather_history_json})

    {:noreply, socket}
  end

  # Keep the old handler for backward compatibility
  def handle_info({:postgres_packet, packet}, socket) do
    # Only update if the packet is for our callsign and contains weather data
    if packet_matches_callsign?(packet, socket.assigns.callsign) and has_weather_data?(packet) do
      # Refresh weather data when new weather packet arrives
      weather_packet = get_latest_weather_packet(socket.assigns.callsign)
      {start_time, end_time} = default_time_range()
      weather_history = get_weather_history(socket.assigns.callsign, start_time, end_time)

      # Get locale from socket assigns
      locale = Map.get(socket.assigns, :locale, "en")

      weather_history_json =
        weather_history
        |> Enum.map(fn pkt ->
          dew_point =
            if is_number(pkt.temperature) and is_number(pkt.humidity) do
              calc_dew_point(pkt.temperature, pkt.humidity)
            end

          # Convert units based on locale
          {temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
          {dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
          {wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
          {rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
          {rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
          {rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)

          %{
            timestamp: pkt.received_at,
            temperature: temp_value,
            dew_point: dew_value,
            humidity: pkt.humidity,
            pressure: pkt.pressure,
            wind_direction: pkt.wind_direction,
            wind_speed: wind_speed_value,
            rain_1h: rain_1h_value,
            rain_24h: rain_24h_value,
            rain_since_midnight: rain_since_midnight_value,
            luminosity: pkt.luminosity
          }
        end)
        |> Jason.encode!()

      socket =
        socket
        |> assign(:weather_packet, weather_packet)
        |> assign(:weather_history, weather_history)
        |> assign(:weather_history_json, weather_history_json)

      # Push event to update all charts with new data
      socket = push_event(socket, "update_weather_charts", %{weather_history: weather_history_json})

      {:noreply, socket}
    else
      {:noreply, socket}
    end
  end

  def handle_info(_message, socket), do: {:noreply, socket}

  defp packet_matches_callsign?(packet, callsign) do
    packet_sender = Map.get(packet, "sender") || Map.get(packet, :sender, "")
    String.upcase(packet_sender) == String.upcase(callsign)
  end

  defp has_weather_data?(packet) do
    # Check if packet contains any weather-related fields
    Enum.any?(Aprsme.EncodingUtils.weather_fields(), fn field ->
      value = Map.get(packet, field) || Map.get(packet, to_string(field))
      not is_nil(value)
    end)
  end

  defp get_latest_weather_packet(callsign) do
    # Get weather packets from the last 7 days to find the most recent one
    end_time = DateTime.utc_now()
    start_time = DateTime.add(end_time, -7 * 24 * 3600, :second)

    callsign
    |> Packets.get_weather_packets(start_time, end_time, %{limit: 1})
    |> List.first()
  end

  defp get_weather_history(callsign, start_time, end_time) do
    Packets.get_weather_packets(callsign, start_time, end_time, %{limit: 500})
  end

  defp default_time_range do
    now = DateTime.utc_now()
    {DateTime.add(now, -48 * 3600, :second), now}
  end

  defp calc_dew_point(temp, humidity) when is_number(temp) and is_number(humidity) do
    temp - (100 - humidity) / 5
  end

  defp calc_dew_point(_, _), do: nil

  @doc """
  Gets weather field value, returning "0" instead of "N/A" for missing numeric data.
  """
  def get_weather_field_zero(packet, key) do
    value = PacketUtils.get_weather_field(packet, key)

    # Return "0" for missing numeric fields, keep other values as-is
    case value do
      "N/A" -> "0"
      _ -> value
    end
  end

  @doc """
  Formats weather values with appropriate units based on locale.
  """
  def format_weather_value(packet, key, locale) do
    value = PacketUtils.get_weather_field(packet, key)

    case {key, value} do
      {:temperature, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_temperature(num_value, locale)
            "#{converted_value}#{unit}"

          :error ->
            value
        end

      {:wind_speed, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_wind_speed(num_value, locale)
            "#{converted_value} #{unit}"

          :error ->
            value
        end

      {:wind_gust, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_wind_speed(num_value, locale)
            "#{converted_value} #{unit}"

          :error ->
            value
        end

      {:rain_1h, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_rain(num_value, locale)
            "#{converted_value} #{unit}"

          :error ->
            value
        end

      {:rain_24h, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_rain(num_value, locale)
            "#{converted_value} #{unit}"

          :error ->
            value
        end

      {:rain_since_midnight, value} when is_binary(value) and value != "N/A" ->
        case Float.parse(value) do
          {num_value, _} ->
            {converted_value, unit} = WeatherUnits.format_rain(num_value, locale)
            "#{converted_value} #{unit}"

          :error ->
            value
        end

      _ ->
        case value do
          "N/A" -> "N/A"
          _ -> "#{value}"
        end
    end
  end
end
