<?php

namespace MintyPHP\Service\Settings;

use MintyPHP\Repository\Auth\ApiTokenRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantService;

class AdminSettingsService
{
    public function __construct(
        private readonly SettingsDefaultsGateway $settingsDefaultsGateway,
        private readonly SettingsAppGateway $settingsAppGateway,
        private readonly SettingsApiPolicyGateway $settingsApiPolicyGateway,
        private readonly SettingsUserLifecycleGateway $settingsUserLifecycleGateway,
        private readonly SettingsSessionGateway $settingsSessionGateway,
        private readonly SettingsSystemAuditGateway $settingsSystemAuditGateway,
        private readonly SettingsFrontendTelemetryGateway $settingsFrontendTelemetryGateway,
        private readonly SettingsMicrosoftGateway $settingsMicrosoftGateway,
        private readonly SettingsSmtpGateway $settingsSmtpGateway,
        private readonly SettingsLoginPersistenceGateway $settingsLoginPersistenceGateway,
        private readonly SettingsMetadataGateway $settingsMetadataGateway,
        private readonly SettingCacheService $settingCacheService,
        private readonly TenantService $tenantService,
        private readonly RoleService $roleService,
        private readonly DepartmentService $departmentService,
        private readonly RememberTokenRepository $rememberTokenRepository,
        private readonly ApiTokenRepository $apiTokenRepository,
        private readonly SystemAuditService $systemAuditService
    ) {
    }

    public function buildPageData(): array
    {
        $tenants = $this->tenantService->list();
        $roles = $this->roleService->listActive();
        $departments = $this->departmentService->list();

        $sortByDescription = static fn (array $left, array $right): int =>
            strcmp((string) ($left['description'] ?? ''), (string) ($right['description'] ?? ''));
        usort($tenants, $sortByDescription);
        usort($roles, $sortByDescription);
        usort($departments, $sortByDescription);

        return [
            'tenants' => $tenants,
            'roles' => $roles,
            'departments' => $departments,
            'values' => [
                'default_tenant_id' => $this->settingsDefaultsGateway->getDefaultTenantId(),
                'default_role_id' => $this->settingsDefaultsGateway->getDefaultRoleId(),
                'default_department_id' => $this->settingsDefaultsGateway->getDefaultDepartmentId(),
                'app_title' => $this->settingsMetadataGateway->getValue(SettingKeys::APP_TITLE_KEY),
                'app_locale' => $this->settingsMetadataGateway->getValue(SettingKeys::APP_LOCALE_KEY),
                'app_theme' => $this->settingsMetadataGateway->getValue(SettingKeys::APP_THEME_KEY),
                'app_theme_user' => $this->settingsAppGateway->isUserThemeAllowed(),
                'app_registration' => $this->settingsAppGateway->isRegistrationEnabled(),
                'app_primary_color' => $this->settingsMetadataGateway->getValue(SettingKeys::APP_PRIMARY_COLOR_KEY),
                'api_token_default_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
                'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
                'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
                'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
                'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
                'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
                'microsoft_auto_remember_default' => $this->settingsLoginPersistenceGateway->isMicrosoftAutoRememberDefault(),
                'remember_token_lifetime_days' => $this->settingsLoginPersistenceGateway->getRememberTokenLifetimeDays(),
                'api_cors_allowed_origins' => $this->settingsApiPolicyGateway->getApiCorsAllowedOriginsText(),
                'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
                'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
                'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
                'frontend_telemetry_sample_rate' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
                'frontend_telemetry_allowed_events' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents(),
                'microsoft_shared_client_id' => $this->settingsMetadataGateway->getValue(SettingKeys::MICROSOFT_SHARED_CLIENT_ID_KEY),
                'microsoft_authority' => $this->settingsMicrosoftGateway->getMicrosoftAuthority(),
                'smtp_host' => $this->settingsMetadataGateway->getValue(SettingKeys::SMTP_HOST_KEY),
                'smtp_port' => $this->settingsMetadataGateway->getValue(SettingKeys::SMTP_PORT_KEY),
                'smtp_user' => $this->settingsMetadataGateway->getValue(SettingKeys::SMTP_USER_KEY),
                'smtp_secure' => $this->settingsSmtpGateway->getSmtpSecure(),
                'smtp_from' => $this->settingsMetadataGateway->getValue(SettingKeys::SMTP_FROM_KEY),
                'smtp_from_name' => $this->settingsMetadataGateway->getValue(SettingKeys::SMTP_FROM_NAME_KEY),
            ],
            'settings' => [
                'default_tenant' => $this->settingsMetadataGateway->get(SettingKeys::DEFAULT_TENANT_KEY),
                'default_role' => $this->settingsMetadataGateway->get(SettingKeys::DEFAULT_ROLE_KEY),
                'default_department' => $this->settingsMetadataGateway->get(SettingKeys::DEFAULT_DEPARTMENT_KEY),
                'app_title' => $this->settingsMetadataGateway->get(SettingKeys::APP_TITLE_KEY),
                'app_locale' => $this->settingsMetadataGateway->get(SettingKeys::APP_LOCALE_KEY),
                'app_theme' => $this->settingsMetadataGateway->get(SettingKeys::APP_THEME_KEY),
                'app_theme_user' => $this->settingsMetadataGateway->get(SettingKeys::APP_THEME_USER_KEY),
                'app_registration' => $this->settingsMetadataGateway->get(SettingKeys::APP_REGISTRATION_KEY),
                'app_primary_color' => $this->settingsMetadataGateway->get(SettingKeys::APP_PRIMARY_COLOR_KEY),
                'api_token_default_ttl_days' => $this->settingsMetadataGateway->get(SettingKeys::API_TOKEN_DEFAULT_TTL_DAYS_KEY),
                'api_token_max_ttl_days' => $this->settingsMetadataGateway->get(SettingKeys::API_TOKEN_MAX_TTL_DAYS_KEY),
                'user_inactivity_deactivate_days' => $this->settingsMetadataGateway->get(SettingKeys::USER_INACTIVITY_DEACTIVATE_DAYS_KEY),
                'user_inactivity_delete_days' => $this->settingsMetadataGateway->get(SettingKeys::USER_INACTIVITY_DELETE_DAYS_KEY),
                'session_idle_timeout_minutes' => $this->settingsMetadataGateway->get(SettingKeys::SESSION_IDLE_TIMEOUT_MINUTES_KEY),
                'session_absolute_timeout_hours' => $this->settingsMetadataGateway->get(SettingKeys::SESSION_ABSOLUTE_TIMEOUT_HOURS_KEY),
                'microsoft_auto_remember_default' => $this->settingsMetadataGateway->get(SettingKeys::MICROSOFT_AUTO_REMEMBER_DEFAULT_KEY),
                'remember_token_lifetime_days' => $this->settingsMetadataGateway->get(SettingKeys::REMEMBER_TOKEN_LIFETIME_DAYS_KEY),
                'api_cors_allowed_origins' => $this->settingsMetadataGateway->get(SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY),
                'system_audit_enabled' => $this->settingsMetadataGateway->get(SettingKeys::SYSTEM_AUDIT_ENABLED_KEY),
                'system_audit_retention_days' => $this->settingsMetadataGateway->get(SettingKeys::SYSTEM_AUDIT_RETENTION_DAYS_KEY),
                'frontend_telemetry_enabled' => $this->settingsMetadataGateway->get(SettingKeys::FRONTEND_TELEMETRY_ENABLED_KEY),
                'frontend_telemetry_sample_rate' => $this->settingsMetadataGateway->get(SettingKeys::FRONTEND_TELEMETRY_SAMPLE_RATE_KEY),
                'frontend_telemetry_allowed_events' => $this->settingsMetadataGateway->get(SettingKeys::FRONTEND_TELEMETRY_ALLOWED_EVENTS_KEY),
                'microsoft_shared_client_id' => $this->settingsMetadataGateway->get(SettingKeys::MICROSOFT_SHARED_CLIENT_ID_KEY),
                'microsoft_shared_client_secret_enc' => $this->settingsMetadataGateway->get(SettingKeys::MICROSOFT_SHARED_CLIENT_SECRET_ENC_KEY),
                'microsoft_authority' => $this->settingsMetadataGateway->get(SettingKeys::MICROSOFT_AUTHORITY_KEY),
                'smtp_host' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_HOST_KEY),
                'smtp_port' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_PORT_KEY),
                'smtp_user' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_USER_KEY),
                'smtp_password' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_PASSWORD_KEY),
                'smtp_secure' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_SECURE_KEY),
                'smtp_from' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_FROM_KEY),
                'smtp_from_name' => $this->settingsMetadataGateway->get(SettingKeys::SMTP_FROM_NAME_KEY),
            ],
            'active_login_tokens' => $this->rememberTokenRepository->countActive(),
            'active_api_tokens' => $this->apiTokenRepository->countActive(),
        ];
    }

    public function updateFromAdmin(array $post): array
    {
        $defaultTenantId = (int) ($post['default_tenant_id'] ?? 0);
        $defaultRoleId = (int) ($post['default_role_id'] ?? 0);
        $defaultDepartmentId = (int) ($post['default_department_id'] ?? 0);
        $appTitle = trim((string) ($post['app_title'] ?? ''));
        $appLocale = trim((string) ($post['app_locale'] ?? ''));
        $appTheme = trim((string) ($post['app_theme'] ?? ''));
        $appThemeUser = isset($post['app_theme_user']);
        $appRegistration = isset($post['app_registration']);
        $apiTokenDefaultTtlDays = (int) ($post['api_token_default_ttl_days'] ?? 0);
        $apiTokenMaxTtlDays = (int) ($post['api_token_max_ttl_days'] ?? 0);
        $userInactivityDeactivateDays = (int) ($post['user_inactivity_deactivate_days'] ?? 0);
        $userInactivityDeleteDays = (int) ($post['user_inactivity_delete_days'] ?? 0);
        $sessionIdleTimeoutMinutes = (int) ($post['session_idle_timeout_minutes'] ?? 0);
        $sessionAbsoluteTimeoutHours = (int) ($post['session_absolute_timeout_hours'] ?? 0);
        $microsoftAutoRememberDefault = isset($post['microsoft_auto_remember_default']);
        $rememberTokenLifetimeDays = (int) ($post['remember_token_lifetime_days'] ?? 0);
        $apiCorsAllowedOrigins = $this->normalizeScalarText($post['api_cors_allowed_origins'] ?? '');
        $systemAuditEnabled = isset($post['system_audit_enabled']);
        $systemAuditRetentionDays = (int) ($post['system_audit_retention_days'] ?? 0);
        $frontendTelemetryEnabled = isset($post['frontend_telemetry_enabled']);
        $frontendTelemetrySampleRate = $this->normalizeScalarText($post['frontend_telemetry_sample_rate'] ?? '');
        $frontendTelemetryAllowedEvents = $post['frontend_telemetry_allowed_events'] ?? [];
        $appPrimaryColor = $this->normalizePrimaryColor($post['app_primary_color'] ?? '');
        $smtpHost = trim((string) ($post['smtp_host'] ?? ''));
        $smtpPort = (int) ($post['smtp_port'] ?? 0);
        $smtpUser = trim((string) ($post['smtp_user'] ?? ''));
        $smtpPassword = (string) ($post['smtp_password'] ?? '');
        $smtpSecure = trim((string) ($post['smtp_secure'] ?? ''));
        $smtpFrom = trim((string) ($post['smtp_from'] ?? ''));
        $smtpFromName = trim((string) ($post['smtp_from_name'] ?? ''));
        $microsoftSharedClientId = trim((string) ($post['microsoft_shared_client_id'] ?? ''));
        $microsoftSharedClientSecret = (string) ($post['microsoft_shared_client_secret'] ?? '');
        $microsoftAuthority = trim((string) ($post['microsoft_authority'] ?? ''));

        $errors = [];
        $beforeAudit = [
            'default_tenant_id' => $this->settingsDefaultsGateway->getDefaultTenantId(),
            'default_role_id' => $this->settingsDefaultsGateway->getDefaultRoleId(),
            'default_department_id' => $this->settingsDefaultsGateway->getDefaultDepartmentId(),
            'app_locale' => $this->settingsAppGateway->getAppLocale(),
            'app_theme' => $this->settingsAppGateway->getAppTheme(),
            'app_theme_user' => $this->settingsAppGateway->isUserThemeAllowed(),
            'app_registration' => $this->settingsAppGateway->isRegistrationEnabled(),
            'app_primary_color' => $this->settingsAppGateway->getAppPrimaryColor(),
            'api_token_default_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
            'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
            'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
            'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
            'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
            'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
            'microsoft_auto_remember_default' => $this->settingsLoginPersistenceGateway->isMicrosoftAutoRememberDefault(),
            'remember_token_lifetime_days' => $this->settingsLoginPersistenceGateway->getRememberTokenLifetimeDays(),
            'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
            'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
            'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
            'frontend_telemetry_sample_rate' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
            'frontend_telemetry_allowed_events' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents(),
        ];

        $this->settingsDefaultsGateway->setDefaultTenantId($defaultTenantId > 0 ? $defaultTenantId : null);
        $this->settingsDefaultsGateway->setDefaultRoleId($defaultRoleId > 0 ? $defaultRoleId : null);
        $this->settingsDefaultsGateway->setDefaultDepartmentId($defaultDepartmentId > 0 ? $defaultDepartmentId : null);
        $this->settingsAppGateway->setAppTitle($appTitle);
        $this->settingsAppGateway->setAppLocale($appLocale);
        $this->settingsAppGateway->setAppTheme($appTheme);
        $this->settingsAppGateway->setUserThemeAllowed($appThemeUser);
        $this->settingsAppGateway->setRegistrationEnabled($appRegistration);

        $apiTokenMaxTtlOk = $this->settingsApiPolicyGateway->setApiTokenMaxTtlDays($apiTokenMaxTtlDays > 0 ? $apiTokenMaxTtlDays : null);
        if (!$apiTokenMaxTtlOk) {
            $errors[] = ['message' => 'API token max lifetime is invalid', 'key' => 'api_token_max_ttl_invalid'];
        }
        $apiTokenTtlOk = $this->settingsApiPolicyGateway->setApiTokenDefaultTtlDays($apiTokenDefaultTtlDays > 0 ? $apiTokenDefaultTtlDays : null);
        if (!$apiTokenTtlOk) {
            $errors[] = ['message' => 'API token default lifetime is invalid', 'key' => 'api_token_default_ttl_invalid'];
        }

        $userDeactivateOk = $this->settingsUserLifecycleGateway->setUserInactivityDeactivateDays($userInactivityDeactivateDays);
        if (!$userDeactivateOk) {
            $errors[] = [
                'message' => 'User inactivity deactivation period is invalid',
                'key' => 'user_inactivity_deactivate_days_invalid',
            ];
        }

        if ($userInactivityDeactivateDays > 0) {
            $userDeleteOk = $this->settingsUserLifecycleGateway->setUserInactivityDeleteDays($userInactivityDeleteDays);
            if (!$userDeleteOk) {
                $errors[] = [
                    'message' => 'User inactivity deletion period is invalid',
                    'key' => 'user_inactivity_delete_days_invalid',
                ];
            }
        } else {
            if ($userInactivityDeleteDays > 0) {
                $errors[] = [
                    'message' => 'Auto-delete is ignored while auto-deactivate is disabled',
                    'key' => 'user_inactivity_delete_requires_deactivate',
                ];
            }
            $this->settingsUserLifecycleGateway->setUserInactivityDeleteDays(0);
        }

        $sessionIdleOk = $this->settingsSessionGateway->setSessionIdleTimeoutMinutes(
            $sessionIdleTimeoutMinutes > 0 ? $sessionIdleTimeoutMinutes : null
        );
        if (!$sessionIdleOk) {
            $errors[] = ['message' => 'Session idle timeout is invalid', 'key' => 'session_idle_timeout_invalid'];
        }

        $sessionAbsoluteOk = $this->settingsSessionGateway->setSessionAbsoluteTimeoutHours(
            $sessionAbsoluteTimeoutHours > 0 ? $sessionAbsoluteTimeoutHours : null
        );
        if (!$sessionAbsoluteOk) {
            $errors[] = ['message' => 'Session absolute timeout is invalid', 'key' => 'session_absolute_timeout_invalid'];
        }

        $this->settingsLoginPersistenceGateway->setMicrosoftAutoRememberDefault($microsoftAutoRememberDefault);

        $rememberTtlOk = $this->settingsLoginPersistenceGateway->setRememberTokenLifetimeDays(
            $rememberTokenLifetimeDays > 0 ? $rememberTokenLifetimeDays : null
        );
        if (!$rememberTtlOk) {
            $errors[] = ['message' => 'Remember token lifetime is invalid', 'key' => 'remember_token_lifetime_days_invalid'];
        }

        $apiCorsOriginsOk = $this->settingsApiPolicyGateway->setApiCorsAllowedOrigins($apiCorsAllowedOrigins);
        if (!$apiCorsOriginsOk) {
            $errors[] = ['message' => 'CORS allowed origins are invalid', 'key' => 'api_cors_allowed_origins_invalid'];
        }

        $systemAuditEnabledOk = $this->settingsSystemAuditGateway->setSystemAuditEnabled($systemAuditEnabled);
        if (!$systemAuditEnabledOk) {
            $errors[] = ['message' => 'System audit setting is invalid', 'key' => 'system_audit_enabled_invalid'];
        }

        $systemAuditRetentionOk = $this->settingsSystemAuditGateway->setSystemAuditRetentionDays(
            $systemAuditRetentionDays > 0 ? $systemAuditRetentionDays : null
        );
        if (!$systemAuditRetentionOk) {
            $errors[] = ['message' => 'System audit retention is invalid', 'key' => 'system_audit_retention_invalid'];
        }

        $frontendTelemetryEnabledOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetryEnabled($frontendTelemetryEnabled);
        if (!$frontendTelemetryEnabledOk) {
            $errors[] = ['message' => 'Frontend telemetry enabled setting is invalid', 'key' => 'frontend_telemetry_enabled_invalid'];
        }

        $frontendTelemetryRateValue = null;
        if ($frontendTelemetrySampleRate !== '') {
            $frontendTelemetryRateValue = is_numeric($frontendTelemetrySampleRate)
                ? (float) $frontendTelemetrySampleRate
                : NAN;
        }
        $frontendTelemetrySampleRateOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetrySampleRate($frontendTelemetryRateValue);
        if (!$frontendTelemetrySampleRateOk) {
            $errors[] = ['message' => 'Frontend telemetry sample rate is invalid', 'key' => 'frontend_telemetry_sample_rate_invalid'];
        }

        $frontendTelemetryAllowedEventsOk = $this->settingsFrontendTelemetryGateway->setFrontendTelemetryAllowedEvents($frontendTelemetryAllowedEvents);
        if (!$frontendTelemetryAllowedEventsOk) {
            $errors[] = ['message' => 'Frontend telemetry allowed events are invalid', 'key' => 'frontend_telemetry_allowed_events_invalid'];
        }

        $primaryOk = $this->settingsAppGateway->setAppPrimaryColor($appPrimaryColor);
        if (!$primaryOk) {
            $appPrimaryColor = '';
            $errors[] = ['message' => 'Primary color is invalid', 'key' => 'app_primary_invalid'];
        }

        $this->settingsSmtpGateway->setSmtpHost($smtpHost);
        $this->settingsSmtpGateway->setSmtpPort($smtpPort > 0 ? $smtpPort : null);
        $this->settingsSmtpGateway->setSmtpUser($smtpUser);
        if (trim($smtpPassword) !== '') {
            $this->settingsSmtpGateway->setSmtpPassword($smtpPassword);
        }
        $this->settingsSmtpGateway->setSmtpSecure($smtpSecure);
        $this->settingsSmtpGateway->setSmtpFrom($smtpFrom);
        $this->settingsSmtpGateway->setSmtpFromName($smtpFromName);

        $msClientIdOk = $this->settingsMicrosoftGateway->setMicrosoftSharedClientId($microsoftSharedClientId);
        if (!$msClientIdOk) {
            $errors[] = ['message' => 'Microsoft client ID is invalid', 'key' => 'microsoft_client_id_invalid'];
        }

        $msAuthorityOk = $this->settingsMicrosoftGateway->setMicrosoftAuthority($microsoftAuthority);
        if (!$msAuthorityOk) {
            $errors[] = ['message' => 'Microsoft authority is invalid', 'key' => 'microsoft_authority_invalid'];
        }

        if (trim($microsoftSharedClientSecret) !== '') {
            $msSecretOk = $this->settingsMicrosoftGateway->setMicrosoftSharedClientSecret($microsoftSharedClientSecret);
            if (!$msSecretOk) {
                $errors[] = [
                    'message' => 'Microsoft client secret could not be encrypted',
                    'key' => 'microsoft_client_secret_invalid',
                ];
            }
        }

        $this->settingCacheService->update([
            'app_title' => $appTitle !== '' ? $appTitle : null,
            'app_locale' => $appLocale !== '' ? $appLocale : null,
            'app_theme' => $appTheme !== '' ? $appTheme : null,
            'app_theme_user' => $appThemeUser ? '1' : '0',
            'app_registration' => $appRegistration ? '1' : '0',
            'app_primary_color' => $appPrimaryColor !== '' ? $appPrimaryColor : null,
            'api_token_default_ttl_days' => (string) $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
            'api_token_max_ttl_days' => (string) $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
            'api_cors_allowed_origins' => $this->settingsMetadataGateway->getValue(SettingKeys::API_CORS_ALLOWED_ORIGINS_KEY),
            'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled() ? '1' : '0',
            'frontend_telemetry_sample_rate' => (string) $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
            'frontend_telemetry_allowed_events' => implode(',', $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents()),
        ]);

        $afterAudit = [
            'default_tenant_id' => $this->settingsDefaultsGateway->getDefaultTenantId(),
            'default_role_id' => $this->settingsDefaultsGateway->getDefaultRoleId(),
            'default_department_id' => $this->settingsDefaultsGateway->getDefaultDepartmentId(),
            'app_locale' => $this->settingsAppGateway->getAppLocale(),
            'app_theme' => $this->settingsAppGateway->getAppTheme(),
            'app_theme_user' => $this->settingsAppGateway->isUserThemeAllowed(),
            'app_registration' => $this->settingsAppGateway->isRegistrationEnabled(),
            'app_primary_color' => $this->settingsAppGateway->getAppPrimaryColor(),
            'api_token_default_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenDefaultTtlDays(),
            'api_token_max_ttl_days' => $this->settingsApiPolicyGateway->getApiTokenMaxTtlDays(),
            'user_inactivity_deactivate_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeactivateDays(),
            'user_inactivity_delete_days' => $this->settingsUserLifecycleGateway->getUserInactivityDeleteDays(),
            'session_idle_timeout_minutes' => $this->settingsSessionGateway->getSessionIdleTimeoutMinutes(),
            'session_absolute_timeout_hours' => $this->settingsSessionGateway->getSessionAbsoluteTimeoutHours(),
            'microsoft_auto_remember_default' => $this->settingsLoginPersistenceGateway->isMicrosoftAutoRememberDefault(),
            'remember_token_lifetime_days' => $this->settingsLoginPersistenceGateway->getRememberTokenLifetimeDays(),
            'system_audit_enabled' => $this->settingsSystemAuditGateway->isSystemAuditEnabled(),
            'system_audit_retention_days' => $this->settingsSystemAuditGateway->getSystemAuditRetentionDays(),
            'frontend_telemetry_enabled' => $this->settingsFrontendTelemetryGateway->isFrontendTelemetryEnabled(),
            'frontend_telemetry_sample_rate' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetrySampleRate(),
            'frontend_telemetry_allowed_events' => $this->settingsFrontendTelemetryGateway->getFrontendTelemetryAllowedEvents(),
        ];

        $outcome = $errors === [] ? 'success' : 'failed';
        $errorKeys = [];
        if ($errors !== []) {
            foreach ($errors as $error) {
                $key = trim((string) $error['key']);
                if ($key !== '') {
                    $errorKeys[] = $key;
                }
            }
        }

        $this->systemAuditService->record('admin.settings.update', $outcome, [
            'before' => $beforeAudit,
            'after' => $afterAudit,
            'error_code' => $errors === [] ? null : 'validation_error',
            'metadata' => $errors === [] ? [] : ['error_keys' => $errorKeys],
        ]);

        return ['errors' => $errors];
    }

    private function normalizeScalarText(mixed $value): string
    {
        if (is_array($value)) {
            return '';
        }

        return trim((string) $value);
    }

    private function normalizePrimaryColor(mixed $value): string
    {
        $color = $this->normalizeScalarText($value);
        if (in_array(strtolower($color), ['null', 'undefined'], true)) {
            return '';
        }

        return $color;
    }
}
