defmodule ToweropsWeb.Admin.MonitoringLive do
  @moduledoc """
  Real-time job monitoring dashboard for Oban workers.

  Displays:
  - Active operations (currently executing jobs)
  - Problems (stuck/failed jobs)
  - Health metrics (throughput, errors, latency)
  - Recent activity timeline
  """
  use ToweropsWeb, :live_view

  alias Phoenix.LiveView.Socket
  alias Towerops.JobMonitoring
  alias Towerops.JobMonitoring.Metrics

  @topic "job:lifecycle"

  @impl Phoenix.LiveView
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(Towerops.PubSub, @topic)
    end

    socket =
      socket
      |> assign(:page_title, "Job Monitoring")
      |> assign(:active_jobs, [])
      |> assign(:executing_jobs, [])
      |> assign(:stuck_jobs, [])
      |> assign(:stuck_jobs_count, 0)
      |> assign(:failed_jobs, [])
      |> assign(:failed_jobs_count, 0)
      |> assign(:recent_jobs, [])
      |> assign(:recent_events, [])
      |> assign(:metrics, %{})
      |> assign(:health_metrics, %{})
      |> load_monitoring_data()

    {:ok, socket}
  end

  @impl Phoenix.LiveView
  def handle_info(%{event: :started} = event, socket) do
    # Job started - add to active jobs list
    active_jobs = [event | socket.assigns.active_jobs]
    {:noreply, assign(socket, :active_jobs, active_jobs)}
  end

  def handle_info(%{event: event} = job_event, socket) when event in [:completed, :failed] do
    # Job finished - remove from active jobs, reload data
    active_jobs = Enum.reject(socket.assigns.active_jobs, fn job -> job.job_id == job_event.job_id end)

    socket =
      socket
      |> assign(:active_jobs, active_jobs)
      |> load_monitoring_data()

    {:noreply, socket}
  end

  def handle_info(_event, socket) do
    {:noreply, socket}
  end

  @spec load_monitoring_data(Socket.t()) :: Socket.t()
  defp load_monitoring_data(socket) do
    executing_jobs = JobMonitoring.list_active_jobs()
    stuck_jobs = JobMonitoring.list_stuck_jobs()
    failed_jobs = JobMonitoring.list_failed_jobs()
    recent_jobs = JobMonitoring.list_recent_completions(20)
    metrics = Metrics.calculate_all()

    socket
    |> assign(:executing_jobs, executing_jobs)
    |> assign(:stuck_jobs, stuck_jobs)
    |> assign(:stuck_jobs_count, length(stuck_jobs))
    |> assign(:failed_jobs, failed_jobs)
    |> assign(:failed_jobs_count, length(failed_jobs))
    |> assign(:recent_jobs, recent_jobs)
    |> assign(:recent_events, recent_jobs)
    |> assign(:metrics, metrics)
    |> assign(:health_metrics, metrics)
  end

  defp worker_name("Towerops.Workers.DevicePollerWorker"), do: "Device Poll"
  defp worker_name("Towerops.Workers.DiscoveryWorker"), do: "SNMP Discovery"
  defp worker_name(worker), do: worker

  defp duration_in_words(started_at) do
    seconds = DateTime.diff(DateTime.utc_now(), started_at)
    minutes = div(seconds, 60)

    cond do
      minutes < 1 -> "#{seconds}s"
      minutes < 60 -> "#{minutes}m"
      true -> "#{div(minutes, 60)}h #{rem(minutes, 60)}m"
    end
  end

  defp event_border_color("completed"), do: "border-green-500"
  defp event_border_color("cancelled"), do: "border-red-500"
  defp event_border_color("discarded"), do: "border-gray-400"
  defp event_border_color(_), do: "border-gray-300"

  defp event_outcome(job) do
    case job.state do
      "completed" -> "Completed"
      "cancelled" -> "Failed"
      "discarded" -> "Discarded"
      _ -> String.capitalize(job.state)
    end
  end
end
