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; same fallback as router/LiveUserAuth (respects config :default_locale in test)
    locale = session["locale"] || Application.get_env(:mv, :default_locale, "de")
    Gettext.put_locale(MvWeb.Gettext, locale)

    socket =
      socket
      |> assign(:page_title, gettext("Settings"))
      |> assign(:settings, settings)
      |> 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(:vereinfacht_test_result, nil)
      |> assign(:oidc_env_configured, Mv.Config.oidc_env_configured?())
      |> assign(:oidc_client_id_env_set, Mv.Config.oidc_client_id_env_set?())
      |> assign(:oidc_base_url_env_set, Mv.Config.oidc_base_url_env_set?())
      |> assign(:oidc_redirect_uri_env_set, Mv.Config.oidc_redirect_uri_env_set?())
      |> assign(:oidc_client_secret_env_set, Mv.Config.oidc_client_secret_env_set?())
      |> assign(:oidc_admin_group_name_env_set, Mv.Config.oidc_admin_group_name_env_set?())
      |> assign(:oidc_groups_claim_env_set, Mv.Config.oidc_groups_claim_env_set?())
      |> assign(:oidc_only_env_set, Mv.Config.oidc_only_env_set?())
      |> assign(:oidc_configured, Mv.Config.oidc_configured?())
      |> assign(:oidc_client_secret_set, present?(settings.oidc_client_secret))
      |> 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 Name")}
          </.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 variant="neutral" size="sm">{gettext("(set)")}</.badge>
                  </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>
          <div class="mt-2 flex flex-wrap gap-2">
            <.button
              :if={Mv.Config.vereinfacht_configured?()}
              type="button"
              variant="outline"
              phx-click="test_vereinfacht_connection"
              phx-disable-with={gettext("Testing...")}
            >
              {gettext("Test Integration")}
            </.button>
            <.button
              :if={Mv.Config.vereinfacht_configured?()}
              type="button"
              variant="outline"
              phx-click="sync_vereinfacht_contacts"
              phx-disable-with={gettext("Syncing...")}
            >
              {gettext("Sync all members without Vereinfacht contact")}
            </.button>
          </div>
          <%= if @vereinfacht_test_result do %>
            <.vereinfacht_test_result result={@vereinfacht_test_result} />
          <% end %>
          <%= if @last_vereinfacht_sync_result do %>
            <.vereinfacht_sync_result result={@last_vereinfacht_sync_result} />
          <% end %>
        </.form>
      </.form_section>
      <%!-- OIDC Section --%>
      <.form_section title={gettext("OIDC")}>
        <%= if @oidc_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="oidc-form" phx-change="validate" phx-submit="save">
          <div class="grid gap-4">
            <.input
              field={@form[:oidc_client_id]}
              type="text"
              label={gettext("Client ID")}
              disabled={@oidc_client_id_env_set}
              placeholder={
                if(@oidc_client_id_env_set, do: gettext("From OIDC_CLIENT_ID"), else: "mv")
              }
            />
            <.input
              field={@form[:oidc_base_url]}
              type="text"
              label={gettext("Base URL")}
              disabled={@oidc_base_url_env_set}
              placeholder={
                if(@oidc_base_url_env_set,
                  do: gettext("From OIDC_BASE_URL"),
                  else: "http://localhost:8080/auth/v1"
                )
              }
            />
            <.input
              field={@form[:oidc_redirect_uri]}
              type="text"
              label={gettext("Redirect URI")}
              disabled={@oidc_redirect_uri_env_set}
              placeholder={
                if(@oidc_redirect_uri_env_set,
                  do: gettext("From OIDC_REDIRECT_URI"),
                  else: "http://localhost:4000/auth/user/oidc/callback"
                )
              }
            />
            <div class="form-control">
              <label class="label" for={@form[:oidc_client_secret].id}>
                <span class="label-text">{gettext("Client Secret")}</span>
                <%= if @oidc_client_secret_set do %>
                  <span class="label-text-alt">
                    <.badge variant="neutral" size="sm">{gettext("(set)")}</.badge>
                  </span>
                <% end %>
              </label>
              <.input
                field={@form[:oidc_client_secret]}
                type="password"
                label=""
                disabled={@oidc_client_secret_env_set}
                placeholder={
                  if(@oidc_client_secret_env_set,
                    do: gettext("From OIDC_CLIENT_SECRET"),
                    else:
                      if(@oidc_client_secret_set,
                        do: gettext("Leave blank to keep current"),
                        else: nil
                      )
                  )
                }
              />
            </div>
            <.input
              field={@form[:oidc_admin_group_name]}
              type="text"
              label={gettext("Admin group name")}
              disabled={@oidc_admin_group_name_env_set}
              placeholder={
                if(@oidc_admin_group_name_env_set,
                  do: gettext("From OIDC_ADMIN_GROUP_NAME"),
                  else: gettext("e.g. admin")
                )
              }
            />
            <.input
              field={@form[:oidc_groups_claim]}
              type="text"
              label={gettext("Groups claim")}
              disabled={@oidc_groups_claim_env_set}
              placeholder={
                if(@oidc_groups_claim_env_set,
                  do: gettext("From OIDC_GROUPS_CLAIM"),
                  else: "groups"
                )
              }
            />
            <div class="form-control">
              <label class="label cursor-pointer justify-start gap-2">
                <.input
                  field={@form[:oidc_only]}
                  type="checkbox"
                  class="checkbox checkbox-sm"
                  disabled={@oidc_only_env_set or not @oidc_configured}
                />
                <span class="label-text">
                  {gettext("Only OIDC sign-in (hide password login)")}
                  <%= if @oidc_only_env_set do %>
                    <span class="label-text-alt text-base-content/70 ml-1">
                      ({gettext("From OIDC_ONLY")})
                    </span>
                  <% end %>
                </span>
              </label>
              <p class="label-text-alt text-base-content/70 mt-1">
                {gettext(
                  "When enabled and OIDC is configured, the sign-in page shows only the Single Sign-On button."
                )}
              </p>
            </div>
          </div>
          <.button
            :if={
              not (@oidc_client_id_env_set and @oidc_base_url_env_set and
                     @oidc_redirect_uri_env_set and @oidc_client_secret_env_set and
                     @oidc_admin_group_name_env_set and @oidc_groups_claim_env_set and
                     @oidc_only_env_set)
            }
            phx-disable-with={gettext("Saving...")}
            variant="primary"
            class="mt-2"
          >
            {gettext("Save OIDC Settings")}
          </.button>
        </.form>
      </.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("test_vereinfacht_connection", _params, socket) do
    result = Mv.Vereinfacht.test_connection()
    {:noreply, assign(socket, :vereinfacht_test_result, result)}
  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}

        {flash_kind, flash_message} =
          if(errors_with_names == [],
            do: {:success, gettext("Synced %{count} member(s) to Vereinfacht.", count: synced)},
            else:
              {:warning,
               gettext("Synced %{count} member(s). %{error_count} failed.",
                 count: synced,
                 error_count: length(errors_with_names)
               )}
          )

        socket =
          socket
          |> assign(:last_vereinfacht_sync_result, result)
          |> put_flash(flash_kind, flash_message)

        {: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 / client secret so we do not overwrite stored secrets
    setting_params_clean =
      setting_params
      |> drop_blank_vereinfacht_api_key()
      |> drop_blank_oidc_client_secret()

    saves_vereinfacht = vereinfacht_params?(setting_params_clean)

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

        test_result =
          if saves_vereinfacht, do: Mv.Vereinfacht.test_connection(), else: nil

        socket =
          socket
          |> assign(:settings, fresh_settings)
          |> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
          |> assign(:oidc_client_secret_set, present?(fresh_settings.oidc_client_secret))
          |> assign(:oidc_configured, Mv.Config.oidc_configured?())
          |> assign(:vereinfacht_test_result, test_result)
          |> put_flash(:success, gettext("Settings updated successfully"))
          |> assign_form()

        {:noreply, socket}

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

  @vereinfacht_param_keys ~w[vereinfacht_api_url vereinfacht_api_key vereinfacht_club_id vereinfacht_app_url]

  defp vereinfacht_params?(params) when is_map(params) do
    Enum.any?(@vereinfacht_param_keys, &Map.has_key?(params, &1))
  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

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

      _ ->
        params
    end
  end

  defp assign_form(%{assigns: %{settings: settings}} = socket) do
    # Show ENV values in disabled fields (Vereinfacht and OIDC); never expose API key / client secret
    settings_display =
      settings
      |> merge_vereinfacht_env_values()
      |> merge_oidc_env_values()

    settings_for_form = %{
      settings_display
      | vereinfacht_api_key: nil,
        oidc_client_secret: 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 put_if_env_set(map, _key, false, _value), do: map
  defp put_if_env_set(map, key, true, value), do: Map.put(map, key, value)

  defp merge_vereinfacht_env_values(s) do
    s
    |> put_if_env_set(
      :vereinfacht_api_url,
      Mv.Config.vereinfacht_api_url_env_set?(),
      Mv.Config.vereinfacht_api_url()
    )
    |> put_if_env_set(
      :vereinfacht_club_id,
      Mv.Config.vereinfacht_club_id_env_set?(),
      Mv.Config.vereinfacht_club_id()
    )
    |> put_if_env_set(
      :vereinfacht_app_url,
      Mv.Config.vereinfacht_app_url_env_set?(),
      Mv.Config.vereinfacht_app_url()
    )
  end

  defp merge_oidc_env_values(s) do
    s
    |> put_if_env_set(
      :oidc_client_id,
      Mv.Config.oidc_client_id_env_set?(),
      Mv.Config.oidc_client_id()
    )
    |> put_if_env_set(
      :oidc_base_url,
      Mv.Config.oidc_base_url_env_set?(),
      Mv.Config.oidc_base_url()
    )
    |> put_if_env_set(
      :oidc_redirect_uri,
      Mv.Config.oidc_redirect_uri_env_set?(),
      Mv.Config.oidc_redirect_uri()
    )
    |> put_if_env_set(
      :oidc_admin_group_name,
      Mv.Config.oidc_admin_group_name_env_set?(),
      Mv.Config.oidc_admin_group_name()
    )
    |> put_if_env_set(
      :oidc_groups_claim,
      Mv.Config.oidc_groups_claim_env_set?(),
      Mv.Config.oidc_groups_claim()
    )
    |> put_if_oidc_only_env_set()
  end

  defp put_if_oidc_only_env_set(s) do
    if Mv.Config.oidc_only_env_set?() do
      Map.put(s, :oidc_only, Mv.Config.oidc_only?())
    else
      s
    end
  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, :any, required: true

  defp vereinfacht_test_result(%{result: {:ok, :connected}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-center gap-2 p-3 rounded-lg border border-success bg-success/10 text-success-aa text-sm">
      <.icon name="hero-check-circle" class="size-5 shrink-0" />
      <span>{gettext("Connection successful. API URL, API Key and Club ID are valid.")}</span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, :not_configured}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-center gap-2 p-3 rounded-lg border border-warning bg-warning/10 text-warning-aa text-sm">
      <.icon name="hero-exclamation-triangle" class="size-5 shrink-0" />
      <span>{gettext("Not configured. Please set API URL, API Key and Club ID.")}</span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:http, _status, :html_response}}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-start gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0 mt-0.5" />
      <span>
        {gettext(
          "Connection failed. The URL does not point to a Vereinfacht API (received HTML instead of JSON)."
        )}
      </span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:http, 401, _}}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-start gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0 mt-0.5" />
      <span>{gettext("Connection failed (HTTP 401): API key is invalid or missing.")}</span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:http, 403, _}}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-start gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0 mt-0.5" />
      <span>
        {gettext(
          "Connection failed (HTTP 403): Access denied. Please check the Club ID and API key permissions."
        )}
      </span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:http, 404, _}}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-start gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0 mt-0.5" />
      <span>
        {gettext(
          "Connection failed (HTTP 404): API endpoint not found. Please check the API URL (e.g. correct version path)."
        )}
      </span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:http, status, message}}} = assigns) do
    assigns = assign(assigns, :status, status)
    assigns = assign(assigns, :message, message)

    ~H"""
    <div class="mt-3 flex items-start gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0 mt-0.5" />
      <span>
        {gettext("Connection failed (HTTP %{status}):", status: @status)}
        <span class="ml-1">{@message}</span>
      </span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, {:request_failed, _reason}}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-center gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0" />
      <span>
        {gettext("Connection failed. Could not reach the API (network error or wrong URL).")}
      </span>
    </div>
    """
  end

  defp vereinfacht_test_result(%{result: {:error, _}} = assigns) do
    ~H"""
    <div class="mt-3 flex items-center gap-2 p-3 rounded-lg border border-error bg-error/10 text-error-aa text-sm">
      <.icon name="hero-x-circle" class="size-5 shrink-0" />
      <span>{gettext("Connection failed. Unknown error.")}</span>
    </div>
    """
  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
