defmodule MvWeb.MemberFieldLive.IndexComponent do
  @moduledoc """
  LiveComponent for managing member field visibility in overview (embedded in settings).

  ## Features
  - List all member fields from Mv.Constants.member_fields()
  - Display show_in_overview status as badge (Yes/No)
  - Display required status based on actual attribute definitions (allow_nil? false)
  - Edit member field properties (expandable form like custom fields)
  - Updates Settings.member_field_visibility
  """
  use MvWeb, :live_component

  alias Ash.Resource.Info
  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(:member_fields, get_member_fields_with_visibility(assigns.settings))

    ~H"""
    <div id={@id}>
      <p class="text-sm text-base-content/70 mb-4">
        {gettext(
          "These fields are neccessary for MILA to handle member identification and payment calculations in the future. Thus you cannot delete these fields but hide them in the member overview."
        )}
      </p>

      <%!-- Show form when editing --%>
      <div :if={@show_form} class="mb-8">
        <.live_component
          module={MvWeb.MemberFieldLive.FormComponent}
          id={@form_id}
          member_field={@editing_member_field}
          settings={@settings}
          on_save={
            fn member_field, action ->
              send(self(), {:member_field_saved, member_field, action})
            end
          }
          on_cancel={fn -> send_update(__MODULE__, id: @id, show_form: false) end}
        />
      </div>

      <%!-- Hide table when form is visible --%>
      <.table
        :if={!@show_form}
        id="member_fields"
        rows={@member_fields}
        row_click={
          fn {field_name, _field_data} ->
            JS.push("edit_member_field", value: %{"field" => field_name}, target: @myself)
          end
        }
        row_tooltip={gettext("Click to edit datafield")}
      >
        <:col :let={{_field_name, field_data}} label={gettext("Name")}>
          {MemberFields.label(field_data.field)}
        </:col>

        <:col :let={{_field_name, field_data}} label={gettext("Value Type")}>
          {format_value_type(field_data.field)}
        </:col>

        <:col
          :let={{_field_name, field_data}}
          label={gettext("Required")}
          class="max-w-[9.375rem] text-center"
        >
          <span :if={field_data.required} class="text-base-content font-semibold">
            {gettext("Required")}
          </span>
          <span :if={!field_data.required} class="text-base-content/70">
            {gettext("Optional")}
          </span>
        </:col>

        <:col
          :let={{_field_name, field_data}}
          label={gettext("Show in overview")}
          class="max-w-[9.375rem] text-center"
        >
          <.badge :if={field_data.show_in_overview} variant="success">
            {gettext("Yes")}
          </.badge>
          <.badge :if={!field_data.show_in_overview} variant="neutral">
            {gettext("No")}
          </.badge>
        </:col>
      </.table>
    </div>
    """
  end

  @impl true
  def update(assigns, socket) do
    # Track previous show_form state to detect when form is closed
    previous_show_form = Map.get(socket.assigns, :show_form, false)

    # If show_form is explicitly provided in assigns, reset editing state
    socket =
      if Map.has_key?(assigns, :show_form) and assigns.show_form == false do
        socket
        |> assign(:editing_member_field, nil)
        |> assign(:form_id, "member-field-form-new")
      else
        socket
      end

    # Detect when form is closed (show_form changes from true to false)
    new_show_form = Map.get(assigns, :show_form, false)

    if previous_show_form and not new_show_form do
      send(self(), {:editing_section_changed, nil})
    end

    {:ok,
     socket
     |> assign(assigns)
     |> assign_new(:settings, fn -> get_settings() end)
     |> assign_new(:show_form, fn -> false end)
     |> assign_new(:form_id, fn -> "member-field-form-new" end)
     |> assign_new(:editing_member_field, fn -> nil end)}
  end

  @impl true
  def handle_event("edit_member_field", %{"field" => field_string}, socket) do
    # Validate that the field is a valid member field before converting to atom
    valid_fields = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)

    if field_string in valid_fields do
      field_atom = String.to_existing_atom(field_string)

      # Only send event if form was not already open
      if not socket.assigns[:show_form] do
        send(self(), {:editing_section_changed, :member_fields})
      end

      {:noreply,
       socket
       |> assign(:show_form, true)
       |> assign(:editing_member_field, field_atom)
       |> assign(:form_id, "member-field-form-#{field_string}")}
    else
      {:noreply, socket}
    end
  end

  # Helper functions

  defp get_settings do
    case Membership.get_settings() do
      {:ok, settings} ->
        settings

      {:error, _} ->
        # Return a minimal struct-like map for fallback
        # This is only used for initial rendering, actual settings will be loaded properly
        %{member_field_visibility: %{}, member_field_required: %{}}
    end
  end

  defp get_member_fields_with_visibility(settings) do
    member_fields = Mv.Constants.member_fields()
    visibility_config = settings.member_field_visibility || %{}
    required_config = settings.member_field_required || %{}
    vereinfacht_required? = Mv.Config.vereinfacht_configured?()

    normalized_visibility = VisibilityConfig.normalize(visibility_config)
    normalized_required = VisibilityConfig.normalize(required_config)

    Enum.map(member_fields, fn field ->
      show_in_overview = Map.get(normalized_visibility, field, true)

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

      attribute = Info.attribute(Mv.Membership.Member, field)

      %{
        field: field,
        show_in_overview: show_in_overview,
        required: required,
        value_type: (attribute && attribute.type) || :string
      }
    end)
    |> Enum.map(fn field_data ->
      {Atom.to_string(field_data.field), field_data}
    end)
  end

  defp format_value_type(field) when is_atom(field) do
    case Info.attribute(Mv.Membership.Member, field) do
      nil -> FieldTypeFormatter.format(:string)
      attribute -> FieldTypeFormatter.format(attribute.type)
    end
  end
end
