defmodule MicrowavepropWeb.RoverPlanningLive.PathShow do
  @moduledoc """
  Renders one rover-planning Path's cached `result` map directly.
  The terrain analysis, baseline link budget, propagation score, and
  per-point profile (with beam + Fresnel-zone radius) all come from
  the worker-stored row — opening this page does no heavy compute.

  Live HRRR-driven conditions, scoring breakdown, sounding, and
  ionosphere readouts aren't cached (they're time-varying); a
  prominent "Recompute live" button hands off to `/path` for those.
  """
  use MicrowavepropWeb, :live_view

  import Ecto.Query

  alias Microwaveprop.Radio.Maidenhead
  alias Microwaveprop.Repo
  alias Microwaveprop.RoverPlanning.Mission
  alias Microwaveprop.RoverPlanning.Path

  @impl true
  def mount(%{"id" => mission_id, "path_id" => path_id}, _session, socket) do
    case load_path(mission_id, path_id) do
      nil ->
        {:ok,
         socket
         |> put_flash(:error, "Path profile not found.")
         |> push_navigate(to: ~p"/rover-planning/#{mission_id}")}

      %Path{} = path ->
        {:ok,
         assign(socket,
           page_title: "Path · #{path.mission.name}",
           path: path,
           mission: path.mission,
           rover: path.rover_location,
           station: path.station,
           profile_payload: profile_payload(path)
         )}
    end
  end

  defp load_path(mission_id, path_id) do
    with {:ok, mission_uuid} <- Ecto.UUID.cast(mission_id),
         {:ok, path_uuid} <- Ecto.UUID.cast(path_id) do
      Path
      |> where([p], p.id == ^path_uuid and p.mission_id == ^mission_uuid)
      |> preload([:rover_location, :station, :mission])
      |> Repo.one()
    else
      _ -> nil
    end
  end

  # Shape the cached path_points into the structure the ElevationProfile
  # JS hook expects. Renders the same earth/terrain/LOS/Fresnel chart
  # `/path` shows — feet on the Y-axis, miles on the X-axis — without
  # re-running TerrainAnalysis on every click.
  defp profile_payload(%Path{result: %{"path_points" => points}} = path) when is_list(points) and points != [] do
    %{
      points:
        Enum.map(points, fn p ->
          %{
            dist_km: p["dist_km"] || p["d"] || 0.0,
            elev: p["elev"] || 0.0,
            beam: p["beam"] || 0.0,
            r1: p["r1"] || 0.0,
            building_m: 0,
            canopy_m: 0
          }
        end),
      freq_mhz: path.band_mhz || path.mission.band_mhz,
      dist_km: path.result["distance_km"] || 0.0,
      verdict: path.result["verdict"],
      ducts: [],
      station1: station_callsign(path.rover_location),
      station2: station_callsign(path.station)
    }
  end

  defp profile_payload(_), do: nil

  defp station_callsign(%{callsign: c}) when is_binary(c) and c != "", do: c
  defp station_callsign(_), do: nil

  defp station_endpoint(%{callsign: c}) when is_binary(c) and c != "", do: c
  defp station_endpoint(%{grid: g}) when is_binary(g) and g != "", do: g

  defp station_endpoint(%{lat: lat, lon: lon}) when is_number(lat) and is_number(lon),
    do: Maidenhead.from_latlon(lat, lon, 8)

  defp station_endpoint(_), do: ""

  defp band_label(mhz) when is_integer(mhz) and mhz >= 1000 do
    ghz = mhz / 1000
    if ghz == trunc(ghz), do: "#{trunc(ghz)} GHz", else: "#{Float.round(ghz, 1)} GHz"
  end

  defp band_label(mhz) when is_integer(mhz), do: "#{mhz} MHz"
  defp band_label(_), do: "—"

  defp format_distance(km) when is_number(km), do: "#{Float.round(km * 1.0, 1)} km / #{Float.round(km * 0.621371, 1)} mi"

  defp format_distance(_), do: "—"

  defp format_db(nil), do: "—"
  defp format_db(v) when is_number(v), do: "#{Float.round(v * 1.0, 1)} dB"

  # Heights and clearances render in feet — meters are a curiosity for
  # microwave operators on the consumer side of this page.
  defp format_feet(nil), do: "—"
  defp format_feet(v) when is_number(v), do: "#{(v * 3.28084) |> Float.round(0) |> trunc()} ft"

  defp format_bearing(nil), do: "—"
  defp format_bearing(deg) when is_number(deg), do: "#{Float.round(deg * 1.0, 1)}°"

  defp verdict_label("CLEAR"), do: {"Clear", "badge badge-success"}
  defp verdict_label("BLOCKED"), do: {"Blocked", "badge badge-error"}
  defp verdict_label("FRESNEL_PARTIAL"), do: {"Fresnel partial", "badge badge-warning"}
  defp verdict_label("FRESNEL_MINOR"), do: {"Fresnel minor", "badge badge-info"}
  defp verdict_label(_), do: {"—", "badge"}

  defp live_path_url(%Mission{} = mission, %Path{} = path, rover, station) do
    src = Maidenhead.from_latlon(rover.lat, rover.lon, 10)
    dst = station_endpoint(station)
    band = path.band_mhz || mission.band_mhz

    query =
      URI.encode_query(%{
        "source" => src,
        "destination" => dst,
        "band" => Integer.to_string(band),
        "src_height_ft" => Float.to_string((mission.rover_height_ft || 8.0) * 1.0),
        "dst_height_ft" => Float.to_string((mission.station_height_ft || 30.0) * 1.0)
      })

    "/path?" <> query
  end

  @impl true
  def render(assigns) do
    {verdict_text, verdict_class} = verdict_label((assigns.path.result || %{})["verdict"])
    assigns = assign(assigns, verdict_text: verdict_text, verdict_class: verdict_class)

    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-5xl">
      <.header>
        Path profile
        <:subtitle>
          <span class="font-mono">{Maidenhead.from_latlon(@rover.lat, @rover.lon, 10)}</span>
          → <span class="font-mono">{station_endpoint(@station)}</span>
          · {band_label(@path.band_mhz || @mission.band_mhz)}
          <span class={"ml-2 #{@verdict_class}"}>{@verdict_text}</span>
        </:subtitle>
        <:actions>
          <.link navigate={~p"/rover-planning/#{@mission.id}"} class="btn btn-ghost btn-sm">
            <.icon name="hero-arrow-left" class="w-4 h-4" /> Back to mission
          </.link>
          <.link
            href={live_path_url(@mission, @path, @rover, @station)}
            class="btn btn-primary btn-sm"
          >
            <.icon name="hero-arrow-top-right-on-square" class="w-4 h-4" /> Recompute live
          </.link>
        </:actions>
      </.header>

      <div :if={!@path.result} class="alert alert-warning">
        Path is still {@path.status}. Refresh in a moment.
      </div>

      <div :if={@path.result} class="space-y-4">
        <%!-- Elevation profile chart — feet on Y, miles on X.
              Same JS hook /path uses (single data-profile attribute, not
              multiple data-* — the hook reads `el.dataset.profile`). --%>
        <div :if={@profile_payload} class="card bg-base-100 border border-base-300 p-4">
          <h3 class="font-semibold mb-2">Terrain profile</h3>
          <div
            id={"elevation-profile-#{@path.id}"}
            phx-hook="ElevationProfile"
            phx-update="ignore"
            data-profile={Jason.encode!(@profile_payload)}
            data-station1={@profile_payload.station1 || ""}
            data-station2={@profile_payload.station2 || ""}
            class="w-full h-48 md:h-56 rounded-box overflow-hidden bg-base-200 p-2"
          >
            <canvas></canvas>
          </div>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
          <div class="card bg-base-100 border border-base-300 p-4">
            <h3 class="font-semibold mb-2">Geometry</h3>
            <dl class="text-sm space-y-1">
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Distance</dt>
                <dd class="font-mono">{format_distance(@path.result["distance_km"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Bearing</dt>
                <dd class="font-mono">{format_bearing(@path.result["bearing_deg"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Min clearance</dt>
                <dd class="font-mono">{format_feet(@path.result["min_clearance_m"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Max elevation</dt>
                <dd class="font-mono">{format_feet(@path.result["max_elevation_m"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Rover antenna</dt>
                <dd class="font-mono">{@mission.rover_height_ft} ft</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Station antenna</dt>
                <dd class="font-mono">{@mission.station_height_ft} ft</dd>
              </div>
            </dl>
          </div>

          <div class="card bg-base-100 border border-base-300 p-4">
            <h3 class="font-semibold mb-2">Cached link budget</h3>
            <dl class="text-sm space-y-1">
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Free-space loss</dt>
                <dd class="font-mono">{format_db(@path.result["free_space_loss_db"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Oxygen absorption</dt>
                <dd class="font-mono">{format_db(@path.result["oxygen_loss_db"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">H₂O (baseline 7.5 g/m³)</dt>
                <dd class="font-mono">{format_db(@path.result["humidity_loss_db_baseline"])}</dd>
              </div>
              <div class="flex justify-between gap-2">
                <dt class="text-base-content/60">Diffraction</dt>
                <dd class="font-mono">{format_db(@path.result["diffraction_db"])}</dd>
              </div>
              <div class="flex justify-between gap-2 font-semibold border-t border-base-300 pt-1 mt-1">
                <dt>Total baseline</dt>
                <dd class="font-mono">{format_db(@path.result["total_baseline_loss_db"])}</dd>
              </div>
            </dl>
            <p class="text-xs text-base-content/60 mt-2">
              Recompute live for HRRR-driven humidity / rain.
            </p>
          </div>
        </div>

        <div class="card bg-base-100 border border-base-300 p-4">
          <h3 class="font-semibold mb-2">Propagation forecast</h3>
          <p class="text-sm">
            Cached score (midpoint, mission band):
            <span class="font-mono ml-1">
              {(@path.result["propagation_score"] || "—") |> to_string()}
            </span>
            <span class="text-base-content/60 ml-1">/ 100</span>
          </p>
          <p class="text-xs text-base-content/60 mt-1">
            Computed at {(@path.computed_at &&
                            Calendar.strftime(@path.computed_at, "%Y-%m-%d %H:%M UTC")) || "—"}.
            For current weather conditions, scoring breakdown, sounding,
            and ionosphere readouts, click <strong>Recompute live</strong>.
          </p>
        </div>
      </div>
    </Layouts.app>
    """
  end
end
