defmodule MicrowavepropWeb.RoverLocationsLive.Show do
  @moduledoc "Detail page for a single rover location, with a map + marker."
  use MicrowavepropWeb, :live_view

  alias Microwaveprop.Accounts.User
  alias Microwaveprop.Radio.Maidenhead
  alias Microwaveprop.Repo
  alias Microwaveprop.Rover
  alias Microwaveprop.Rover.Location

  @impl true
  def mount(%{"id" => id}, _session, socket) do
    case load_location(id) do
      %Location{} = loc ->
        {:ok,
         socket
         |> assign(
           page_title: "Rover Location",
           location: loc,
           editing: false,
           status_options: status_options()
         )
         |> assign_working_fields(loc)}

      nil ->
        {:ok,
         socket
         |> put_flash(:error, "Location not found.")
         |> push_navigate(to: ~p"/rover-locations")}
    end
  end

  @impl true
  def handle_event("toggle_edit", _params, socket) do
    if can_modify?(socket.assigns[:current_scope], socket.assigns.location) do
      {:noreply,
       socket
       |> assign(editing: true)
       |> push_event("set_marker_draggable", %{draggable: true})}
    else
      {:noreply, put_flash(socket, :error, "You can only edit your own locations.")}
    end
  end

  def handle_event("cancel_edit", _params, socket) do
    %Location{lat: lat, lon: lon} = location = socket.assigns.location

    {:noreply,
     socket
     |> assign_working_fields(location)
     |> assign(editing: false)
     |> push_event("reset_marker", %{lat: lat, lon: lon})
     |> push_event("set_marker_draggable", %{draggable: false})}
  end

  # Hook fires this on `dragend` with the marker's new coordinates.
  # We only update the working preview — persistence waits for save.
  def handle_event("location_dragged", %{"lat" => lat, "lon" => lon}, socket) when is_number(lat) and is_number(lon) do
    {:noreply, assign_working_coords(socket, lat, lon)}
  end

  def handle_event("update_form", %{"location" => params}, socket) do
    {:noreply,
     assign(socket,
       working_status: parse_status(Map.get(params, "status"), socket.assigns.working_status),
       working_notes: Map.get(params, "notes", socket.assigns.working_notes)
     )}
  end

  def handle_event("save_edit", _params, socket) do
    %{
      working_lat: lat,
      working_lon: lon,
      working_status: status,
      working_notes: notes,
      location: %Location{id: id}
    } = socket.assigns

    attrs = %{lat: lat, lon: lon, status: status, notes: notes}

    with {:ok, user} <- authenticated(socket),
         {:ok, updated} <- Rover.update_location(user, id, attrs) do
      updated = Repo.preload(updated, :user)

      {:noreply,
       socket
       |> assign(location: updated, editing: false)
       |> assign_working_fields(updated)
       |> put_flash(:info, "Location updated.")
       |> push_event("set_marker_draggable", %{draggable: false})}
    else
      {:error, :unauthenticated} -> {:noreply, put_flash(socket, :error, "Sign in required.")}
      {:error, :not_found} -> {:noreply, put_flash(socket, :error, "You can only edit your own locations.")}
      {:error, %Ecto.Changeset{}} -> {:noreply, put_flash(socket, :error, "Could not save those coordinates.")}
    end
  end

  def handle_event("delete", _params, socket) do
    with {:ok, user} <- authenticated(socket),
         {:ok, _} <- Rover.delete_location(user, socket.assigns.location.id) do
      {:noreply,
       socket
       |> put_flash(:info, "Location removed.")
       |> push_navigate(to: ~p"/rover-locations")}
    else
      {:error, :unauthenticated} -> {:noreply, put_flash(socket, :error, "Sign in required.")}
      {:error, :not_found} -> {:noreply, put_flash(socket, :error, "You can only delete your own locations.")}
    end
  end

  defp assign_working_coords(socket, lat, lon) do
    assign(socket,
      working_lat: lat,
      working_lon: lon,
      grid: Maidenhead.from_latlon(lat, lon, 10)
    )
  end

  defp assign_working_fields(socket, %Location{lat: lat, lon: lon, status: status, notes: notes}) do
    socket
    |> assign_working_coords(lat, lon)
    |> assign(working_status: status, working_notes: notes || "")
  end

  defp status_options, do: [{"Good", "good"}, {"Bad", "bad"}]

  defp parse_status("good", _fallback), do: :good
  defp parse_status("bad", _fallback), do: :bad
  defp parse_status(_, fallback), do: fallback

  defp load_location(id) do
    case Ecto.UUID.cast(id) do
      {:ok, uuid} -> Location |> Repo.get(uuid) |> Repo.preload(:user)
      :error -> nil
    end
  end

  defp authenticated(%Phoenix.LiveView.Socket{assigns: %{current_scope: %{user: %User{} = user}}}), do: {:ok, user}

  defp authenticated(_), do: {:error, :unauthenticated}

  defp can_modify?(%{user: %User{is_admin: true}}, _), do: true

  defp can_modify?(%{user: %User{id: id}}, %Location{user_id: id}) when not is_nil(id), do: true

  defp can_modify?(_, _), do: false

  defp status_label(:good), do: "Good"
  defp status_label(:bad), do: "Bad"
  defp status_label(_), do: ""

  defp status_class(:good), do: "badge badge-success"
  defp status_class(:bad), do: "badge badge-error"
  defp status_class(_), do: "badge"

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope} max_width="max-w-5xl">
      <.header>
        Rover Location
        <:subtitle>
          <span class={status_class(@location.status)}>{status_label(@location.status)}</span>
          {@grid}
          <span :if={@editing} class="ml-2 badge badge-warning">Editing — drag the marker</span>
        </:subtitle>
        <:actions>
          <.link navigate={~p"/rover-locations"} class="btn btn-ghost btn-sm">
            <.icon name="hero-arrow-left" class="w-4 h-4" /> Back
          </.link>

          <button
            :if={can_modify?(@current_scope, @location) and not @editing}
            type="button"
            phx-click="toggle_edit"
            class="btn btn-ghost btn-sm"
          >
            <.icon name="hero-pencil-square" class="w-4 h-4" /> Edit
          </button>

          <button
            :if={@editing}
            type="button"
            phx-click="save_edit"
            class="btn btn-primary btn-sm"
          >
            <.icon name="hero-check" class="w-4 h-4" /> Save
          </button>

          <button
            :if={@editing}
            type="button"
            phx-click="cancel_edit"
            class="btn btn-ghost btn-sm"
          >
            Cancel
          </button>

          <button
            :if={can_modify?(@current_scope, @location) and not @editing}
            type="button"
            phx-click="delete"
            data-confirm="Delete this location?"
            class="btn btn-ghost btn-sm text-error"
          >
            <.icon name="hero-trash" class="w-4 h-4" /> Delete
          </button>
        </:actions>
      </.header>

      <div class="card bg-base-100 border border-base-300 p-4 mb-4">
        <dl class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-sm">
          <div>
            <dt class="text-base-content/60">Grid</dt>
            <dd class="font-mono">{@grid}</dd>
          </div>
          <div>
            <dt class="text-base-content/60">Coordinates</dt>
            <dd class="font-mono">
              {Float.round(@working_lat, 6)}, {Float.round(@working_lon, 6)}
            </dd>
          </div>
          <div>
            <dt class="text-base-content/60">Status</dt>
            <dd>
              <span class={status_class(@working_status)}>
                {status_label(@working_status)}
              </span>
            </dd>
          </div>
          <div>
            <dt class="text-base-content/60">Added</dt>
            <dd>{Calendar.strftime(@location.inserted_at, "%Y-%m-%d %H:%M UTC")}</dd>
          </div>
          <div :if={@location.user}>
            <dt class="text-base-content/60">Submitted by</dt>
            <dd>
              <.link
                navigate={~p"/u/#{@location.user.callsign}"}
                class="link link-primary font-mono"
              >
                {@location.user.callsign}
              </.link>
            </dd>
          </div>
        </dl>

        <div
          :if={(not @editing and @location.notes) && @location.notes != ""}
          class="mt-4"
        >
          <h3 class="text-sm text-base-content/60 mb-1">Notes</h3>
          <p class="whitespace-pre-line text-sm">{@location.notes}</p>
        </div>

        <form
          :if={@editing}
          id="location-edit-form"
          phx-change="update_form"
          phx-submit="save_edit"
          class="mt-4 space-y-3"
        >
          <div>
            <label class="label" for="location-status">
              <span class="label-text text-sm text-base-content/60">Status</span>
            </label>
            <select
              id="location-status"
              name="location[status]"
              class="select select-bordered select-sm w-full max-w-xs"
            >
              <option
                :for={{label, value} <- @status_options}
                value={value}
                selected={to_string(@working_status) == value}
              >
                {label}
              </option>
            </select>
          </div>
          <div>
            <label class="label" for="location-notes">
              <span class="label-text text-sm text-base-content/60">Notes</span>
            </label>
            <textarea
              id="location-notes"
              name="location[notes]"
              rows="3"
              class="textarea textarea-bordered w-full"
            >{@working_notes}</textarea>
          </div>
        </form>
      </div>

      <div
        id={"location-map-#{@location.id}"}
        phx-hook="LocationMap"
        phx-update="ignore"
        data-lat={@location.lat}
        data-lon={@location.lon}
        class="h-[70vh] rounded border border-base-300 z-0"
      >
      </div>
    </Layouts.app>
    """
  end
end
