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

  use Phoenix.Component

  alias BDS.Desktop.ShellData
  alias BDS.{MCP, Repo, Scripts, Scripting, Templates}
  alias BDS.Scripts.Script
  alias BDS.Templates.Template

  embed_templates "code_entity_editor_html/*"

  def assign_socket(socket) do
    socket
    |> assign(:script_editor, build_script(socket.assigns))
    |> assign(:template_editor, build_template(socket.assigns))
  end

  def update_script(socket, params, reload) do
    %{id: script_id} = socket.assigns.current_tab

    socket
    |> assign(:script_editor_drafts, Map.put(socket.assigns.script_editor_drafts, script_id, normalize_script_params(params)))
    |> reload.(socket.assigns.workbench)
  end

  def save_script(socket, reload, append_output) do
    %{id: script_id} = socket.assigns.current_tab

    case Repo.get(Script, script_id) do
      nil -> reload.(socket, socket.assigns.workbench)
      %Script{} = script ->
        draft = current_script_draft(socket.assigns, script)

        case Scripting.validate(draft["content"] || "") do
          :ok ->
            case Scripts.update_script(script.id, script_attrs(draft)) do
              {:ok, _updated} ->
                socket
                |> assign(:script_editor_drafts, Map.delete(socket.assigns.script_editor_drafts, script.id))
                |> reload.(socket.assigns.workbench)

              {:error, reason} ->
                socket
                |> append_output.(translated("Scripts"), inspect(reason), nil, "error")
                |> reload.(socket.assigns.workbench)
            end

          {:error, reason} ->
            socket
            |> append_output.(translated("Scripts"), inspect(reason), nil, "error")
            |> reload.(socket.assigns.workbench)
        end
    end
  end

  def check_script(socket, reload, append_output) do
    %{id: script_id} = socket.assigns.current_tab

    case Repo.get(Script, script_id) do
      nil -> reload.(socket, socket.assigns.workbench)
      %Script{} = script ->
        case Scripting.validate(current_script_draft(socket.assigns, script)["content"] || "") do
          :ok -> append_output.(socket, translated("Scripts"), translated("Syntax is valid")) |> reload.(socket.assigns.workbench)
          {:error, reason} -> append_output.(socket, translated("Scripts"), inspect(reason), nil, "error") |> reload.(socket.assigns.workbench)
        end
    end
  end

  def run_script(socket, reload, append_output) do
    %{id: script_id} = socket.assigns.current_tab

    case Repo.get(Script, script_id) do
      nil -> reload.(socket, socket.assigns.workbench)
      %Script{} = script ->
        draft = current_script_draft(socket.assigns, script)

        case Scripting.execute_project_script(script.project_id, draft["content"] || "", draft["entrypoint"] || "main", []) do
          {:ok, result} ->
            socket
            |> append_output.(translated("Scripts"), inspect(result))
            |> reload.(socket.assigns.workbench)

          {:error, reason} ->
            socket
            |> append_output.(translated("Scripts"), inspect(reason), nil, "error")
            |> reload.(socket.assigns.workbench)
        end
    end
  end

  def delete_script(socket, reload, append_output) do
    %{id: script_id} = socket.assigns.current_tab

    case Scripts.delete_script(script_id) do
      {:ok, _deleted} -> reload.(socket, BDS.UI.Workbench.close_tab(socket.assigns.workbench, :scripts, script_id))
      {:error, reason} -> append_output.(socket, translated("Scripts"), inspect(reason), nil, "error") |> reload.(socket.assigns.workbench)
    end
  end

  def update_template(socket, params, reload) do
    %{id: template_id} = socket.assigns.current_tab

    socket
    |> assign(:template_editor_drafts, Map.put(socket.assigns.template_editor_drafts, template_id, normalize_template_params(params)))
    |> reload.(socket.assigns.workbench)
  end

  def save_template(socket, reload, append_output) do
    %{id: template_id} = socket.assigns.current_tab

    case Repo.get(Template, template_id) do
      nil -> reload.(socket, socket.assigns.workbench)
      %Template{} = template ->
        draft = current_template_draft(socket.assigns, template)

        with {:ok, %{valid: true}} <- MCP.validate_template(draft["content"] || ""),
             {:ok, _updated} <- Templates.update_template(template.id, template_attrs(draft)) do
          socket
          |> assign(:template_editor_drafts, Map.delete(socket.assigns.template_editor_drafts, template.id))
          |> reload.(socket.assigns.workbench)
        else
          {:ok, %{valid: false, errors: errors}} -> append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error") |> reload.(socket.assigns.workbench)
          {:error, reason} -> append_output.(socket, translated("Templates"), inspect(reason), nil, "error") |> reload.(socket.assigns.workbench)
        end
    end
  end

  def validate_template(socket, reload, append_output) do
    %{id: template_id} = socket.assigns.current_tab

    case Repo.get(Template, template_id) do
      nil -> reload.(socket, socket.assigns.workbench)
      %Template{} = template ->
        case MCP.validate_template(current_template_draft(socket.assigns, template)["content"] || "") do
          {:ok, %{valid: true}} -> append_output.(socket, translated("Templates"), translated("Template syntax is valid")) |> reload.(socket.assigns.workbench)
          {:ok, %{valid: false, errors: errors}} -> append_output.(socket, translated("Templates"), Enum.join(errors, "; "), nil, "error") |> reload.(socket.assigns.workbench)
        end
    end
  end

  def delete_template(socket, reload, append_output) do
    %{id: template_id} = socket.assigns.current_tab

    case Templates.delete_template(template_id, force: true) do
      {:ok, _deleted} -> reload.(socket, BDS.UI.Workbench.close_tab(socket.assigns.workbench, :templates, template_id))
      {:error, reason} -> append_output.(socket, translated("Templates"), inspect(reason), nil, "error") |> reload.(socket.assigns.workbench)
    end
  end

  def build_script(%{current_tab: %{type: :scripts, id: script_id}} = assigns) do
    case Repo.get(Script, script_id) do
      nil -> nil
      %Script{} = script ->
        draft = current_script_draft(assigns, script)
        %{
          id: script.id,
          title: draft["title"],
          slug: draft["slug"],
          kind: draft["kind"],
          entrypoint: draft["entrypoint"],
          enabled: draft["enabled"],
          content: draft["content"],
          entrypoints: discover_entrypoints(draft["content"]),
          created_at: script.created_at,
          updated_at: script.updated_at
        }
    end
  end

  def build_script(_assigns), do: nil

  def build_template(%{current_tab: %{type: :templates, id: template_id}} = assigns) do
    case Repo.get(Template, template_id) do
      nil -> nil
      %Template{} = template ->
        draft = current_template_draft(assigns, template)
        %{
          id: template.id,
          title: draft["title"],
          slug: draft["slug"],
          kind: draft["kind"],
          enabled: draft["enabled"],
          content: draft["content"],
          created_at: template.created_at,
          updated_at: template.updated_at
        }
    end
  end

  def build_template(_assigns), do: nil

  def translated(text, bindings \\ %{}), do: ShellData.translate(text, bindings, Process.get(:bds_ui_locale))

  def format_timestamp(nil), do: ""
  def format_timestamp(timestamp), do: BDS.Persistence.timestamp_to_iso8601(timestamp)

  defp normalize_script_params(params) do
    %{
      "title" => Map.get(params, "title", ""),
      "slug" => Map.get(params, "slug", ""),
      "kind" => Map.get(params, "kind", "utility"),
      "entrypoint" => Map.get(params, "entrypoint", "main"),
      "enabled" => Map.get(params, "enabled") in [true, "true", "on", "1", 1],
      "content" => Map.get(params, "content", "")
    }
  end

  defp normalize_template_params(params) do
    %{
      "title" => Map.get(params, "title", ""),
      "slug" => Map.get(params, "slug", ""),
      "kind" => Map.get(params, "kind", "post"),
      "enabled" => Map.get(params, "enabled") in [true, "true", "on", "1", 1],
      "content" => Map.get(params, "content", "")
    }
  end

  defp current_script_draft(assigns, %Script{} = script) do
    Map.get(assigns.script_editor_drafts, script.id, %{
      "title" => script.title || "",
      "slug" => script.slug || "",
      "kind" => to_string(script.kind || :utility),
      "entrypoint" => script.entrypoint || "main",
      "enabled" => script.enabled != false,
      "content" => script.content || ""
    })
  end

  defp current_template_draft(assigns, %Template{} = template) do
    Map.get(assigns.template_editor_drafts, template.id, %{
      "title" => template.title || "",
      "slug" => template.slug || "",
      "kind" => to_string(template.kind || :post),
      "enabled" => template.enabled != false,
      "content" => template.content || ""
    })
  end

  defp script_attrs(draft) do
    %{
      title: draft["title"],
      slug: draft["slug"],
      kind: String.to_existing_atom(draft["kind"]),
      entrypoint: draft["entrypoint"],
      enabled: draft["enabled"],
      content: draft["content"]
    }
  rescue
    _error -> %{title: draft["title"], slug: draft["slug"], kind: :utility, entrypoint: draft["entrypoint"], enabled: draft["enabled"], content: draft["content"]}
  end

  defp template_attrs(draft) do
    %{title: draft["title"], slug: draft["slug"], kind: normalize_template_kind(draft["kind"]), enabled: draft["enabled"], content: draft["content"]}
  end

  defp normalize_template_kind("post"), do: :post
  defp normalize_template_kind("list"), do: :list
  defp normalize_template_kind("not-found"), do: :"not-found"
  defp normalize_template_kind("partial"), do: :partial
  defp normalize_template_kind(_kind), do: :post

  defp discover_entrypoints(content) do
    ["main" | Regex.scan(~r/function\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/, content || "", capture: :all_but_first)
    |> List.flatten()
    |> Enum.reject(&(&1 == "main"))]
  end
end
