defmodule BerrypodWeb.Admin.Pages.CustomForm do
  use BerrypodWeb, :live_view

  alias Berrypod.Pages
  alias Berrypod.Pages.{Defaults, Page}

  @impl true
  def mount(params, _session, socket) do
    {:ok, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :new, _params) do
    changeset = Page.custom_changeset(%Page{}, %{})

    socket
    |> assign(:page_title, "New page")
    |> assign(:page_struct, %Page{})
    |> assign(:slug_touched, false)
    |> assign(:selected_template, "blank")
    |> assign(:form, to_form(changeset))
  end

  defp apply_action(socket, :edit, %{"slug" => slug}) do
    case Pages.get_page_struct(slug) do
      %Page{type: "custom"} = page ->
        changeset = Page.custom_changeset(page, %{})

        socket
        |> assign(:page_title, "#{page.title} settings")
        |> assign(:page_struct, page)
        |> assign(:slug_touched, true)
        |> assign(:form, to_form(changeset))

      _ ->
        socket
        |> put_flash(:error, "Page not found")
        |> push_navigate(to: ~p"/admin/pages")
    end
  end

  @impl true
  def handle_event("validate", %{"page" => params}, socket) do
    params = maybe_slugify(params, socket)

    form =
      socket.assigns.page_struct
      |> Page.custom_changeset(params)
      |> Map.put(:action, :validate)
      |> to_form()

    slug_touched =
      socket.assigns.slug_touched || (params["slug"] && params["slug"] != "")

    {:noreply, assign(socket, form: form, slug_touched: slug_touched)}
  end

  @impl true
  def handle_event("select_template", %{"key" => key}, socket) do
    {:noreply, assign(socket, :selected_template, key)}
  end

  @impl true
  def handle_event("save", %{"page" => params}, socket) do
    save_page(socket, socket.assigns.live_action, params)
  end

  defp save_page(socket, :new, params) do
    template_blocks = Defaults.template_blocks(socket.assigns.selected_template)
    params = Map.put(params, "blocks", template_blocks)

    case Pages.create_custom_page(params) do
      {:ok, page} ->
        {:noreply,
         socket
         |> put_flash(:info, "Page created")
         |> push_navigate(to: ~p"/admin/pages/#{page.slug}")}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

  defp save_page(socket, :edit, params) do
    case Pages.update_custom_page(socket.assigns.page_struct, params) do
      {:ok, page} ->
        {:noreply,
         socket
         |> put_flash(:info, "Page settings saved")
         |> push_navigate(to: ~p"/admin/pages/#{page.slug}")}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

  # Auto-generate slug from title when creating and slug hasn't been manually edited
  defp maybe_slugify(params, %{assigns: %{live_action: :new, slug_touched: false}}) do
    case params["title"] do
      title when is_binary(title) and title != "" ->
        slug =
          title
          |> String.downcase()
          |> String.replace(~r/[^a-z0-9\s-]/, "")
          |> String.trim()
          |> String.replace(~r/\s+/, "-")
          |> String.replace(~r/-+/, "-")
          |> String.trim("-")

        Map.put(params, "slug", slug)

      _ ->
        params
    end
  end

  defp maybe_slugify(params, _socket), do: params

  @impl true
  def render(assigns) do
    ~H"""
    <.link navigate={~p"/admin/pages"} class="admin-back-link">
      &larr; Pages
    </.link>

    <.header>
      {if @live_action == :new, do: "New page", else: "Page settings"}
    </.header>

    <div :if={@live_action == :new} class="template-picker">
      <p class="template-picker-label">Start from a template</p>
      <div class="template-picker-cards">
        <button
          :for={tmpl <- Defaults.templates()}
          type="button"
          phx-click="select_template"
          phx-value-key={tmpl.key}
          class={[
            "template-card",
            assigns[:selected_template] == tmpl.key && "template-card-selected"
          ]}
        >
          <span class="template-card-name">{tmpl.label}</span>
          <span class="template-card-desc">{tmpl.description}</span>
        </button>
      </div>
    </div>

    <.form
      for={@form}
      id="custom-page-form"
      phx-change="validate"
      phx-submit="save"
      class="admin-form-stack admin-section"
    >
      <.input field={@form[:title]} label="Title" />
      <.input field={@form[:slug]} label="URL slug" />
      <.input
        field={@form[:meta_description]}
        type="textarea"
        label="Meta description"
        phx-no-feedback
      />
      <.input field={@form[:published]} type="checkbox" label="Published" />
      <.input field={@form[:show_in_nav]} type="checkbox" label="Show in navigation" />

      <div
        :if={@form[:show_in_nav].value == true}
        class="admin-form-sub"
      >
        <.input field={@form[:nav_label]} label="Nav label" />
        <.input field={@form[:nav_position]} type="number" label="Nav position" />
      </div>

      <div class="admin-row admin-row-lg">
        <.button type="submit" phx-disable-with="Saving...">
          {if @live_action == :new, do: "Create page", else: "Save settings"}
        </.button>
        <.link navigate={~p"/admin/pages"} class="admin-btn admin-btn-ghost">
          Cancel
        </.link>
      </div>
    </.form>
    """
  end
end
