defmodule ToweropsWeb.TraceLive.Index do
  @moduledoc """
  LiveView for the Subscriber Trace / Help Desk Mode.

  Provides a unified search across subscribers, devices, and access points,
  then assembles a full "patient chart" trace view showing all relevant
  network health information at a glance.
  """
  use ToweropsWeb, :live_view

  alias Towerops.Trace

  attr :trace, :map, required: true

  defp issues_callout(assigns) do
    issues = []
    issues = if assigns.trace[:device] && assigns.trace.device[:status] == :down, do: ["Device is DOWN" | issues], else: issues
    issues = if assigns.trace[:qoe_metrics] && assigns.trace.qoe_metrics[:qoe_score] && assigns.trace.qoe_metrics.qoe_score < 50, do: ["QoE score is critical (#{assigns.trace.qoe_metrics.qoe_score})" | issues], else: issues
    issues = if assigns.trace[:active_alerts] && length(assigns.trace.active_alerts) > 0, do: ["#{length(assigns.trace.active_alerts)} active alert(s)" | issues], else: issues
    assigns = assign(assigns, :issues, Enum.reverse(issues))

    ~H"""
    <div :if={@issues != []} class="alert alert-warning shadow-sm">
      <.icon name="hero-exclamation-triangle" class="h-5 w-5" />
      <div>
        <h3 class="font-bold">Issues Detected</h3>
        <ul class="text-sm mt-1">
          <li :for={issue <- @issues}>• {issue}</li>
        </ul>
      </div>
    </div>
    """
  end

  attr :label, :string, required: true
  attr :value, :string, required: true
  attr :quality, :atom, default: :unknown

  defp metric_card(assigns) do
    color = case assigns.quality do
      :good -> "border-green-200 dark:border-green-800"
      :warning -> "border-yellow-200 dark:border-yellow-800"
      :bad -> "border-red-200 dark:border-red-800"
      _ -> "border-base-200"
    end
    assigns = assign(assigns, :color, color)

    ~H"""
    <div class={"stat bg-base-100 rounded-box border #{@color} p-4"}>
      <div class="stat-title text-xs">{@label}</div>
      <div class="stat-value text-2xl">{@value}</div>
    </div>
    """
  end

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     socket
     |> assign(:page_title, "Trace")
     |> assign(:query, "")
     |> assign(:results, [])
     |> assign(:trace, nil)
     |> assign(:trace_type, nil)
     |> assign(:trace_id, nil)
     |> assign(:searching, false)}
  end

  @impl true
  def handle_params(params, _url, socket) do
    organization = socket.assigns.current_scope.organization

    socket =
      case params do
        %{"type" => type, "id" => id} ->
          type_atom = safe_type(type)
          trace = Trace.assemble_trace(organization.id, type_atom, id)

          socket
          |> assign(:trace, trace)
          |> assign(:trace_type, type_atom)
          |> assign(:trace_id, id)

        _ ->
          socket
      end

    {:noreply, socket}
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    organization = socket.assigns.current_scope.organization

    results =
      if String.trim(query) != "" do
        Trace.search(organization.id, query)
      else
        []
      end

    {:noreply,
     socket
     |> assign(:query, query)
     |> assign(:results, results)
     |> assign(:trace, nil)
     |> assign(:trace_type, nil)
     |> assign(:trace_id, nil)}
  end

  @impl true
  def handle_event("select_result", %{"type" => type, "id" => id}, socket) do
    {:noreply, push_patch(socket, to: ~p"/trace?type=#{type}&id=#{id}")}
  end

  @impl true
  def handle_event("clear", _params, socket) do
    {:noreply,
     socket
     |> assign(:query, "")
     |> assign(:results, [])
     |> assign(:trace, nil)
     |> assign(:trace_type, nil)
     |> assign(:trace_id, nil)
     |> push_patch(to: ~p"/trace")}
  end

  defp type_badge_class(:account), do: "badge-primary"
  defp type_badge_class(:inventory_item), do: "badge-secondary"
  defp type_badge_class(:device), do: "badge-accent"
  defp type_badge_class(:access_point), do: "badge-info"
  defp type_badge_class(_), do: "badge-ghost"

  defp type_badge_label(:account), do: "Account"
  defp type_badge_label(:inventory_item), do: "Inventory"
  defp type_badge_label(:device), do: "Device"
  defp type_badge_label(:access_point), do: "AP"
  defp type_badge_label(_), do: "Unknown"

  defp status_badge_class("active"), do: "badge-success"
  defp status_badge_class("suspended"), do: "badge-warning"
  defp status_badge_class("cancelled"), do: "badge-error"
  defp status_badge_class(_), do: "badge-ghost"

  defp device_status_color(:up), do: "text-green-600 dark:text-green-400"
  defp device_status_color(:down), do: "text-red-600 dark:text-red-400"
  defp device_status_color(_), do: "text-gray-500"

  defp format_speed(nil), do: "—"
  defp format_speed(speed) when is_number(speed) and speed >= 1000, do: "#{Float.round(speed / 1000, 1)} Gbps"
  defp format_speed(speed) when is_number(speed), do: "#{speed} Mbps"
  defp format_speed(speed), do: "#{speed}"

  defp format_relative_time(nil), do: "—"
  defp format_relative_time(dt) do
    diff = DateTime.diff(DateTime.utc_now(), dt, :second)
    cond do
      diff < 60 -> "just now"
      diff < 3600 -> "#{div(diff, 60)}m ago"
      diff < 86400 -> "#{div(diff, 3600)}h ago"
      true -> "#{div(diff, 86400)}d ago"
    end
  end

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

  defp format_pct(nil), do: "—"
  defp format_pct(val) when is_number(val), do: "#{Float.round(val * 1.0, 1)}%"
  defp format_pct(val), do: "#{val}"

  defp format_ms(nil), do: "—"
  defp format_ms(val) when is_number(val), do: "#{Float.round(val * 1.0, 1)} ms"
  defp format_ms(val), do: "#{val}"

  defp format_throughput(nil), do: "—"
  defp format_throughput(val) when is_number(val) and val >= 1000, do: "#{Float.round(val / 1000, 1)} Gbps"
  defp format_throughput(val) when is_number(val), do: "#{Float.round(val * 1.0, 1)} Mbps"
  defp format_throughput(val), do: "#{val}"

  defp format_alert_type(nil), do: "—"
  defp format_alert_type(type) when is_binary(type), do: type |> String.replace("_", " ") |> String.capitalize()
  defp format_alert_type(type), do: "#{type}"

  defp score_quality(nil), do: :unknown
  defp score_quality(s) when s >= 80, do: :good
  defp score_quality(s) when s >= 50, do: :warning
  defp score_quality(_), do: :bad

  defp score_color(nil), do: "text-gray-400"
  defp score_color(s) when s >= 80, do: "text-green-600 dark:text-green-400"
  defp score_color(s) when s >= 50, do: "text-yellow-600 dark:text-yellow-400"
  defp score_color(_), do: "text-red-600 dark:text-red-400"

  defp latency_color(nil), do: "text-gray-400"
  defp latency_color(v) when v <= 30, do: "text-green-600 dark:text-green-400"
  defp latency_color(v) when v <= 80, do: "text-yellow-600 dark:text-yellow-400"
  defp latency_color(_), do: "text-red-600 dark:text-red-400"

  defp jitter_color(nil), do: "text-gray-400"
  defp jitter_color(v) when v <= 10, do: "text-green-600 dark:text-green-400"
  defp jitter_color(v) when v <= 30, do: "text-yellow-600 dark:text-yellow-400"
  defp jitter_color(_), do: "text-red-600 dark:text-red-400"

  defp loss_color(nil), do: "text-gray-400"
  defp loss_color(v) when v <= 1, do: "text-green-600 dark:text-green-400"
  defp loss_color(v) when v <= 5, do: "text-yellow-600 dark:text-yellow-400"
  defp loss_color(_), do: "text-red-600 dark:text-red-400"

  defp device_status_dot(:up), do: "bg-green-500"
  defp device_status_dot(:down), do: "bg-red-500"
  defp device_status_dot(_), do: "bg-gray-400"

  defp alert_dot(%{severity: "critical"}), do: "bg-red-500"
  defp alert_dot(%{severity: "warning"}), do: "bg-yellow-500"
  defp alert_dot(%{alert_type: "down"}), do: "bg-red-500"
  defp alert_dot(_), do: "bg-orange-400"

  defp urgency_dot("critical"), do: "bg-red-500"
  defp urgency_dot("high"), do: "bg-orange-500"
  defp urgency_dot("medium"), do: "bg-yellow-500"
  defp urgency_dot(_), do: "bg-blue-400"

  defp airtime_quality(nil), do: :unknown
  defp airtime_quality(v) when v <= 50, do: :good
  defp airtime_quality(v) when v <= 80, do: :warning
  defp airtime_quality(_), do: :bad

  defp safe_type("account"), do: :account
  defp safe_type("inventory_item"), do: :inventory_item
  defp safe_type("device"), do: :device
  defp safe_type("access_point"), do: :access_point
  defp safe_type(_), do: :account
end
