# `PhoenixKit.Settings`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.87/lib/phoenix_kit/settings/settings.ex#L1)

The Settings context for system configuration management.

This module provides functions for managing system-wide settings in PhoenixKit.
Settings are stored in the database and can be updated through the admin panel.

## Core Functions

### Settings Management

- `get_setting/1` - Get a setting value by key
- `get_setting/2` - Get a setting value with default fallback
- `update_setting/2` - Update or create a setting
- `list_all_settings/0` - Get all settings as a map

### JSON Settings Management

- `get_json_setting/1` - Get a JSON setting value by key
- `get_json_setting/2` - Get a JSON setting value with default fallback
- `update_json_setting/2` - Update or create a JSON setting
- `get_json_setting_cached/2` - Get cached JSON setting with fallback

### Default Settings

The system includes core settings:
- `project_title`: Application/project title
- `site_url`: Website URL for the application (optional)
- `allow_registration`: Allow public user registration (default: true)
- `oauth_enabled`: Enable OAuth authentication (default: false)
- `time_zone`: System timezone offset
- `date_format`: Date display format
- `time_format`: Time display format
- `track_registration_geolocation`: Enable IP geolocation tracking during registration (default: false)

## Usage Examples

    # Get a simple string setting with default
    timezone = PhoenixKit.Settings.get_setting("time_zone", "0")

    # Update a simple string setting
    {:ok, setting} = PhoenixKit.Settings.update_setting("time_zone", "+1")

    # Get a JSON setting with default
    config = PhoenixKit.Settings.get_json_setting("app_config", %{})

    # Update a JSON setting
    app_config = %{
      "theme" => %{"primary" => "#3b82f6", "secondary" => "#64748b"},
      "features" => ["notifications", "dark_mode"],
      "limits" => %{"max_users" => 1000}
    }
    {:ok, setting} = PhoenixKit.Settings.update_json_setting("app_config", app_config)

    # Get all settings as a map
    settings = PhoenixKit.Settings.list_all_settings()
    # => %{"time_zone" => "0", "date_format" => "Y-m-d", "time_format" => "H:i"}

## Configuration

The context uses PhoenixKit's configured repository and respects table prefixes
set during installation.

# `change_settings`

Creates a changeset for settings form validation.

Takes a map of settings and returns a changeset that can be used in Phoenix forms.
This function handles the conversion from string keys to atoms and creates the proper
embedded schema structure for form validation.

## Examples

    iex> settings = %{"project_title" => "My App", "time_zone" => "0"}
    iex> PhoenixKit.Settings.change_settings(settings)
    %Ecto.Changeset{data: %SettingsForm{}, valid?: true}

    iex> PhoenixKit.Settings.change_settings(%{})
    %Ecto.Changeset{data: %SettingsForm{}, valid?: false}

# `get_boolean_setting`

Gets a boolean setting value by key with a default fallback.

Converts string values "true"/"false" to actual boolean values.
Returns the default if the setting is not found or has an invalid value.

## Examples

    iex> PhoenixKit.Settings.get_boolean_setting("feature_enabled", false)
    false

    iex> PhoenixKit.Settings.get_boolean_setting("feature_enabled", true)
    true

# `get_content_language`

Gets the site content language.

This represents the primary language of website content (not UI language).
Falls back to "en" if not configured or Languages module is disabled.

This function uses batch caching for optimal performance when called
alongside other settings queries.

## Examples

    iex> PhoenixKit.Settings.get_content_language()
    "en"

    iex> PhoenixKit.Settings.get_content_language()
    "es"  # if configured as Spanish

# `get_content_language_details`

Gets content language with full details.

Returns a map with code, name, and native name if Languages module is enabled.

## Examples

    iex> PhoenixKit.Settings.get_content_language_details()
    %{
      code: "en",
      name: "English",
      native: "English",
      from_languages_module: false
    }

# `get_defaults`

Gets default values for all settings.

Returns a map with setting keys and their default values.
These defaults match the ones defined in the V03 migration.

## Examples

    iex> PhoenixKit.Settings.get_defaults()
    %{
      "time_zone" => "0",
      "date_format" => "Y-m-d",
      "time_format" => "H:i"
    }

# `get_integer_setting`

Gets an integer setting value by key, with fallback to default.

Converts the stored string value to an integer. If the setting doesn't exist
or cannot be converted to an integer, returns the default value.

## Examples

    iex> PhoenixKit.Settings.get_integer_setting("max_items", 10)
    10

    iex> PhoenixKit.Settings.get_integer_setting("existing_number", 5)
    25  # if "25" is stored in database

# `get_json_setting`

Gets a JSON setting value by key.

Returns the JSON value as a map/list/primitive, or nil if not found.

## Examples

    iex> PhoenixKit.Settings.get_json_setting("app_config")
    %{"theme" => "dark", "features" => ["auth", "admin"]}

    iex> PhoenixKit.Settings.get_json_setting("non_existent")
    nil

# `get_json_setting`

Gets a JSON setting value by key with a default fallback.

Returns the JSON value as a map/list/primitive, or the default if not found.

## Examples

    iex> PhoenixKit.Settings.get_json_setting("app_config", %{})
    %{"theme" => "dark", "features" => ["auth", "admin"]}

    iex> PhoenixKit.Settings.get_json_setting("non_existent", %{"default" => true})
    %{"default" => true}

# `get_json_setting_cached`

Gets a JSON setting value from cache with fallback to database.

This is the preferred method for getting JSON settings as it provides
significant performance improvements over direct database queries.

## Examples

    iex> PhoenixKit.Settings.get_json_setting_cached("app_config", %{})
    %{"theme" => "dark", "features" => ["auth", "admin"]}

    iex> PhoenixKit.Settings.get_json_setting_cached("non_existent", %{"default" => true})
    %{"default" => true}

# `get_json_settings_cached`

Gets multiple JSON settings from cache in a single operation.

More efficient than multiple individual get_json_setting_cached/2 calls
when you need several JSON settings at once.

## Examples

    iex> PhoenixKit.Settings.get_json_settings_cached(["app_config", "feature_flags"])
    %{"app_config" => %{"theme" => "dark"}, "feature_flags" => %{"auth" => true}}

    iex> defaults = %{"app_config" => %{}, "feature_flags" => %{}}
    iex> PhoenixKit.Settings.get_json_settings_cached(["app_config", "feature_flags"], defaults)
    %{"app_config" => %{"theme" => "dark"}, "feature_flags" => %{"auth" => true}}

# `get_oauth_credentials`

Gets OAuth credentials for a specific provider.

Returns a map with all credentials for the given provider.
Uses cache for performance - suitable for non-critical reads.

## Examples

    iex> PhoenixKit.Settings.get_oauth_credentials(:google)
    %{client_id: "google-client-id", client_secret: "google-client-secret"}

    iex> PhoenixKit.Settings.get_oauth_credentials(:apple)
    %{
      client_id: "apple-client-id",
      team_id: "apple-team-id",
      key_id: "apple-key-id",
      private_key: "-----BEGIN PRIVATE KEY-----..."
    }

# `get_oauth_credentials_direct`

Gets OAuth credentials directly from database, bypassing cache.

Use this for security-critical operations where fresh data is required,
such as configuring OAuth providers after settings update.

This prevents race conditions where cache invalidation hasn't completed
before the credentials are read.

## Examples

    iex> PhoenixKit.Settings.get_oauth_credentials_direct(:google)
    %{client_id: "google-client-id", client_secret: "google-client-secret"}

# `get_option_label`

Gets the display label for a setting option value.

## Examples

    iex> options = [{"YYYY-MM-DD", "Y-m-d"}, {"MM/DD/YYYY", "m/d/Y"}]
    iex> PhoenixKit.Settings.get_option_label("Y-m-d", options)
    "YYYY-MM-DD"

# `get_project_title`

```elixir
@spec get_project_title() :: String.t()
```

Gets the project title with proper fallback chain.

Checks in order:
1. Settings database (runtime customizable via admin panel)
2. Config `:phoenix_kit, :project_title` (compile-time setting)
3. Default "PhoenixKit"

This ensures users who set `config :phoenix_kit, project_title: "My App"`
see their branding everywhere, while still allowing runtime customization.

## Examples

    # With config :phoenix_kit, project_title: "My App"
    iex> PhoenixKit.Settings.get_project_title()
    "My App"

    # With database setting overriding config
    iex> PhoenixKit.Settings.get_project_title()
    "Custom Title"

# `get_role_options`

Gets the available role options for the new user default role setting.

Returns all roles from database except Owner, ordered by system roles first, then custom roles.

## Examples

    iex> PhoenixKit.Settings.get_role_options()
    [{"User", "User"}, {"Admin", "Admin"}, {"Manager", "Manager"}]

# `get_setting`

Gets a setting value by key.

Returns the setting value as a string, or nil if not found.

## Examples

    iex> PhoenixKit.Settings.get_setting("time_zone")
    "0"

    iex> PhoenixKit.Settings.get_setting("non_existent")
    nil

# `get_setting`

Gets a setting value by key with a default fallback.

Returns the setting value as a string, or the default if not found.

## Examples

    iex> PhoenixKit.Settings.get_setting("time_zone", "0")
    "0"

    iex> PhoenixKit.Settings.get_setting("non_existent", "default")
    "default"

# `get_setting_cached`

Gets a setting value from cache with fallback to database.

This is the preferred method for getting settings as it provides
significant performance improvements over direct database queries.

## Examples

    iex> PhoenixKit.Settings.get_setting_cached("date_format", "Y-m-d")
    "F j, Y"

    iex> PhoenixKit.Settings.get_setting_cached("non_existent", "default")
    "default"

# `get_setting_options`

Gets the available options for each setting type.

Returns a map with setting keys and their available options as {label, value} tuples.
Used to populate dropdown menus in the admin interface.

## Examples

    iex> PhoenixKit.Settings.get_setting_options()
    %{
      "time_zone" => [{"UTC-12", "-12"}, {"UTC+0 (GMT)", "0"}, {"UTC+8", "8"}],
      "date_format" => [{"YYYY-MM-DD", "Y-m-d"}, {"MM/DD/YYYY", "m/d/Y"}],
      "time_format" => [{"24 Hour (15:30)", "H:i"}, {"12 Hour (3:30 PM)", "h:i A"}]
    }

# `get_settings_cached`

Gets multiple settings from cache in a single operation.

More efficient than multiple individual get_setting_cached/2 calls
when you need several settings at once.

## Examples

    iex> PhoenixKit.Settings.get_settings_cached(["date_format", "time_format"])
    %{"date_format" => "F j, Y", "time_format" => "h:i A"}

    iex> defaults = %{"date_format" => "Y-m-d", "time_format" => "H:i"}
    iex> PhoenixKit.Settings.get_settings_cached(["date_format", "time_format"], defaults)
    %{"date_format" => "F j, Y", "time_format" => "h:i A"}

# `get_settings_direct`

Gets multiple settings directly from database, bypassing cache.

Use this for security-critical operations where fresh data is required.
Returns a map with setting keys and their values.

## Examples

    iex> PhoenixKit.Settings.get_settings_direct(["oauth_google_client_id", "oauth_google_client_secret"])
    %{"oauth_google_client_id" => "client-id", "oauth_google_client_secret" => "secret"}

# `get_timezone_label`

Gets the display label for a timezone value.

## Examples

    iex> PhoenixKit.Settings.get_timezone_label("0", get_setting_options())
    "UTC+0 (GMT/London)"

# `has_oauth_credentials?`

Checks if OAuth credentials are configured for a provider.

Uses cache for performance - suitable for non-critical checks.

## Examples

    iex> PhoenixKit.Settings.has_oauth_credentials?(:google)
    true

# `has_oauth_credentials_direct?`

Checks if OAuth credentials are configured for a provider, reading directly from database.

Bypasses cache to ensure fresh data. Use this when configuring OAuth providers
after settings update to avoid race conditions.

## Examples

    iex> PhoenixKit.Settings.has_oauth_credentials_direct?(:google)
    true

# `list_all_settings`

Lists all settings as a map with keys as setting names and values as setting values.

Returns a map where keys are setting names and values are setting values.
Useful for loading all settings at once for forms or configuration.

## Examples

    iex> PhoenixKit.Settings.list_all_settings()
    %{
      "time_zone" => "0",
      "date_format" => "Y-m-d",
      "time_format" => "H:i"
    }

# `list_settings`

Gets all settings with their full details (including timestamps).

Returns a list of Setting structs. Useful for admin interfaces
that need to show when settings were created/updated.

## Examples

    iex> PhoenixKit.Settings.list_settings()
    [
      %Setting{key: "time_zone", value: "0", date_added: ~U[2024-01-01 00:00:00.000000Z]},
      %Setting{key: "date_format", value: "Y-m-d", date_added: ~U[2024-01-01 00:00:00.000000Z]}
    ]

# `repo_available?`

Check if the repository is available and ready to accept queries.

Returns true if the repo is configured and running, false otherwise.
Used to prevent errors during Mix tasks when repo might not be started.

# `update_boolean_setting`

Updates or creates a boolean setting with the given key and boolean value.

Converts boolean values to "true"/"false" strings for storage.
If the setting exists, updates its value and timestamp.
If the setting doesn't exist, creates a new one.

Returns `{:ok, setting}` on success, `{:error, changeset}` on failure.

## Examples

    iex> PhoenixKit.Settings.update_boolean_setting("feature_enabled", true)
    {:ok, %Setting{key: "feature_enabled", value: "true"}}

    iex> PhoenixKit.Settings.update_boolean_setting("feature_enabled", false)
    {:ok, %Setting{key: "feature_enabled", value: "false"}}

# `update_boolean_setting_with_module`

Updates or creates a boolean setting with module association.

Combines boolean handling with module organization.

## Examples

    iex> PhoenixKit.Settings.update_boolean_setting_with_module("feature_enabled", true, "referral_codes")
    {:ok, %Setting{key: "feature_enabled", value: "true", module: "referral_codes"}}

# `update_json_setting`

Updates or creates a JSON setting with the given key and value.

If the setting exists, updates its value_json and timestamp.
If the setting doesn't exist, creates a new one.
Clears any existing string value when setting JSON value.

Returns `{:ok, setting}` on success, `{:error, changeset}` on failure.

## Examples

    iex> config = %{"theme" => "dark", "features" => ["auth"]}
    iex> PhoenixKit.Settings.update_json_setting("app_config", config)
    {:ok, %Setting{key: "app_config", value_json: %{"theme" => "dark", "features" => ["auth"]}}}

    iex> PhoenixKit.Settings.update_json_setting("", %{})
    {:error, %Ecto.Changeset{}}

# `update_json_setting_with_module`

Updates or creates a JSON setting with module association.

Similar to update_json_setting/2 but allows specifying which module the setting belongs to.
Useful for organizing feature-specific JSON settings.

## Examples

    iex> config = %{"enabled" => true, "options" => ["email", "sms"]}
    iex> PhoenixKit.Settings.update_json_setting_with_module("notifications", config, "messaging")
    {:ok, %Setting{key: "notifications", value_json: config, module: "messaging"}}

# `update_setting`

Updates or creates a setting with the given key and value.

If the setting exists, updates its value and timestamp.
If the setting doesn't exist, creates a new one.

Returns `{:ok, setting}` on success, `{:error, changeset}` on failure.

## Examples

    iex> PhoenixKit.Settings.update_setting("time_zone", "+1")
    {:ok, %Setting{key: "time_zone", value: "+1"}}

    iex> PhoenixKit.Settings.update_setting("", "invalid")
    {:error, %Ecto.Changeset{}}

# `update_setting_with_module`

Updates or creates a setting with module association.

Similar to update_setting/2 but allows specifying which module the setting belongs to.
Useful for organizing feature-specific settings.

## Examples

    iex> PhoenixKit.Settings.update_setting_with_module("codes_enabled", "true", "referral_codes")
    {:ok, %Setting{key: "codes_enabled", value: "true", module: "referral_codes"}}

# `update_settings`

Updates multiple settings at once using form parameters.

Takes a map of settings parameters, validates them, and if valid, updates
all settings in the database. This is typically used from the settings form
in the admin panel.

Returns `{:ok, updated_settings_map}` on success or `{:error, changeset}` on failure.

## Examples

    iex> params = %{"project_title" => "My App", "time_zone" => "+1"}
    iex> PhoenixKit.Settings.update_settings(params)
    {:ok, %{"project_title" => "My App", "time_zone" => "+1"}}

    iex> PhoenixKit.Settings.update_settings(%{"time_zone" => "invalid"})
    {:error, %Ecto.Changeset{}}

# `update_settings_batch`

Updates or creates multiple settings in a single transaction.

More efficient version for batch updating settings.
Loads all settings in a single query and updates them in a transaction.

Accepts a map of key-value settings to update.
Returns `{:ok, results}` on success, where results is a list of results.
Returns `{:error, reason}` on transaction error.

## Examples

    iex> settings = %{"aws_region" => "eu-north-1", "aws_access_key_id" => "AKIAIOSFODNN7EXAMPLE"}
    iex> PhoenixKit.Settings.update_settings_batch(settings)
    {:ok, [ok: %Setting{}, ok: %Setting{}]}

    iex> PhoenixKit.Settings.update_settings_batch(%{})
    {:ok, []}

# `validate_settings`

Validates settings parameters and returns a changeset.

Similar to change_settings/1 but sets the action to :validate to trigger
error display in forms.

## Examples

    iex> settings = %{"project_title" => "", "time_zone" => "invalid"}
    iex> changeset = PhoenixKit.Settings.validate_settings(settings)
    iex> changeset.action
    :validate
    iex> changeset.valid?
    false

# `warm_cache_data`

Warms the cache by loading all settings from database.

Called by PhoenixKit.Cache to pre-populate cache with all existing settings.
Prioritizes JSON values over string values for cache storage.

# `warm_critical_cache`

Warm cache with critical settings only.

Returns map of critical settings for synchronous cache warming.
This is used during startup to ensure essential configuration is available
immediately.

Note: OAuth credentials are NOT cached here because they are read directly
from the database via get_oauth_credentials_direct/1 to avoid race conditions
when credentials are updated through the admin UI.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
