defmodule BDS.Desktop.ShellLive.ChatSurface do
  @moduledoc false

  import Phoenix.Component, only: [assign: 3]

  alias BDS.BoundedAtoms
  alias BDS.Desktop.ShellData
  alias BDS.Desktop.ShellLive.{ChatEditor, TabHelpers}
  alias BDS.UI.Workbench

  @doc """
  Handle a chat-surface action from a chat message. Receives callbacks for
  `reload_shell/2` and `open_sidebar_item/3` to remain decoupled from
  `BDS.Desktop.ShellLive` private state.
  """
  def handle_action(socket, params, callbacks) do
    surface_id = Map.get(params, "surface-id", "")

    payload =
      params
      |> Map.get("payload")
      |> decode_payload()
      |> maybe_put_form_data(socket, surface_id)

    case normalize_action(Map.get(params, "action", "")) do
      :open_post ->
        case Map.get(payload, "postId") || Map.get(payload, "post_id") do
          post_id when is_binary(post_id) and post_id != "" ->
            socket
            |> clear_action_error()
            |> callbacks.open_sidebar.(
              %{
                "route" => "post",
                "id" => post_id,
                "title" => TabHelpers.post_title(post_id),
                "subtitle" => TabHelpers.post_subtitle(post_id)
              },
              :pin
            )

          _other ->
            ChatEditor.set_action_error(
              socket,
              socket.assigns.current_tab.id,
              "Invalid payload for openPost action",
              callbacks.reload
            )
        end

      :open_media ->
        case Map.get(payload, "mediaId") || Map.get(payload, "media_id") do
          media_id when is_binary(media_id) and media_id != "" ->
            socket
            |> clear_action_error()
            |> callbacks.open_sidebar.(
              %{
                "route" => "media",
                "id" => media_id,
                "title" => TabHelpers.media_title(media_id),
                "subtitle" => TabHelpers.media_subtitle(media_id)
              },
              :pin
            )

          _other ->
            ChatEditor.set_action_error(
              socket,
              socket.assigns.current_tab.id,
              "Invalid payload for openMedia action",
              callbacks.reload
            )
        end

      :open_settings ->
        socket
        |> clear_action_error()
        |> callbacks.open_sidebar.(
          %{
            "route" => "settings",
            "id" => "settings-ai",
            "title" => "Settings",
            "subtitle" => "AI"
          },
          :pin
        )

      :open_chat ->
        chat_id =
          Map.get(payload, "conversationId") || Map.get(payload, "conversation_id") ||
            socket.assigns.current_tab.id

        socket
        |> clear_action_error()
        |> callbacks.open_sidebar.(
          %{
            "route" => "chat",
            "id" => chat_id,
            "title" => Map.get(payload, "title", "Chat"),
            "subtitle" => Map.get(payload, "subtitle", "")
          },
          :pin
        )

      :switch_view ->
        case BoundedAtoms.sidebar_view(Map.get(payload, "view")) do
          nil ->
            ChatEditor.set_action_error(
              socket,
              socket.assigns.current_tab.id,
              "Invalid payload for switchView action",
              callbacks.reload
            )

          view ->
            socket
            |> clear_action_error()
            |> callbacks.reload.(Workbench.click_activity(socket.assigns.workbench, view))
        end

      :toggle_sidebar ->
        socket
        |> clear_action_error()
        |> callbacks.reload.(Workbench.toggle_sidebar(socket.assigns.workbench))

      :toggle_panel ->
        socket
        |> clear_action_error()
        |> callbacks.reload.(Workbench.toggle_panel(socket.assigns.workbench))

      :toggle_assistant_sidebar ->
        socket
        |> clear_action_error()
        |> callbacks.reload.(Workbench.toggle_assistant_sidebar(socket.assigns.workbench))

      :unknown ->
        ChatEditor.set_action_error(
          socket,
          socket.assigns.current_tab.id,
          "Unsupported assistant action",
          callbacks.reload
        )
    end
  end

  def assistant_turn(prompt, socket) do
    [
      %{role: "user", content: prompt},
      %{role: "assistant", content: assistant_reply(socket)}
    ]
  end

  def assistant_project_name(nil), do: translated("Projects")
  def assistant_project_name(project), do: project.name

  def assistant_message_label("assistant"), do: translated("Assistant")
  def assistant_message_label("user"), do: translated("You")
  def assistant_message_label(_role), do: translated("Assistant")

  def assistant_message_testid(role), do: "assistant-message-#{role}"

  def update_shell_overlay(socket, updater) do
    case socket.assigns[:shell_overlay] do
      nil -> socket
      overlay -> assign(socket, :shell_overlay, updater.(overlay))
    end
  end

  def clear_action_error(%{assigns: %{current_tab: %{type: :chat, id: conversation_id}}} = socket) do
    assign(
      socket,
      :chat_editor_action_errors,
      Map.delete(socket.assigns.chat_editor_action_errors, conversation_id)
    )
  end

  def clear_action_error(socket), do: socket

  defp decode_payload(nil), do: %{}
  defp decode_payload(""), do: %{}

  defp decode_payload(payload) when is_binary(payload) do
    case Jason.decode(payload) do
      {:ok, decoded} when is_map(decoded) -> decoded
      _other -> %{}
    end
  end

  defp decode_payload(_payload), do: %{}

  defp maybe_put_form_data(payload, socket, surface_id)
       when is_binary(surface_id) and surface_id != "" do
    form_data = ChatEditor.current_surface_data(socket, surface_id)

    if form_data == %{} do
      payload
    else
      Map.put(payload, "formData", form_data)
    end
  end

  defp maybe_put_form_data(payload, _socket, _surface_id), do: payload

  defp normalize_action(action) do
    action
    |> to_string()
    |> String.replace("_", "")
    |> String.downcase()
    |> case do
      "openpost" -> :open_post
      "openmedia" -> :open_media
      "opensettings" -> :open_settings
      "openchat" -> :open_chat
      "switchview" -> :switch_view
      "setactiveview" -> :switch_view
      "togglesidebar" -> :toggle_sidebar
      "togglepanel" -> :toggle_panel
      "openpanel" -> :toggle_panel
      "toggleassistantsidebar" -> :toggle_assistant_sidebar
      _other -> :unknown
    end
  end

  defp assistant_reply(socket) do
    if socket.assigns.offline_mode do
      ShellData.translate(
        "Automatic AI actions stay gated by airplane mode.",
        %{},
        socket.assigns.page_language
      )
    else
      ShellData.translate(
        "The assistant sidebar chat surface is ready, but model execution is not connected yet.",
        %{},
        socket.assigns.page_language
      )
    end
  end

  defp translated(text), do: ShellData.translate(text, %{}, BDS.Desktop.UILocale.current())
end
