defmodule MicrowavepropWeb.ImportLive do
  @moduledoc """
  Shows live progress for an async CSV/ADIF contact import.

  Mounts on `/imports/:id`, loads the `ImportRun`, and subscribes to the
  `"csv_import:<id>"` PubSub topic so counters update as
  `ContactImportWorker` chunks finish.
  """
  use MicrowavepropWeb, :live_view

  alias Microwaveprop.Radio.ImportRun
  alias Microwaveprop.Repo

  @pubsub Microwaveprop.PubSub

  @impl true
  def mount(%{"id" => id}, _session, socket) do
    case load_run(id) do
      nil ->
        {:ok,
         socket
         |> put_flash(:error, "Import not found.")
         |> push_navigate(to: ~p"/submit")}

      %ImportRun{} = run ->
        if connected?(socket) do
          Phoenix.PubSub.subscribe(@pubsub, "csv_import:#{run.id}")
        end

        {:ok,
         socket
         |> assign(:page_title, "Import ##{String.slice(run.id, 0, 8)}")
         |> assign(:run, run)}
    end
  end

  @impl true
  def handle_info({:import_progress, _id, %ImportRun{} = updated}, socket) do
    {:noreply, assign(socket, :run, updated)}
  end

  defp load_run(id) do
    if valid_uuid?(id), do: Repo.get(ImportRun, id)
  end

  defp valid_uuid?(id) when is_binary(id) do
    case Ecto.UUID.cast(id) do
      {:ok, _} -> true
      :error -> false
    end
  end

  defp valid_uuid?(_), do: false

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope}>
      <.header>
        Contact Import
        <:subtitle>
          Submitted by <span class="font-mono">{@run.submitter_email}</span>
          &middot; {@run.total_rows} {pluralize(@run.total_rows, "row")}
        </:subtitle>
        <:actions>
          <span id="import-status-badge" class={["badge badge-lg", status_badge_class(@run.status)]}>
            {status_label(@run.status)}
          </span>
        </:actions>
      </.header>

      <div class="space-y-6">
        <div>
          <div class="flex items-center justify-between text-sm mb-1 tabular-nums">
            <span>
              {@run.processed_rows} / {@run.total_rows} processed
            </span>
            <span>{progress_percent(@run)}%</span>
          </div>
          <progress
            id="import-progress-bar"
            class={["progress w-full", progress_class(@run.status)]}
            value={@run.processed_rows}
            max={max(@run.total_rows, 1)}
          >
          </progress>
        </div>

        <div class="stats stats-vertical sm:stats-horizontal shadow w-full">
          <div class="stat">
            <div class="stat-title">Imported</div>
            <div id="import-imported-count" class="stat-value text-success tabular-nums">
              {@run.imported_count}
            </div>
            <div class="stat-desc">New contacts inserted</div>
          </div>
          <div class="stat">
            <div class="stat-title">Refined</div>
            <div id="import-refined-count" class="stat-value text-info tabular-nums">
              {@run.refined_count}
            </div>
            <div class="stat-desc">Existing contacts updated</div>
          </div>
          <div class="stat">
            <div class="stat-title">Errors</div>
            <div id="import-error-count" class="stat-value text-error tabular-nums">
              {@run.error_count}
            </div>
            <div class="stat-desc">Rows that failed at insert</div>
          </div>
        </div>

        <div
          :if={@run.status == "complete"}
          id="import-complete-panel"
          class="alert alert-success"
          phx-hook="ImportConfetti"
        >
          <.icon name="hero-check-circle" class="w-6 h-6" />
          <div>
            <div class="font-semibold">Import complete</div>
            <div class="text-sm opacity-90">
              Enrichment (weather, terrain, propagation) will continue in the background.
            </div>
          </div>
        </div>

        <div
          :if={@run.status == "failed"}
          id="import-failed-panel"
          class="alert alert-error"
        >
          <.icon name="hero-exclamation-triangle" class="w-6 h-6" />
          <span>Import failed before processing all rows.</span>
        </div>

        <details
          :if={@run.error_count > 0}
          id="import-errors-section"
          class="rounded-box border border-base-300"
        >
          <summary class="cursor-pointer p-4 font-semibold">
            {@run.error_count} {pluralize(@run.error_count, "row")} with errors
          </summary>
          <div class="overflow-x-auto p-4 pt-0">
            <table class="table table-sm">
              <thead>
                <tr>
                  <th class="w-24">Row</th>
                  <th>Errors</th>
                </tr>
              </thead>
              <tbody>
                <tr :for={{row_num, messages} <- sorted_errors(@run.errors)}>
                  <td class="font-mono">Row {row_num}</td>
                  <td>
                    <div :for={msg <- messages}>{msg}</div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </details>

        <div class="flex flex-wrap gap-2 pt-4 border-t border-base-300">
          <.link navigate={~p"/submit"} class="btn btn-outline">
            <.icon name="hero-arrow-left" class="w-4 h-4" /> Upload another
          </.link>
          <.link
            :if={@run.status == "complete"}
            navigate={~p"/contacts"}
            class="btn btn-primary"
          >
            View contacts <.icon name="hero-arrow-right" class="w-4 h-4" />
          </.link>
        </div>
      </div>
    </Layouts.app>
    """
  end

  defp status_label("pending"), do: "Pending"
  defp status_label("running"), do: "Running"
  defp status_label("complete"), do: "Complete"
  defp status_label("failed"), do: "Failed"
  defp status_label(other), do: to_string(other)

  defp status_badge_class("pending"), do: "badge-neutral"
  defp status_badge_class("running"), do: "badge-info"
  defp status_badge_class("complete"), do: "badge-success"
  defp status_badge_class("failed"), do: "badge-error"
  defp status_badge_class(_), do: "badge-neutral"

  defp progress_class("complete"), do: "progress-success"
  defp progress_class("failed"), do: "progress-error"
  defp progress_class("running"), do: "progress-info"
  defp progress_class(_), do: "progress-primary"

  defp progress_percent(%ImportRun{total_rows: 0}), do: 0

  defp progress_percent(%ImportRun{total_rows: total, processed_rows: processed}) do
    min(100, round(processed * 100 / total))
  end

  defp pluralize(1, word), do: word
  defp pluralize(_, word), do: word <> "s"

  # `errors` is a JSONB map of "row_num" (string) → [messages]. Sort by
  # numeric row number for display; fall back to string order if a key
  # can't be parsed as an integer.
  defp sorted_errors(nil), do: []

  defp sorted_errors(errors) when is_map(errors) do
    errors
    |> Enum.map(fn {row_num, messages} -> {row_num, List.wrap(messages)} end)
    |> Enum.sort_by(fn {row_num, _} ->
      case Integer.parse(to_string(row_num)) do
        {n, _} -> n
        :error -> :infinity
      end
    end)
  end
end
