defmodule PrymnWeb.ServerLive.Show do
  use PrymnWeb, :live_view

  require Logger
  alias Prymn.{Agents, Servers}

  @impl true
  def mount(_params, _session, socket) do
    # TODO: A more lightweight call instead of listing all data?
    servers = Servers.list_servers()
    {:ok, assign(socket, :servers, servers)}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="mx-auto max-w-2xl">
      <div class="flex justify-between">
        <div id="server-name" class="relative flex items-center">
          <.dropdown title="Select a different server">
            <:button>Server <%= @server.name %></:button>
            <:item :for={server <- Enum.filter(@servers, fn s -> s.id != @server.id end)}>
              <.link
                patch={~p"/servers/#{server}"}
                class="block text-sm text-gray-700"
                role="menuitem"
              >
                <%= server.name %>
              </.link>
            </:item>
          </.dropdown>
          <button
            class="ml-4 inline-flex items-center"
            title="Edit server name"
            phx-click={show_edit_server_name()}
          >
            <.icon class="h-4 w-4" name="hero-pencil-solid" />
          </button>
          <.indicator message={@health.message} />
        </div>
        <form class="hidden" id="server-name-edit" phx-submit={submit_edit_server_name()}>
          <input
            class="border-0 border-b border-gray-500"
            type="text"
            name="name"
            value={@server.name}
            required
          />
          <button type="submit" title="Confirm">
            <.icon name="hero-check" />
          </button>
        </form>
        <div class="space-x-2">
          <Button.primary size="sm">New App</Button.primary>
          <.dropdown>
            <:button>
              Quick actions
            </:button>
            <:item>
              Test
            </:item>
          </.dropdown>
        </div>
      </div>
      <span class="text-sm opacity-75"><%= @server.public_ip %></span>
      <div :for={{name, task} <- @health.tasks} class="my-3 text-sm text-slate-700">
        <p>Background task in progress: <%= name %></p>
        <p><%= task.progress %> complete</p>
      </div>
      <div :if={@server.status == :unregistered} class="my-10">
        <p class="mb-9">
          Connect to your server using root credentials and execute the following command:
        </p>
        <div class="group inline-flex items-center rounded-lg bg-gray-800 p-4 pl-6 text-white">
          <code class="flex gap-4">
            <span class="select-none text-gray-500">#</span>
            <span class="flex-1">
              <%= @registration_command %>
            </span>
          </code>
          <button type="button" tabindex="-1">
            <.icon
              name="hero-document-duplicate-solid"
              class="invisible ml-4 animate-bounce text-gray-500 group-hover:visible"
            />
          </button>
        </div>
      </div>
      <div :if={@server.status == :registered} class="my-10">
        <.live_component
          id={"system_info-#{@server.name}"}
          module={PrymnWeb.SystemInfo}
          ip={@server.public_ip}
        />
        <section class="mt-4">
          <form phx-change="change_dry_run">
            <.input type="checkbox" name="dry_run" value={@dry_run} label="Enable dry-run operations" />
          </form>
        </section>
        <section class="mt-4">
          <h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">System</h2>
          <p class="mt-4">
            Updates: <%= 0 %> pending updates.
            <Button.primary type="button" class="ml-4" phx-click="system_update">
              Update now
            </Button.primary>
            <p :for={output <- assigns.update_output}>
              <%= output %>
            </p>
          </p>
        </section>
        <section class="mt-4">
          <h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">
            Backups
          </h2>
          <.table id="backups" rows={[%{date: "2023-10-11"}, %{date: "2023-10-10"}]}>
            <:col :let={backup} label="Date"><%= backup.date %></:col>
            <:action>
              <Button.primary>Restore</Button.primary>
            </:action>
          </.table>
        </section>
        <section class="mt-4">
          <h2 class="border-b border-solid border-gray-500 pb-1 text-2xl font-medium">
            Manage Services
          </h2>
          <.table
            id="services"
            rows={[%{name: "mariadb", status: "Active"}, %{name: "php8.0", status: "Disabled"}]}
          >
            <:col :let={service} label="Service"><%= service.name %></:col>
            <:col :let={service} label="Status"><%= service.status %></:col>
            <:action>
              <Button.primary>Activate</Button.primary>
              <Button.secondary>Deactivate</Button.secondary>
            </:action>
          </.table>
        </section>
      </div>
      <.back navigate={~p"/servers"}>Back to servers</.back>
    </div>
    """
  end

  @impl true
  def handle_params(%{"id" => id}, _, socket) do
    server = Servers.get_server!(id)

    if connected?(socket) and server.status == :registered do
      Agents.subscribe_to_health(server.public_ip)
      Agents.start_connection(server.public_ip)
    end

    health = Agents.get_health(server.public_ip)

    {:noreply,
     socket
     |> assign(:page_title, server.name)
     |> assign(:health, health || %{message: "Connecting...", tasks: []})
     |> assign(:server, server)
     |> assign(:dry_run, false)
     |> assign(:update_output, [])
     # TODO: Do not assign this to the socket - instead generate it in the HTML
     |> assign(:registration_command, Servers.create_setup_command(server))}
  end

  @impl true
  def handle_info(%PrymnProto.Prymn.SysUpdateResponse{} = response, socket) do
    output = String.split(response.output, "\n")
    socket = assign(socket, :update_output, output)
    {:noreply, socket}
  end

  def handle_info(%Agents.Health{} = health, socket) do
    {:noreply, assign(socket, :health, health)}
  end

  @impl true
  def handle_event("system_update", _params, socket) do
    host_address = get_in(socket.assigns, [:server, Access.key(:public_ip)])
    server_name = get_in(socket.assigns, [:server, Access.key(:name)])

    socket =
      if host_address do
        Agents.sys_update(host_address, socket.assigns.dry_run)
        put_flash(socket, :info, "Started a system update on server #{server_name}.")
      else
        put_flash(
          socket,
          :error,
          "Could not perform the update. Your server does not seem to have an address"
        )
      end

    {:noreply, socket}
  end

  def handle_event("edit_server_name", %{"name" => name}, socket) do
    server =
      socket.assigns.server
      |> Servers.update_server(%{"name" => name})
      |> case do
        {:ok, server} -> server
        {:error, _} -> raise "Oops"
      end

    {:noreply, assign(socket, :server, server)}
  end

  def handle_event("change_dry_run", %{"dry_run" => enabled}, socket) do
    enabled = (enabled == "true" && true) || false
    {:noreply, assign(socket, :dry_run, enabled)}
  end

  defp show_edit_server_name() do
    JS.hide(to: "#server-name")
    |> JS.show(to: "#server-name-edit")
    |> JS.focus_first(to: "#server-name-edit")
  end

  defp submit_edit_server_name() do
    JS.push("edit_server_name")
    |> JS.hide()
    |> JS.show(to: "#server-name", display: "flex")
  end

  defp indicator(assigns) do
    ~H"""
    <span
      role="tooltip"
      class={[
        "absolute -left-6 inline-flex h-3 w-3 before:-translate-x-1/2 before:-translate-y-full",
        "before:-top-2 before:left-1/2 before:absolute before:text-sm before:text-white",
        "before:font-normal before:content-[attr(data-tip)] before:opacity-0",
        "hover:before:opacity-100 before:py-1 before:px-2 before:bg-black",
        "before:rounded before:pointer-events-none before:transition-opacity"
      ]}
      data-tip={@message}
    >
      <%= case @message do %>
        <% "Connected" -> %>
          <span class="absolute top-0 left-0 h-full w-full animate-ping rounded-full bg-green-400 opacity-75" />
          <span class="h-3 w-3 rounded-full bg-green-500" />
        <% "Disconnected" -> %>
          <span class="h-3 w-3 rounded-full bg-red-500" />
        <% _ -> %>
          <span class="h-3 w-3 rounded-full bg-yellow-500" />
      <% end %>
    </span>
    """
  end
end