defmodule MvWeb.MemberFieldLive.FormComponent do
  @moduledoc """
  LiveComponent form for editing member field properties (embedded in settings).

  ## Features
  - Edit member field visibility (show_in_overview)
  - Display member field information from Member Resource (read-only)
  - Restrict editing for email field (only show_in_overview can be changed)
  - Real-time validation
  - Updates Settings.member_field_visibility atomically

  ## Props
  - `member_field` - The member field atom to edit (e.g., :first_name, :email)
  - `settings` - The current Settings resource
  - `on_save` - Callback function to call when form is saved
  - `on_cancel` - Callback function to call when form is cancelled

  ## Note
  Member fields are technical fields that cannot be changed (name, value_type).
  Visibility (show_in_overview) and required flag are stored in Settings and can be modified.
  """
  use MvWeb, :live_component

  alias Mv.Helpers.TypeParsers
  alias Mv.Membership
  alias Mv.Membership.Helpers.VisibilityConfig
  alias MvWeb.Helpers.FieldTypeFormatter
  alias MvWeb.Translations.MemberFields

  @impl true
  def render(assigns) do
    assigns =
      assigns
      |> assign(:field_attributes, get_field_attributes(assigns.member_field))
      |> assign(:is_email_field?, assigns.member_field == :email)
      |> assign(:vereinfacht_required_field?, vereinfacht_required_field?(assigns))
      |> assign(:field_label, MemberFields.label(assigns.member_field))

    ~H"""
    <div id={@id} class="mb-8 border shadow-xl card border-base-300">
      <div class="card-body">
        <div class="flex items-center gap-4 mb-4">
          <.button
            type="button"
            variant="neutral"
            phx-click="cancel"
            phx-target={@myself}
            aria-label={gettext("Back to settings")}
          >
            <.icon name="hero-arrow-left" class="size-4" />
            {gettext("Back")}
          </.button>
          <h3 class="card-title">
            {gettext("Edit Field: %{field}", field: @field_label)}
          </h3>
        </div>

        <.form
          for={@form}
          id={@id <> "-form"}
          phx-change="validate"
          phx-submit="save"
          phx-target={@myself}
        >
          <div
            class="tooltip tooltip-right"
            data-tip={gettext("This is a technical field and cannot be changed")}
            aria-label={gettext("This is a technical field and cannot be changed")}
          >
            <fieldset class="mb-2 fieldset">
              <label>
                <span class="mb-1 label flex items-center gap-2">
                  {gettext("Name")}
                  <.icon
                    name="hero-information-circle"
                    class="w-4 h-4 text-base-content/60 cursor-help"
                    aria-hidden="true"
                  />
                </span>
                <input
                  type="text"
                  name={@form[:name].name}
                  id={@form[:name].id}
                  value={@field_label}
                  disabled
                  readonly
                  class="w-full input"
                />
              </label>
            </fieldset>
          </div>

          <div
            class="tooltip tooltip-right"
            data-tip={gettext("This is a technical field and cannot be changed")}
            aria-label={gettext("This is a technical field and cannot be changed")}
          >
            <fieldset class="mb-2 fieldset">
              <label>
                <span class="mb-1 label flex items-center gap-2">
                  {gettext("Value type")}
                  <.icon
                    name="hero-information-circle"
                    class="w-4 h-4 text-base-content/60 cursor-help"
                    aria-hidden="true"
                  />
                </span>
                <input
                  type="text"
                  name={@form[:value_type].name}
                  id={@form[:value_type].id}
                  value={FieldTypeFormatter.format(@field_attributes.value_type)}
                  disabled
                  readonly
                  class="w-full input"
                />
              </label>
            </fieldset>
          </div>

          <%!-- Line break before Required / Show in overview block --%>
          <div class="mt-4">
            <%!-- Required: disabled for email (always required) or Vereinfacht-required fields when integration is active --%>
            <div
              :if={@is_email_field? or @vereinfacht_required_field?}
              class="tooltip tooltip-right"
              data-tip={
                if(@is_email_field?,
                  do: gettext("This is a technical field and cannot be changed"),
                  else: gettext("Required for Vereinfacht integration and cannot be disabled.")
                )
              }
              aria-label={
                if(@is_email_field?,
                  do: gettext("This is a technical field and cannot be changed"),
                  else: gettext("Required for Vereinfacht integration and cannot be disabled.")
                )
              }
            >
              <fieldset class="mb-2 fieldset">
                <label>
                  <input type="hidden" name={@form[:required].name} value="true" />
                  <span class="label flex items-center gap-2">
                    <input
                      type="checkbox"
                      name={@form[:required].name}
                      id={@form[:required].id}
                      value="true"
                      checked={@form[:required].value}
                      disabled
                      readonly
                      class="checkbox checkbox-sm"
                    />
                    <span class="flex items-center gap-2">
                      {gettext("Required")}
                      <.icon
                        name="hero-information-circle"
                        class="w-4 h-4 text-base-content/60 cursor-help"
                        aria-hidden="true"
                      />
                    </span>
                  </span>
                </label>
              </fieldset>
            </div>
            <.input
              :if={not @is_email_field? and not @vereinfacht_required_field?}
              field={@form[:required]}
              type="checkbox"
              label={gettext("Required")}
            />

            <.input
              field={@form[:show_in_overview]}
              type="checkbox"
              label={gettext("Show in overview")}
            />
          </div>

          <div class="justify-end mt-4 card-actions">
            <.button type="button" variant="neutral" phx-click="cancel" phx-target={@myself}>
              {gettext("Cancel")}
            </.button>
            <.button phx-disable-with={gettext("Saving...")} variant="primary">
              {gettext("Save Field")}
            </.button>
          </div>
        </.form>
      </div>
    </div>
    """
  end

  @impl true
  def update(assigns, socket) do
    {:ok,
     socket
     |> assign(assigns)
     |> assign_form()}
  end

  @impl true
  def handle_event("validate", %{"member_field" => member_field_params}, socket) do
    form = socket.assigns.form
    # Unchecked checkboxes are not in params; preserve current form value when key is missing
    show_in_overview =
      if Map.has_key?(member_field_params, "show_in_overview") do
        TypeParsers.parse_boolean(member_field_params["show_in_overview"])
      else
        form.source["show_in_overview"]
      end

    required =
      socket.assigns.vereinfacht_required_field? ||
        if Map.has_key?(member_field_params, "required") do
          TypeParsers.parse_boolean(member_field_params["required"])
        else
          form.source["required"]
        end

    # Merge so we keep name/value_type and have current checkbox state; use as new form source
    merged_source =
      form.source
      |> Map.merge(%{
        "show_in_overview" => show_in_overview,
        "required" => required,
        "name" => form.source["name"],
        "value_type" => form.source["value_type"]
      })

    updated_form =
      to_form(merged_source, as: "member_field")
      |> Map.put(:errors, [])

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

  @impl true
  def handle_event("save", %{"member_field" => member_field_params}, socket) do
    form = socket.assigns.form
    # Unchecked checkboxes are not in submit params; use form source when key missing
    show_in_overview =
      if Map.has_key?(member_field_params, "show_in_overview") do
        TypeParsers.parse_boolean(member_field_params["show_in_overview"])
      else
        form.source["show_in_overview"]
      end

    required =
      socket.assigns.vereinfacht_required_field? ||
        if Map.has_key?(member_field_params, "required") do
          TypeParsers.parse_boolean(member_field_params["required"])
        else
          form.source["required"]
        end

    field_string = Atom.to_string(socket.assigns.member_field)

    case Membership.update_single_member_field(
           socket.assigns.settings,
           field: field_string,
           show_in_overview: show_in_overview,
           required: required
         ) do
      {:ok, _updated_settings} ->
        socket.assigns.on_save.(socket.assigns.member_field, "update")
        {:noreply, socket}

      {:error, error} ->
        form =
          socket.assigns.form
          |> Map.put(:errors, [
            %{field: :show_in_overview, message: format_error(error)}
          ])

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

  @impl true
  def handle_event("cancel", _params, socket) do
    socket.assigns.on_cancel.()
    {:noreply, socket}
  end

  # Helper functions

  defp assign_form(%{assigns: %{member_field: member_field, settings: settings}} = socket) do
    field_attributes = get_field_attributes(member_field)
    visibility_config = settings.member_field_visibility || %{}
    required_config = settings.member_field_required || %{}
    normalized_visibility = VisibilityConfig.normalize(visibility_config)
    normalized_required = VisibilityConfig.normalize(required_config)
    show_in_overview = Map.get(normalized_visibility, member_field, true)
    vereinfacht_required? = Mv.Config.vereinfacht_configured?()
    # Persist in socket so validate/save can enforce server-side without relying on render assigns
    socket =
      assign(
        socket,
        :vereinfacht_required_field?,
        vereinfacht_required_field?(%{member_field: member_field})
      )

    # Email always required; Vereinfacht-required fields when integration active; else from settings
    required =
      member_field == :email ||
        (vereinfacht_required? && Mv.Constants.vereinfacht_required_field?(member_field)) ||
        Map.get(normalized_required, member_field, false)

    form_data = %{
      "name" => MemberFields.label(member_field),
      "value_type" => FieldTypeFormatter.format(field_attributes.value_type),
      "required" => required,
      "show_in_overview" => show_in_overview
    }

    form = to_form(form_data, as: "member_field")

    assign(socket, form: form)
  end

  defp get_field_attributes(field) when is_atom(field) do
    alias Ash.Resource.Info

    case Info.attribute(Mv.Membership.Member, field) do
      nil ->
        %{value_type: :string}

      attribute ->
        %{value_type: attribute.type}
    end
  end

  defp format_error(%Ash.Error.Invalid{} = error) do
    Ash.ErrorKind.message(error)
  end

  defp format_error(error) do
    inspect(error)
  end

  defp vereinfacht_required_field?(assigns) do
    Mv.Config.vereinfacht_configured?() &&
      Mv.Constants.vereinfacht_required_field?(assigns.member_field)
  end
end
