defmodule MvWeb.GlobalSettingsLive do
  @moduledoc """
  LiveView for managing global application settings (Vereinsdaten).

  ## Features
  - Edit the association/club name
  - Manage custom fields
  - Real-time form validation
  - Success/error feedback

  ## Settings
  - `club_name` - The name of the association/club (required)

  ## Events
  - `validate` - Real-time form validation
  - `save` - Save settings changes

  ## Note
  Settings is a singleton resource - there is only one settings record.
  The club_name can also be set via the `ASSOCIATION_NAME` environment variable.

  CSV member import has been moved to the Import/Export page (`/admin/import-export`).
  """
  use MvWeb, :live_view

  require Ash.Query
  import Ash.Expr

  alias Mv.Membership

  on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}

  @impl true
  def mount(_params, session, socket) do
    {:ok, settings} = Membership.get_settings()

    # Get locale from session for translations
    locale = session["locale"] || "de"
    Gettext.put_locale(MvWeb.Gettext, locale)

    socket =
      socket
      |> assign(:page_title, gettext("Settings"))
      |> assign(:settings, settings)
      |> assign(:active_editing_section, nil)
      |> assign(:locale, locale)
      |> assign(:vereinfacht_env_configured, Mv.Config.vereinfacht_env_configured?())
      |> assign(:vereinfacht_api_url_env_set, Mv.Config.vereinfacht_api_url_env_set?())
      |> assign(:vereinfacht_api_key_env_set, Mv.Config.vereinfacht_api_key_env_set?())
      |> assign(:vereinfacht_club_id_env_set, Mv.Config.vereinfacht_club_id_env_set?())
      |> assign(:vereinfacht_app_url_env_set, Mv.Config.vereinfacht_app_url_env_set?())
      |> assign(:vereinfacht_api_key_set, present?(settings.vereinfacht_api_key))
      |> assign(:last_vereinfacht_sync_result, nil)
      |> assign_form()

    {:ok, socket}
  end

  defp present?(nil), do: false
  defp present?(""), do: false
  defp present?(s) when is_binary(s), do: String.trim(s) != ""
  defp present?(_), do: false

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_user={@current_user} club_name={@settings.club_name}>
      <.header>
        {gettext("Settings")}
        <:subtitle>
          {gettext("Manage global settings for the association.")}
        </:subtitle>
      </.header>

      <%!-- Club Settings Section --%>
      <.form_section title={gettext("Club Settings")}>
        <.form for={@form} id="settings-form" phx-change="validate" phx-submit="save">
          <div class="w-100">
            <.input
              field={@form[:club_name]}
              type="text"
              label={gettext("Association Name")}
              required
            />
          </div>

          <.button phx-disable-with={gettext("Saving...")} variant="primary">
            {gettext("Save Settings")}
          </.button>
        </.form>
      </.form_section>
      <%!-- Vereinfacht Integration Section --%>
      <.form_section title={gettext("Vereinfacht Integration")}>
        <%= if @vereinfacht_env_configured do %>
          <p class="text-sm text-base-content/70 mb-4">
            {gettext("Some values are set via environment variables. Those fields are read-only.")}
          </p>
        <% end %>
        <.form for={@form} id="vereinfacht-form" phx-change="validate" phx-submit="save">
          <div class="grid gap-4">
            <.input
              field={@form[:vereinfacht_api_url]}
              type="text"
              label={gettext("API URL")}
              disabled={@vereinfacht_api_url_env_set}
              placeholder={
                if(@vereinfacht_api_url_env_set,
                  do: gettext("From VEREINFACHT_API_URL"),
                  else: "https://api.verein.visuel.dev/api/v1"
                )
              }
            />
            <div class="form-control">
              <label class="label" for={@form[:vereinfacht_api_key].id}>
                <span class="label-text">{gettext("API Key")}</span>
                <%= if @vereinfacht_api_key_set do %>
                  <span class="label-text-alt badge badge-ghost">{gettext("(set)")}</span>
                <% end %>
              </label>
              <.input
                field={@form[:vereinfacht_api_key]}
                type="password"
                label=""
                disabled={@vereinfacht_api_key_env_set}
                placeholder={
                  if(@vereinfacht_api_key_env_set,
                    do: gettext("From VEREINFACHT_API_KEY"),
                    else:
                      if(@vereinfacht_api_key_set,
                        do: gettext("Leave blank to keep current"),
                        else: nil
                      )
                  )
                }
              />
            </div>
            <.input
              field={@form[:vereinfacht_club_id]}
              type="text"
              label={gettext("Club ID")}
              disabled={@vereinfacht_club_id_env_set}
              placeholder={
                if(@vereinfacht_club_id_env_set, do: gettext("From VEREINFACHT_CLUB_ID"), else: "2")
              }
            />
            <.input
              field={@form[:vereinfacht_app_url]}
              type="text"
              label={gettext("App URL (contact view link)")}
              disabled={@vereinfacht_app_url_env_set}
              placeholder={
                if(@vereinfacht_app_url_env_set,
                  do: gettext("From VEREINFACHT_APP_URL"),
                  else: "https://app.verein.visuel.dev"
                )
              }
            />
          </div>
          <.button
            :if={
              not (@vereinfacht_api_url_env_set and @vereinfacht_api_key_env_set and
                     @vereinfacht_club_id_env_set)
            }
            phx-disable-with={gettext("Saving...")}
            variant="primary"
            class="mt-2"
          >
            {gettext("Save Vereinfacht Settings")}
          </.button>
          <.button
            :if={Mv.Config.vereinfacht_configured?()}
            type="button"
            phx-click="sync_vereinfacht_contacts"
            phx-disable-with={gettext("Syncing...")}
            class="mt-4 btn-outline"
          >
            {gettext("Sync all members without Vereinfacht contact")}
          </.button>
          <%= if @last_vereinfacht_sync_result do %>
            <.vereinfacht_sync_result result={@last_vereinfacht_sync_result} />
          <% end %>
        </.form>
      </.form_section>
      <%!-- Memberdata Section --%>
      <.form_section title={gettext("Memberdata")}>
        <.live_component
          :if={@active_editing_section != :custom_fields}
          module={MvWeb.MemberFieldLive.IndexComponent}
          id="member-fields-component"
          settings={@settings}
        />
        <%!-- Custom Fields Section --%>
        <.live_component
          :if={@active_editing_section != :member_fields}
          module={MvWeb.CustomFieldLive.IndexComponent}
          id="custom-fields-component"
          actor={@current_user}
        />
      </.form_section>
    </Layouts.app>
    """
  end

  @impl true
  def handle_event("validate", %{"setting" => setting_params}, socket) do
    {:noreply,
     assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, setting_params))}
  end

  @impl true
  def handle_event("sync_vereinfacht_contacts", _params, socket) do
    case Mv.Vereinfacht.sync_members_without_contact() do
      {:ok, %{synced: synced, errors: errors}} ->
        errors_with_names = enrich_sync_errors(errors)
        result = %{synced: synced, errors: errors_with_names}

        socket =
          socket
          |> assign(:last_vereinfacht_sync_result, result)
          |> put_flash(
            :info,
            if(errors_with_names == [],
              do: gettext("Synced %{count} member(s) to Vereinfacht.", count: synced),
              else:
                gettext("Synced %{count} member(s). %{error_count} failed.",
                  count: synced,
                  error_count: length(errors_with_names)
                )
            )
          )

        {:noreply, socket}

      {:error, :not_configured} ->
        {:noreply,
         put_flash(
           socket,
           :error,
           gettext("Vereinfacht is not configured. Set API URL, API Key, and Club ID.")
         )}
    end
  end

  @impl true
  def handle_event("save", %{"setting" => setting_params}, socket) do
    actor = MvWeb.LiveHelpers.current_actor(socket)
    # Never send blank API key so we do not overwrite the stored secret (security)
    setting_params_clean = drop_blank_vereinfacht_api_key(setting_params)

    case MvWeb.LiveHelpers.submit_form(socket.assigns.form, setting_params_clean, actor) do
      {:ok, _updated_settings} ->
        {:ok, fresh_settings} = Membership.get_settings()

        socket =
          socket
          |> assign(:settings, fresh_settings)
          |> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
          |> put_flash(:info, gettext("Settings updated successfully"))
          |> assign_form()

        {:noreply, socket}

      {:error, form} ->
        {:noreply, assign(socket, form: form)}
    end
  end

  defp drop_blank_vereinfacht_api_key(params) when is_map(params) do
    case params do
      %{"vereinfacht_api_key" => v} when v in [nil, ""] ->
        Map.delete(params, "vereinfacht_api_key")

      _ ->
        params
    end
  end

  @impl true
  def handle_info({:custom_field_saved, _custom_field, action}, socket) do
    send_update(MvWeb.CustomFieldLive.IndexComponent,
      id: "custom-fields-component",
      show_form: false
    )

    {:noreply,
     socket
     |> assign(:active_editing_section, nil)
     |> put_flash(:info, gettext("Data field %{action} successfully", action: action))}
  end

  @impl true
  def handle_info({:custom_field_deleted, _custom_field}, socket) do
    {:noreply, put_flash(socket, :info, gettext("Data field deleted successfully"))}
  end

  @impl true
  def handle_info({:custom_field_delete_error, error}, socket) do
    {:noreply,
     put_flash(
       socket,
       :error,
       gettext("Failed to delete data field: %{error}", error: inspect(error))
     )}
  end

  @impl true
  def handle_info(:custom_field_slug_mismatch, socket) do
    {:noreply, put_flash(socket, :error, gettext("Slug does not match. Deletion cancelled."))}
  end

  def handle_info({:custom_fields_load_error, _error}, socket) do
    {:noreply,
     put_flash(
       socket,
       :error,
       gettext("Could not load data fields. Please check your permissions.")
     )}
  end

  @impl true
  def handle_info({:editing_section_changed, section}, socket) do
    {:noreply, assign(socket, :active_editing_section, section)}
  end

  @impl true
  def handle_info({:member_field_saved, _member_field, action}, socket) do
    # Reload settings to get updated member_field_visibility
    {:ok, updated_settings} = Membership.get_settings()

    # Send update to member fields component to close form
    send_update(MvWeb.MemberFieldLive.IndexComponent,
      id: "member-fields-component",
      show_form: false,
      settings: updated_settings
    )

    {:noreply,
     socket
     |> assign(:settings, updated_settings)
     |> assign(:active_editing_section, nil)
     |> put_flash(:info, gettext("Member field %{action} successfully", action: action))}
  end

  @impl true
  def handle_info({:member_field_visibility_updated}, socket) do
    # Legacy event - reload settings and update component
    {:ok, updated_settings} = Membership.get_settings()

    send_update(MvWeb.MemberFieldLive.IndexComponent,
      id: "member-fields-component",
      settings: updated_settings
    )

    {:noreply, assign(socket, :settings, updated_settings)}
  end

  defp assign_form(%{assigns: %{settings: settings}} = socket) do
    # Never put API key into form/DOM to avoid secret leak in source or DevTools
    settings_for_form = %{settings | vereinfacht_api_key: nil}

    form =
      AshPhoenix.Form.for_update(
        settings_for_form,
        :update,
        api: Membership,
        as: "setting",
        forms: [auto?: true]
      )

    assign(socket, form: to_form(form))
  end

  defp enrich_sync_errors([]), do: []

  defp enrich_sync_errors(errors) when is_list(errors) do
    name_by_id = fetch_member_names_by_ids(Enum.map(errors, fn {id, _} -> id end))

    Enum.map(errors, fn {member_id, reason} ->
      %{
        member_id: member_id,
        member_name: Map.get(name_by_id, member_id) || to_string(member_id),
        message: Mv.Vereinfacht.format_error(reason),
        detail: extract_vereinfacht_detail(reason)
      }
    end)
  end

  defp fetch_member_names_by_ids(ids) do
    actor = Mv.Helpers.SystemActor.get_system_actor()
    opts = Mv.Helpers.ash_actor_opts(actor)
    query = Ash.Query.filter(Mv.Membership.Member, expr(id in ^ids))

    case Ash.read(query, opts) do
      {:ok, members} ->
        Map.new(members, fn m -> {m.id, MvWeb.Helpers.MemberHelpers.display_name(m)} end)

      _ ->
        %{}
    end
  end

  defp extract_vereinfacht_detail({:http, _status, detail}) when is_binary(detail), do: detail
  defp extract_vereinfacht_detail(_), do: nil

  defp translate_vereinfacht_message(%{detail: detail}) when is_binary(detail) do
    gettext("Vereinfacht: %{detail}",
      detail: Gettext.dgettext(MvWeb.Gettext, "default", detail)
    )
  end

  defp translate_vereinfacht_message(%{message: message}) do
    Gettext.dgettext(MvWeb.Gettext, "default", message)
  end

  attr :result, :map, required: true

  defp vereinfacht_sync_result(assigns) do
    ~H"""
    <div class="mt-4 p-4 rounded-lg border border-base-300 bg-base-200 space-y-2">
      <p class="font-medium">
        {gettext("Last sync result:")}
        <span class="text-success-aa ml-1">{gettext("%{count} synced", count: @result.synced)}</span>
        <%= if @result.errors != [] do %>
          <span class="text-error-aa ml-1">
            {gettext("%{count} failed", count: length(@result.errors))}
          </span>
        <% end %>
      </p>
      <%= if @result.errors != [] do %>
        <p class="text-sm text-base-content/70 mt-2">{gettext("Failed members:")}</p>
        <ul class="list-disc list-inside text-sm space-y-1 max-h-48 overflow-y-auto">
          <%= for err <- @result.errors do %>
            <li>
              <span class="font-medium">{err.member_name}</span>: {translate_vereinfacht_message(err)}
            </li>
          <% end %>
        </ul>
      <% end %>
    </div>
    """
  end
end
