defmodule ToweropsWeb.AgentLive.Show do
  @moduledoc false
  use ToweropsWeb, :live_view

  import ToweropsWeb.AgentLive.Helpers

  alias Towerops.Agents
  alias Towerops.Agents.ReleaseChecker

  @impl true
  def mount(%{"id" => id}, _session, socket) do
    organization = socket.assigns.current_scope.organization
    agent_token = Agents.get_agent_token!(id)
    current_user = socket.assigns.current_scope.user

    # Verify access: agent must belong to this org, or superadmin can view any agent
    has_access =
      agent_token.organization_id == organization.id ||
        current_user.is_superuser

    if !has_access do
      raise Ecto.NoResultsError, queryable: Towerops.Agents.AgentToken
    end

    # device this agent is responsible for)
    polling_targets = Agents.list_agent_polling_targets(agent_token.id)

    # Get direct assignments
    direct_assignments = Agents.count_assigned_devices(agent_token.id)

    {:ok,
     socket
     |> assign(:page_title, agent_token.name)
     |> assign(:timezone, socket.assigns.current_scope.timezone)
     |> assign(:agent_token, agent_token)
     |> assign(:polling_targets, polling_targets)
     |> assign(:direct_assignments, direct_assignments)
     |> assign(:active_tab, "overview")}
  end

  @impl true
  def handle_params(_params, _url, socket) do
    {:noreply, socket}
  end

  @impl true
  def handle_event("switch_tab", %{"tab" => tab}, socket) do
    {:noreply, assign(socket, :active_tab, tab)}
  end

  def handle_event("restart_agent", _params, socket) do
    if socket.assigns.current_scope.user.is_superuser do
      Agents.restart_agent(socket.assigns.agent_token.id)

      {:noreply, put_flash(socket, :info, "Restart command sent to agent")}
    else
      {:noreply, socket}
    end
  end

  def handle_event("update_agent", _params, socket) do
    if socket.assigns.current_scope.user.is_superuser do
      agent_token = socket.assigns.agent_token
      arch = get_in(agent_token.metadata, ["arch"]) || "x86_64"

      {:noreply, send_update_command(socket, agent_token, arch)}
    else
      {:noreply, socket}
    end
  end

  defp send_update_command(socket, agent_token, arch) do
    with {:ok, release} <- ReleaseChecker.get_latest_release(),
         {:ok, asset} <- ReleaseChecker.asset_for_arch(release.assets, arch) do
      Agents.update_agent(agent_token.id, asset.url, asset.checksum)
      put_flash(socket, :info, "Update command sent to agent (v#{release.version})")
    else
      {:error, :no_matching_asset} ->
        put_flash(socket, :error, "No binary available for architecture: #{arch}")

      {:error, _reason} ->
        put_flash(socket, :error, "Failed to fetch latest release from GitHub")
    end
  end

  defp assignment_source(device, agent_token_id) do
    cond do
      directly_assigned?(device, agent_token_id) ->
        {:direct, "Directly assigned"}

      site_assigned?(device, agent_token_id) ->
        {:site, "From site: #{device.site.name}"}

      organization_assigned?(device, agent_token_id) ->
        {:organization, "From organization"}

      true ->
        {:unknown, "Unknown"}
    end
  end

  defp directly_assigned?(device, agent_token_id) do
    Enum.any?(device.agent_assignments || [], fn a ->
      a.agent_token_id == agent_token_id and a.enabled
    end)
  end

  defp site_assigned?(device, agent_token_id) do
    device.site && device.site.agent_token_id == agent_token_id
  end

  defp organization_assigned?(device, agent_token_id) do
    device.site && device.site.organization &&
      device.site.organization.default_agent_token_id == agent_token_id
  end

  defp source_badge_class(source) do
    case source do
      :direct -> "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"
      :site -> "bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300"
      :organization -> "bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-300"
      :unknown -> "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300"
    end
  end
end
