<?php
namespace BLR\Backline\Core;

if (!defined('ABSPATH')) {
    exit;
}

final class SettingsPage {
    public const OPT_DASHBOARD_SLUG = 'blr_ba_dashboard_slug';
    public const OPT_ALLOWED_USERS = 'blr_ba_allowed_users';
    public const OPT_DEFAULT_FROM_NAME = 'blr_ba_default_from_name';
    public const OPT_DEFAULT_FROM_EMAIL = 'blr_ba_default_from_email';
    public const OPT_DEFAULT_REPLY_TO = 'blr_ba_default_reply_to';
    public const OPT_THEME_PRIMARY = 'blr_ba_theme_primary';
    public const OPT_REMOVE_DATA_ON_UNINSTALL = 'blr_ba_remove_data_on_uninstall';
    public const OPT_PLUGIN_VERSION = 'blr_ba_plugin_version';
    public const OPT_REWRITE_FLUSH_REQUIRED = 'blr_ba_rewrite_flush_required';
    public const OPT_SIDEBAR_LOGO_ID = 'blr_ba_sidebar_logo_id';
    public const OPT_ENABLED_MODULES = 'blr_ba_enabled_modules';
    public const OPT_OPERATOR_CAPS = 'blr_ba_operator_caps';
    public const OPT_USER_MODULE_ACCESS = 'blr_ba_user_module_access';
    public const OPT_USER_EXTRA_CAPS = 'blr_ba_user_extra_caps';
    public const USER_META_THEME_MODE = 'blr_ba_theme_mode';

    private const SETTINGS_GROUP = 'blr_band_admin_settings';
    private const DEFAULT_DASHBOARD_SLUG = 'blr-backline';
    private const DEFAULT_THEME_PRIMARY = '#f17600';
    private const DEFAULT_THEME_MODE = 'light';

    private const AVAILABLE_MODULES = [
        'discography',
        'contacts',
        'outreach',
        'events',
        'tasks',
        'finances',
        'notifications',
    ];

    private const DEFAULT_ENABLED_MODULES = self::AVAILABLE_MODULES;

    private const AVAILABLE_OPERATOR_CAPS = [
        'blr_ba_view_dashboard',
        'blr_ba_manage_discography',
        'blr_ba_manage_contacts',
        'blr_ba_manage_outreach',
        'blr_ba_manage_events',
        'blr_ba_manage_tasks',
        'blr_ba_manage_finances',
        'blr_ba_view_notifications',
        'blr_ba_view_settings',
        'blr_ba_edit_settings',
    ];

    private const DEFAULT_OPERATOR_CAPS = [
        'blr_ba_view_dashboard',
    ];

    private const AVAILABLE_EXTRA_CAPS = [
        'blr_ba_view_settings',
        'blr_ba_edit_settings',
    ];

    private const LEGACY_TRANSPORT_OPTIONS = [
        'blr_ba_mailer_mode',
        'blr_ba_smtp_host',
        'blr_ba_smtp_port',
        'blr_ba_smtp_encryption',
        'blr_ba_smtp_username',
        'blr_ba_smtp_password',
        'blr_ba_smtp_auth',
    ];

    public static function boot(): void {
        add_action('admin_init', [self::class, 'register_settings']);
        add_action('admin_post_blr_ba_save_admin_settings', [self::class, 'handle_admin_settings_save']);
        add_action('admin_post_blr_ba_save_frontend_settings', [self::class, 'handle_frontend_settings_save']);
        add_action('wp_ajax_blr_ba_set_theme_mode', [self::class, 'handle_theme_mode_save']);
    }

    public static function register_settings(): void {
        register_setting(self::SETTINGS_GROUP, self::OPT_DASHBOARD_SLUG, [
            'type' => 'string',
            'sanitize_callback' => static function ($value) {
                $current_slug = self::get_dashboard_slug();
                $value = self::normalize_dashboard_slug($value);
                $validation_error = self::dashboard_slug_validation_error($value, $current_slug);

                if ($validation_error !== '') {
                    add_settings_error(self::SETTINGS_GROUP, self::OPT_DASHBOARD_SLUG, $validation_error, 'error');
                    return $current_slug;
                }

                if ($value !== $current_slug) {
                    self::mark_rewrite_flush_required();
                }

                return $value;
            },
            'default' => self::DEFAULT_DASHBOARD_SLUG,
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_ALLOWED_USERS, [
            'type' => 'array',
            'sanitize_callback' => [self::class, 'sanitize_user_ids'],
            'default' => [],
        ]);
        register_setting(self::SETTINGS_GROUP, self::OPT_ENABLED_MODULES, [
            'type' => 'array',
            'sanitize_callback' => [self::class, 'sanitize_enabled_modules'],
            'default' => self::DEFAULT_ENABLED_MODULES,
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_OPERATOR_CAPS, [
            'type' => 'array',
            'sanitize_callback' => [self::class, 'sanitize_operator_caps'],
            'default' => self::DEFAULT_OPERATOR_CAPS,
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_USER_MODULE_ACCESS, [
            'type' => 'array',
            'sanitize_callback' => [self::class, 'sanitize_user_module_access'],
            'default' => [],
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_USER_EXTRA_CAPS, [
            'type' => 'array',
            'sanitize_callback' => [self::class, 'sanitize_user_extra_caps'],
            'default' => [],
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_SIDEBAR_LOGO_ID, [
            'type' => 'integer',
            'sanitize_callback' => [self::class, 'sanitize_image_attachment_id'],
            'default' => 0,
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_DEFAULT_FROM_NAME, [
            'type' => 'string',
            'sanitize_callback' => 'sanitize_text_field',
            'default' => '',
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_DEFAULT_FROM_EMAIL, [
            'type' => 'string',
            'sanitize_callback' => 'sanitize_email',
            'default' => '',
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_DEFAULT_REPLY_TO, [
            'type' => 'string',
            'sanitize_callback' => 'sanitize_email',
            'default' => '',
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_THEME_PRIMARY, [
            'type' => 'string',
            'sanitize_callback' => [self::class, 'sanitize_theme_primary'],
            'default' => self::DEFAULT_THEME_PRIMARY,
        ]);

        register_setting(self::SETTINGS_GROUP, self::OPT_REMOVE_DATA_ON_UNINSTALL, [
            'type' => 'boolean',
            'sanitize_callback' => static function ($value) {
                return empty($value) ? 0 : 1;
            },
            'default' => 0,
        ]);
    }

    public static function maybe_migrate_legacy_options(): void {
        $legacy_from_name = (string) get_option('blr_ba_smtp_from_name', '');
        $legacy_from_email = (string) get_option('blr_ba_smtp_from_email', '');
        $legacy_reply_to = (string) get_option('blr_ba_smtp_reply_to', '');

        if ((string) get_option(self::OPT_DEFAULT_FROM_NAME, '') === '' && $legacy_from_name !== '') {
            update_option(self::OPT_DEFAULT_FROM_NAME, sanitize_text_field($legacy_from_name));
        }

        if ((string) get_option(self::OPT_DEFAULT_FROM_EMAIL, '') === '' && $legacy_from_email !== '') {
            update_option(self::OPT_DEFAULT_FROM_EMAIL, sanitize_email($legacy_from_email));
        }

        if ((string) get_option(self::OPT_DEFAULT_REPLY_TO, '') === '' && $legacy_reply_to !== '') {
            update_option(self::OPT_DEFAULT_REPLY_TO, sanitize_email($legacy_reply_to));
        }

        foreach (array_merge(self::LEGACY_TRANSPORT_OPTIONS, ['blr_ba_smtp_from_name', 'blr_ba_smtp_from_email', 'blr_ba_smtp_reply_to']) as $option_name) {
            delete_option($option_name);
        }
    }

    public static function sanitize_user_ids($value): array {
        if (!is_array($value)) {
            return [];
        }

        $out = [];
        foreach ($value as $item) {
            $id = (int) $item;
            if ($id > 0) {
                $out[] = $id;
            }
        }

        return array_values(array_unique($out));
    }

    public static function module_labels(): array {
        return [
            'discography' => __('Discography', 'blr-backline'),
            'contacts' => __('Contacts', 'blr-backline'),
            'outreach' => __('Outreach', 'blr-backline'),
            'events' => __('Events', 'blr-backline'),
            'tasks' => __('Tasks', 'blr-backline'),
            'finances' => __('Finances', 'blr-backline'),
            'notifications' => __('Notifications', 'blr-backline'),
        ];
    }

    public static function capability_labels(): array {
        return [
            'blr_ba_view_dashboard' => __('View dashboard', 'blr-backline'),
            'blr_ba_manage_discography' => __('Manage discography', 'blr-backline'),
            'blr_ba_manage_contacts' => __('Manage contacts', 'blr-backline'),
            'blr_ba_manage_outreach' => __('Manage outreach', 'blr-backline'),
            'blr_ba_manage_events' => __('Manage events', 'blr-backline'),
            'blr_ba_manage_tasks' => __('Manage tasks', 'blr-backline'),
            'blr_ba_manage_finances' => __('Manage finances', 'blr-backline'),
            'blr_ba_view_notifications' => __('View notifications', 'blr-backline'),
            'blr_ba_view_settings' => __('View settings', 'blr-backline'),
            'blr_ba_edit_settings' => __('Edit settings', 'blr-backline'),
        ];
    }

    public static function module_capability_map(): array {
        return [
            'discography' => 'blr_ba_manage_discography',
            'contacts' => 'blr_ba_manage_contacts',
            'outreach' => 'blr_ba_manage_outreach',
            'events' => 'blr_ba_manage_events',
            'tasks' => 'blr_ba_manage_tasks',
            'finances' => 'blr_ba_manage_finances',
            'notifications' => 'blr_ba_view_notifications',
        ];
    }

    public static function sanitize_enabled_modules($value): array {
        $allowed = self::AVAILABLE_MODULES;
        if (!is_array($value)) {
            return self::DEFAULT_ENABLED_MODULES;
        }

        $normalized = array_values(array_unique(array_intersect(array_map('sanitize_key', $value), $allowed)));
        return !empty($normalized) ? $normalized : [];
    }

    public static function sanitize_operator_caps($value): array {
        $allowed = self::AVAILABLE_OPERATOR_CAPS;
        if (!is_array($value)) {
            return self::DEFAULT_OPERATOR_CAPS;
        }

        $normalized = array_values(array_unique(array_intersect(array_map('sanitize_key', $value), $allowed)));
        if (in_array('blr_ba_edit_settings', $normalized, true) && !in_array('blr_ba_view_settings', $normalized, true)) {
            $normalized[] = 'blr_ba_view_settings';
        }

        return $normalized;
    }


    public static function sanitize_user_module_access($value): array {
        if (!is_array($value)) {
            return [];
        }

        $allowed_modules = self::AVAILABLE_MODULES;
        $normalized = [];

        foreach ($value as $user_id => $modules) {
            $user_id = absint($user_id);
            if ($user_id <= 0 || !is_array($modules)) {
                continue;
            }

            $user_modules = array_values(array_unique(array_intersect(array_map('sanitize_key', $modules), $allowed_modules)));
            $normalized[$user_id] = $user_modules;
        }

        return $normalized;
    }

    public static function sanitize_user_extra_caps($value): array {
        if (!is_array($value)) {
            return [];
        }

        $allowed_caps = self::AVAILABLE_EXTRA_CAPS;
        $normalized = [];

        foreach ($value as $user_id => $caps) {
            $user_id = absint($user_id);
            if ($user_id <= 0 || !is_array($caps)) {
                continue;
            }

            $user_caps = array_values(array_unique(array_intersect(array_map('sanitize_key', $caps), $allowed_caps)));
            if (in_array('blr_ba_edit_settings', $user_caps, true) && !in_array('blr_ba_view_settings', $user_caps, true)) {
                $user_caps[] = 'blr_ba_view_settings';
            }

            $normalized[$user_id] = $user_caps;
        }

        return $normalized;
    }

    public static function sanitize_theme_primary($value): string {
        $color = self::normalize_hex_color((string) $value);
        return $color !== '' ? $color : self::DEFAULT_THEME_PRIMARY;
    }

    public static function sanitize_image_attachment_id($value): int {
        $attachment_id = absint($value);

        if ($attachment_id <= 0) {
            return 0;
        }

        if (!wp_attachment_is_image($attachment_id)) {
            return 0;
        }

        return Media::current_user_can_use_attachment($attachment_id) ? $attachment_id : 0;
    }

    public static function get_dashboard_slug(): string {
        return self::normalize_dashboard_slug((string) get_option(self::OPT_DASHBOARD_SLUG, self::DEFAULT_DASHBOARD_SLUG));
    }

    private static function normalize_dashboard_slug($value): string {
        $value = sanitize_title((string) $value);
        return $value !== '' ? $value : self::DEFAULT_DASHBOARD_SLUG;
    }

    private static function dashboard_slug_validation_error(string $slug, string $current_slug = ''): string {
        $slug = self::normalize_dashboard_slug($slug);
        $current_slug = self::normalize_dashboard_slug($current_slug);

        if ($slug === $current_slug) {
            return '';
        }

        $reserved_slugs = [
            'wp-admin',
            'wp-login',
            'wp-login.php',
            'wp-json',
            'xmlrpc.php',
            'feed',
            'comments',
            'trackback',
            'embed',
            'search',
            'author',
            'category',
            'tag',
            'attachment',
            'page',
            'preview',
            'robots.txt',
            'favicon.ico',
        ];

        if (in_array($slug, $reserved_slugs, true)) {
            return __('Choose a different dashboard slug. That path is reserved by WordPress.', 'blr-backline');
        }

        $content_id = url_to_postid(home_url('/' . $slug . '/'));
        if ($content_id > 0) {
            return __('Choose a different dashboard slug. That path is already used by existing site content.', 'blr-backline');
        }

        $page = get_page_by_path($slug, OBJECT, array_values(get_post_types(['publicly_queryable' => true], 'names')));
        if ($page instanceof \WP_Post) {
            return __('Choose a different dashboard slug. That path is already used by existing site content.', 'blr-backline');
        }

        foreach (get_post_types(['public' => true], 'objects') as $post_type) {
            if (!($post_type instanceof \WP_Post_Type)) {
                continue;
            }

            $archive_slug = '';
            if (is_string($post_type->has_archive) && $post_type->has_archive !== '') {
                $archive_slug = trim($post_type->has_archive, '/');
            } elseif (!empty($post_type->has_archive) && !empty($post_type->rewrite['slug'])) {
                $archive_slug = trim((string) $post_type->rewrite['slug'], '/');
            }

            if ($archive_slug === $slug) {
                return __('Choose a different dashboard slug. That path is already claimed by a public post type archive.', 'blr-backline');
            }

            $rewrite_slug = trim((string) ($post_type->rewrite['slug'] ?? ''), '/');
            if ($rewrite_slug === $slug) {
                return __('Choose a different dashboard slug. That path is already claimed by a public content type.', 'blr-backline');
            }
        }

        foreach (get_taxonomies(['public' => true], 'objects') as $taxonomy) {
            if (!($taxonomy instanceof \WP_Taxonomy)) {
                continue;
            }

            $rewrite_slug = trim((string) ($taxonomy->rewrite['slug'] ?? ''), '/');
            if ($rewrite_slug === $slug) {
                return __('Choose a different dashboard slug. That path is already claimed by a public taxonomy.', 'blr-backline');
            }
        }

        return '';
    }

    public static function mark_rewrite_flush_required(): void {
        update_option(self::OPT_REWRITE_FLUSH_REQUIRED, 1, false);
    }

    public static function rewrite_flush_required(): bool {
        return !empty(get_option(self::OPT_REWRITE_FLUSH_REQUIRED, 0));
    }

    public static function clear_rewrite_flush_required(): void {
        delete_option(self::OPT_REWRITE_FLUSH_REQUIRED);
    }

    public static function get_allowed_user_ids(): array {
        $value = get_option(self::OPT_ALLOWED_USERS, []);
        if (!is_array($value)) {
            return [];
        }

        return array_values(array_map('intval', $value));
    }

    public static function raw_default_from_name(): string {
        return sanitize_text_field((string) get_option(self::OPT_DEFAULT_FROM_NAME, ''));
    }

    public static function raw_default_from_email(): string {
        return sanitize_email((string) get_option(self::OPT_DEFAULT_FROM_EMAIL, ''));
    }

    public static function raw_default_reply_to(): string {
        return sanitize_email((string) get_option(self::OPT_DEFAULT_REPLY_TO, ''));
    }

    public static function default_from_name(): string {
        $value = trim(self::raw_default_from_name());
        return $value !== '' ? $value : (string) get_bloginfo('name');
    }

    public static function default_from_email(): string {
        $value = self::raw_default_from_email();
        if ($value !== '') {
            return $value;
        }

        return sanitize_email((string) get_option('admin_email'));
    }

    public static function default_reply_to(): string {
        $value = self::raw_default_reply_to();
        if ($value !== '') {
            return $value;
        }

        return self::default_from_email();
    }

    public static function theme_primary(): string {
        return self::sanitize_theme_primary((string) get_option(self::OPT_THEME_PRIMARY, self::DEFAULT_THEME_PRIMARY));
    }

    public static function normalize_theme_mode(string $value): string {
        $value = sanitize_key($value);
        return in_array($value, ['light', 'dark'], true) ? $value : self::DEFAULT_THEME_MODE;
    }

    public static function current_theme_mode(?int $user_id = null): string {
        $user_id = $user_id ?? get_current_user_id();

        if ($user_id <= 0) {
            return self::DEFAULT_THEME_MODE;
        }

        return self::normalize_theme_mode((string) get_user_meta($user_id, self::USER_META_THEME_MODE, true));
    }

    public static function set_theme_mode(int $user_id, string $mode): string {
        if ($user_id <= 0) {
            return self::DEFAULT_THEME_MODE;
        }

        $mode = self::normalize_theme_mode($mode);
        update_user_meta($user_id, self::USER_META_THEME_MODE, $mode);

        return $mode;
    }

    public static function theme_mode_boot_code(string $fallback_mode = self::DEFAULT_THEME_MODE, bool $prefer_server_mode = false): string {
        $fallback_mode = self::normalize_theme_mode($fallback_mode);
        $prefer_server_mode = $prefer_server_mode ? 'true' : 'false';
        $fallback_json = wp_json_encode($fallback_mode);

        return "(function(){var key='blrBaThemeMode';var mode={$fallback_json};try{if(!{$prefer_server_mode}){var stored=window.localStorage?window.localStorage.getItem(key):'';if(stored==='light'||stored==='dark'){mode=stored;}}else if(window.localStorage){window.localStorage.setItem(key,mode);}}catch(e){}var docEl=document.documentElement;if(docEl){docEl.setAttribute('data-blr-theme',mode);docEl.setAttribute('data-bs-theme',mode);}window.blrBaInitialThemeMode=mode;})();";
    }

    public static function handle_theme_mode_save(): void {
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => __('You must be logged in to save a theme preference.', 'blr-backline')], 403);
        }

        check_ajax_referer('blr_ba_theme_mode', 'nonce');

        $mode_raw = isset($_POST['theme_mode'])
            ? sanitize_text_field((string) wp_unslash($_POST['theme_mode']))
            : '';

        $mode = self::normalize_theme_mode($mode_raw);
        $saved_mode = self::set_theme_mode(get_current_user_id(), $mode);

        wp_send_json_success(['theme_mode' => $saved_mode]);
    }

    public static function remove_data_on_uninstall(): bool {
        return !empty(get_option(self::OPT_REMOVE_DATA_ON_UNINSTALL, 0));
    }

    public static function admin_settings_url(): string {
        return admin_url('admin.php?page=' . AdminMenu::MENU_SLUG);
    }

    public static function frontend_settings_url(): string {
        return Router::base_url() . 'settings/';
    }

    public static function enabled_modules(): array {
        $value = get_option(self::OPT_ENABLED_MODULES, self::DEFAULT_ENABLED_MODULES);
        return self::sanitize_enabled_modules(is_array($value) ? $value : []);
    }

    public static function is_module_enabled(string $module): bool {
        return in_array(sanitize_key($module), self::enabled_modules(), true);
    }


    public static function operator_caps(): array {
        $value = get_option(self::OPT_OPERATOR_CAPS, self::DEFAULT_OPERATOR_CAPS);
        return self::sanitize_operator_caps(is_array($value) ? $value : []);
    }

    public static function available_extra_caps(): array {
        return self::AVAILABLE_EXTRA_CAPS;
    }

    public static function user_module_access_map(): array {
        $value = get_option(self::OPT_USER_MODULE_ACCESS, []);
        return self::sanitize_user_module_access(is_array($value) ? $value : []);
    }

    public static function user_extra_caps_map(): array {
        $value = get_option(self::OPT_USER_EXTRA_CAPS, []);
        return self::sanitize_user_extra_caps(is_array($value) ? $value : []);
    }

    public static function user_enabled_modules(?int $user_id = null): array {
        $user_id = $user_id ?? get_current_user_id();
        $enabled_modules = self::enabled_modules();

        if ($user_id <= 0) {
            return [];
        }

        $map = self::user_module_access_map();
        if (array_key_exists($user_id, $map)) {
            return array_values(array_unique(array_intersect($map[$user_id], $enabled_modules)));
        }

        $legacy_caps = self::operator_caps();
        $module_cap_map = self::module_capability_map();
        $granted = [];

        foreach ($enabled_modules as $module) {
            $required_cap = $module_cap_map[$module] ?? '';
            if ($required_cap !== '' && in_array($required_cap, $legacy_caps, true)) {
                $granted[] = $module;
            }
        }

        return array_values(array_unique($granted));
    }

    public static function user_extra_caps(?int $user_id = null): array {
        $user_id = $user_id ?? get_current_user_id();

        if ($user_id <= 0) {
            return [];
        }

        $map = self::user_extra_caps_map();
        if (array_key_exists($user_id, $map)) {
            return $map[$user_id];
        }

        return array_values(array_intersect(self::operator_caps(), self::AVAILABLE_EXTRA_CAPS));
    }

    public static function allowed_operator_caps(?int $user_id = null): array {
        $caps = ['blr_ba_access', 'blr_ba_view_dashboard'];
        $extra_caps = self::user_extra_caps($user_id);

        foreach ($extra_caps as $cap) {
            $caps[] = $cap;
        }

        $module_cap_map = self::module_capability_map();

        foreach (self::user_enabled_modules($user_id) as $module) {
            $required_cap = $module_cap_map[$module] ?? '';
            if ($required_cap === '') {
                continue;
            }

            $caps[] = $required_cap;

            if ('discography' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_release')), array_values(Access::post_type_capabilities('blr_track')), array_values(Access::taxonomy_capabilities('blr_release_type')), array_values(Access::taxonomy_capabilities('blr_release_status')));
            }
            if ('contacts' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_contact')));
            }
            if ('outreach' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_outreach')), array_values(Access::post_type_capabilities('blr_outreach_log')));
            }
            if ('events' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_event')));
            }
            if ('tasks' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_task')));
            }
            if ('finances' === $module) {
                $caps = array_merge($caps, array_values(Access::post_type_capabilities('blr_finance_record')));
            }
        }

        return array_values(array_unique($caps));
    }

    public static function can_manage_frontend_settings(?int $user_id = null): bool {

        $user_id = $user_id ?? get_current_user_id();
        if (user_can($user_id, 'manage_options')) {
            return true;
        }

        if (!Access::user_can_access($user_id)) {
            return false;
        }

        return in_array('blr_ba_edit_settings', self::user_extra_caps($user_id), true);
    }

    public static function can_view_frontend_settings(?int $user_id = null): bool {
        $user_id = $user_id ?? get_current_user_id();
        if (user_can($user_id, 'manage_options')) {
            return true;
        }

        if (!Access::user_can_access($user_id)) {
            return false;
        }

        $caps = self::user_extra_caps($user_id);
        return in_array('blr_ba_view_settings', $caps, true) || in_array('blr_ba_edit_settings', $caps, true);
    }

    public static function admin_menu_capability(): string {
        return 'blr_ba_edit_settings';
    }

    public static function sidebar_logo_id(): int {
        return self::sanitize_image_attachment_id(get_option(self::OPT_SIDEBAR_LOGO_ID, 0));
    }

    public static function sidebar_logo_url(): string {
        $attachment_id = self::sidebar_logo_id();

        if ($attachment_id > 0) {
            $url = (string) wp_get_attachment_image_url($attachment_id, 'full');
            if ($url !== '') {
                return $url;
            }
        }

        return Plugin::instance()->url('assets/blr-ba/img/backline-icon.png');
    }

    private static function settings_view_data(): array {
        $dashboard_slug = self::get_dashboard_slug();
        $dashboard_url = home_url('/' . $dashboard_slug . '/');
        $enabled_modules = self::enabled_modules();

        return [
            'dashboard_slug' => $dashboard_slug,
            'dashboard_url' => $dashboard_url,
            'allowed_users' => self::get_allowed_user_ids(),
            'users' => get_users([
                'fields' => ['ID', 'display_name', 'user_email'],
                'orderby' => 'display_name',
                'order' => 'ASC',
            ]),
            'enabled_modules' => $enabled_modules,
            'module_labels' => self::module_labels(),
            'operator_caps' => self::operator_caps(),
            'user_module_access' => self::user_module_access_map(),
            'user_extra_caps' => self::user_extra_caps_map(),
            'capability_labels' => self::capability_labels(),
            'available_extra_caps' => self::AVAILABLE_EXTRA_CAPS,
            'default_from_name' => self::raw_default_from_name(),
            'default_from_email' => self::raw_default_from_email(),
            'default_reply_to' => self::raw_default_reply_to(),
            'wp_default_from_name' => (string) get_bloginfo('name'),
            'wp_default_from_email' => sanitize_email((string) get_option('admin_email')),
            'theme_primary' => self::theme_primary(),
            'sidebar_logo_id' => self::sidebar_logo_id(),
            'sidebar_logo_url' => self::sidebar_logo_url(),
            'remove_data_on_uninstall' => self::remove_data_on_uninstall(),
            'save_action' => admin_url('admin-post.php'),
            'settings_url' => self::frontend_settings_url(),
            'diagnostics' => [
                'allowed_user_count' => count(self::get_allowed_user_ids()),
                'module_count' => count($enabled_modules),
                'default_sender_ready' => self::default_from_email() !== '',
                'sidebar_logo_set' => self::sidebar_logo_id() > 0,
                'cron_ready' => wp_next_scheduled('blr_ba_outreach_queue_healthcheck') !== false,
                'disable_wp_cron' => defined('DISABLE_WP_CRON') && DISABLE_WP_CRON === true,
                'rewrite_flush_required' => self::rewrite_flush_required(),
            ],
        ];
    }


    public static function render_frontend_page(array $ctx): string {
        if (!self::can_view_frontend_settings()) {
            return Views::render('pages/not-allowed', $ctx);
        }

        $ctx['settings'] = self::settings_view_data();

        return Views::render('pages/settings', $ctx);
    }

    public static function handle_admin_settings_save(): void {
        if (!is_user_logged_in()) {
            wp_die(esc_html__('Forbidden', 'blr-backline'), 403);
        }

        if (!current_user_can(self::admin_menu_capability()) && !current_user_can('manage_options')) {
            wp_die(esc_html__('Forbidden', 'blr-backline'), 403);
        }

        check_admin_referer('blr_ba_save_admin_settings');

        $request = [
            'dashboard_slug_input' => isset($_POST[self::OPT_DASHBOARD_SLUG])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_DASHBOARD_SLUG]))
                : self::DEFAULT_DASHBOARD_SLUG,
            'allowed_users_input' => (isset($_POST[self::OPT_ALLOWED_USERS]) && is_array($_POST[self::OPT_ALLOWED_USERS]))
                ? array_map('absint', wp_unslash($_POST[self::OPT_ALLOWED_USERS]))
                : [],
            'enabled_modules_input' => (isset($_POST[self::OPT_ENABLED_MODULES]) && is_array($_POST[self::OPT_ENABLED_MODULES]))
                ? array_map('sanitize_key', wp_unslash($_POST[self::OPT_ENABLED_MODULES]))
                : [],
            'operator_caps_input' => (isset($_POST[self::OPT_OPERATOR_CAPS]) && is_array($_POST[self::OPT_OPERATOR_CAPS]))
                ? array_map('sanitize_key', wp_unslash($_POST[self::OPT_OPERATOR_CAPS]))
                : [],
            'user_module_access_input' => (isset($_POST[self::OPT_USER_MODULE_ACCESS]) && is_array($_POST[self::OPT_USER_MODULE_ACCESS]))
                ? map_deep(wp_unslash($_POST[self::OPT_USER_MODULE_ACCESS]), 'sanitize_text_field')
                : [],
            'user_extra_caps_input' => (isset($_POST[self::OPT_USER_EXTRA_CAPS]) && is_array($_POST[self::OPT_USER_EXTRA_CAPS]))
                ? map_deep(wp_unslash($_POST[self::OPT_USER_EXTRA_CAPS]), 'sanitize_text_field')
                : [],
            'from_name_raw' => isset($_POST[self::OPT_DEFAULT_FROM_NAME])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_DEFAULT_FROM_NAME]))
                : '',
            'from_email_raw' => isset($_POST[self::OPT_DEFAULT_FROM_EMAIL])
                ? sanitize_email((string) wp_unslash($_POST[self::OPT_DEFAULT_FROM_EMAIL]))
                : '',
            'reply_to_raw' => isset($_POST[self::OPT_DEFAULT_REPLY_TO])
                ? sanitize_email((string) wp_unslash($_POST[self::OPT_DEFAULT_REPLY_TO]))
                : '',
            'theme_primary_raw' => isset($_POST[self::OPT_THEME_PRIMARY])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_THEME_PRIMARY]))
                : '',
            'sidebar_logo_id' => array_key_exists(self::OPT_SIDEBAR_LOGO_ID, $_POST)
                ? self::sanitize_image_attachment_id(absint(wp_unslash($_POST[self::OPT_SIDEBAR_LOGO_ID])))
                : self::sidebar_logo_id(),
            'remove_data' => !empty($_POST[self::OPT_REMOVE_DATA_ON_UNINSTALL]) ? 1 : 0,
        ];

        self::process_settings_save('admin', $request);
    }

    public static function handle_frontend_settings_save(): void {
        if (!is_user_logged_in()) {
            wp_die(esc_html__('Forbidden', 'blr-backline'), 403);
        }

        if (!self::can_manage_frontend_settings()) {
            wp_safe_redirect(Router::not_allowed_url());
            exit;
        }

        check_admin_referer('blr_ba_save_frontend_settings');

        $request = [
            'dashboard_slug_input' => isset($_POST[self::OPT_DASHBOARD_SLUG])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_DASHBOARD_SLUG]))
                : self::DEFAULT_DASHBOARD_SLUG,
            'from_name_raw' => isset($_POST[self::OPT_DEFAULT_FROM_NAME])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_DEFAULT_FROM_NAME]))
                : '',
            'from_email_raw' => isset($_POST[self::OPT_DEFAULT_FROM_EMAIL])
                ? sanitize_email((string) wp_unslash($_POST[self::OPT_DEFAULT_FROM_EMAIL]))
                : '',
            'reply_to_raw' => isset($_POST[self::OPT_DEFAULT_REPLY_TO])
                ? sanitize_email((string) wp_unslash($_POST[self::OPT_DEFAULT_REPLY_TO]))
                : '',
            'theme_primary_raw' => isset($_POST[self::OPT_THEME_PRIMARY])
                ? sanitize_text_field((string) wp_unslash($_POST[self::OPT_THEME_PRIMARY]))
                : '',
            'sidebar_logo_id' => array_key_exists(self::OPT_SIDEBAR_LOGO_ID, $_POST)
                ? self::sanitize_image_attachment_id(absint(wp_unslash($_POST[self::OPT_SIDEBAR_LOGO_ID])))
                : self::sidebar_logo_id(),
            'remove_data' => self::remove_data_on_uninstall() ? 1 : 0,
        ];

        self::process_settings_save('frontend', $request);
    }

    private static function process_settings_save(string $context, array $request): void {
        $redirect = $context === 'admin' ? self::admin_settings_url() : self::frontend_settings_url();
        $dashboard_slug = self::normalize_dashboard_slug((string) ($request['dashboard_slug_input'] ?? self::DEFAULT_DASHBOARD_SLUG));

        $allowed_users = self::get_allowed_user_ids();
        $enabled_modules = self::enabled_modules();
        $operator_caps = self::operator_caps();
        $user_module_access = self::user_module_access_map();
        $user_extra_caps = self::user_extra_caps_map();

        if ('admin' === $context) {
            $allowed_users = self::sanitize_user_ids((array) ($request['allowed_users_input'] ?? []));
            $enabled_modules = self::sanitize_enabled_modules((array) ($request['enabled_modules_input'] ?? []));
            $operator_caps = self::sanitize_operator_caps((array) ($request['operator_caps_input'] ?? []));
            $user_module_access = self::sanitize_user_module_access((array) ($request['user_module_access_input'] ?? []));
            $user_extra_caps = self::sanitize_user_extra_caps((array) ($request['user_extra_caps_input'] ?? []));

            $allowed_lookup = array_fill_keys($allowed_users, true);

            $user_module_access = array_filter(
                $user_module_access,
                static function ($modules, $user_id) use ($allowed_lookup, $enabled_modules) {
                    return isset($allowed_lookup[(int) $user_id]) && !empty(array_intersect($modules, $enabled_modules));
                },
                ARRAY_FILTER_USE_BOTH
            );

            $user_extra_caps = array_filter(
                $user_extra_caps,
                static function ($caps, $user_id) use ($allowed_lookup) {
                    return isset($allowed_lookup[(int) $user_id]) && !empty($caps);
                },
                ARRAY_FILTER_USE_BOTH
            );
        }

        $from_name_raw = (string) ($request['from_name_raw'] ?? '');
        $from_email_raw = (string) ($request['from_email_raw'] ?? '');
        $reply_to_raw = (string) ($request['reply_to_raw'] ?? '');
        $theme_primary_raw = (string) ($request['theme_primary_raw'] ?? '');

        $sidebar_logo_id = isset($request['sidebar_logo_id'])
            ? self::sanitize_image_attachment_id((int) $request['sidebar_logo_id'])
            : self::sidebar_logo_id();
        $previous_sidebar_logo_id = self::sidebar_logo_id();
        $created_sidebar_logo_attachment_id = 0;

        $remove_data = !empty($request['remove_data']) ? 1 : 0;

        $from_name = $from_name_raw;
        $from_email = $from_email_raw;
        $reply_to = $reply_to_raw;
        $theme_primary = self::sanitize_theme_primary($theme_primary_raw);

        if (trim($from_email_raw) !== '' && $from_email === '') {
            wp_safe_redirect(add_query_arg(['err' => __('Enter a valid default From email address.', 'blr-backline')], $redirect));
            exit;
        }

        if (trim($reply_to_raw) !== '' && $reply_to === '') {
            wp_safe_redirect(add_query_arg(['err' => __('Enter a valid default Reply-To address.', 'blr-backline')], $redirect));
            exit;
        }

        if (trim($theme_primary_raw) !== '' && self::normalize_hex_color($theme_primary_raw) === '') {
            wp_safe_redirect(add_query_arg(['err' => __('Choose a valid hex theme color.', 'blr-backline')], $redirect));
            exit;
        }

        $current_dashboard_slug = self::get_dashboard_slug();
        $dashboard_slug_error = self::dashboard_slug_validation_error($dashboard_slug, $current_dashboard_slug);
        if ($dashboard_slug_error !== '') {
            wp_safe_redirect(add_query_arg(['err' => $dashboard_slug_error], $redirect));
            exit;
        }

        // Defer the physical upload until all unrelated settings validations pass.
        // Otherwise a validation error in another field could leave an orphaned media attachment.
        $sidebar_logo_upload = CrudHelpers::handle_upload(
            'sidebar_logo_file',
            0,
            CrudHelpers::image_mimes(),
            CrudHelpers::visual_max_file_size()
        );
        $sidebar_logo_upload_errors = CrudHelpers::get_last_upload_errors();
        if (!empty($sidebar_logo_upload_errors)) {
            wp_safe_redirect(add_query_arg(['err' => reset($sidebar_logo_upload_errors)], $redirect));
            exit;
        }

        if (is_array($sidebar_logo_upload) && !empty($sidebar_logo_upload['attachment_id'])) {
            $uploaded_sidebar_logo_id = self::sanitize_image_attachment_id((int) $sidebar_logo_upload['attachment_id']);
            if ($uploaded_sidebar_logo_id > 0) {
                $sidebar_logo_id = $uploaded_sidebar_logo_id;
                $created_sidebar_logo_attachment_id = $uploaded_sidebar_logo_id;
            }
        }

        if ($dashboard_slug !== $current_dashboard_slug) {
            self::mark_rewrite_flush_required();
        }

        update_option(self::OPT_DASHBOARD_SLUG, $dashboard_slug);
        update_option(self::OPT_ALLOWED_USERS, $allowed_users);
        update_option(self::OPT_ENABLED_MODULES, $enabled_modules);
        update_option(self::OPT_OPERATOR_CAPS, $operator_caps);
        update_option(self::OPT_USER_MODULE_ACCESS, $user_module_access);
        update_option(self::OPT_USER_EXTRA_CAPS, $user_extra_caps);
        update_option(self::OPT_DEFAULT_FROM_NAME, $from_name);
        update_option(self::OPT_DEFAULT_FROM_EMAIL, $from_email);
        update_option(self::OPT_DEFAULT_REPLY_TO, $reply_to);
        update_option(self::OPT_THEME_PRIMARY, $theme_primary);
        update_option(self::OPT_REMOVE_DATA_ON_UNINSTALL, $remove_data);
        update_option(self::OPT_SIDEBAR_LOGO_ID, $sidebar_logo_id);

        if ($previous_sidebar_logo_id > 0 && $previous_sidebar_logo_id != $sidebar_logo_id) {
            $was_plugin_owned = absint((string) get_post_meta($previous_sidebar_logo_id, '_blr_ba_managed_attachment', true)) === 1;
            if ($was_plugin_owned) {
                CrudHelpers::delete_attachment($previous_sidebar_logo_id);
            }
        }

        if ($created_sidebar_logo_attachment_id > 0) {
            update_post_meta($created_sidebar_logo_attachment_id, '_blr_ba_managed_attachment', 1);
            update_post_meta($created_sidebar_logo_attachment_id, '_blr_ba_owner_context', 'sidebar_logo');
        }

        wp_safe_redirect(add_query_arg(['updated' => '1'], $redirect));
        exit;
    }

    public static function theme_css_variables(): string {
        $primary = self::theme_primary();
        $rgb = self::hex_to_rgb($primary);
        $hover_rgb = self::mix_with_white($rgb, 0.12);
        $rgb_csv = self::rgb_to_csv($rgb);
        $hover_hex = self::rgb_to_hex($hover_rgb);
        $hover_rgb_csv = self::rgb_to_csv($hover_rgb);
        $selectors = implode(', ', [
            ':root',
            'html[data-bs-theme="light"]',
            'html[data-bs-theme="dark"]',
            'body[data-bs-theme="light"]',
            'body[data-bs-theme="dark"]',
            'html[data-blr-theme="light"]',
            'html[data-blr-theme="dark"]',
            'body[data-blr-theme="light"]',
            'body[data-blr-theme="dark"]',
        ]);

        return "{$selectors} {
"
            . "  --bs-primary: {$primary};
"
            . "  --bs-primary-rgb: {$rgb_csv};
"
            . "  --bs-link-color: {$primary};
"
            . "  --bs-link-color-rgb: {$rgb_csv};
"
            . "  --bs-link-hover-color: {$hover_hex};
"
            . "  --bs-link-hover-color-rgb: {$hover_rgb_csv};
"
            . "  --bs-focus-ring-color: rgba({$rgb_csv}, 0.25);
"
            . "  --bs-primary-bg-subtle: rgba({$rgb_csv}, 0.14);
"
            . "  --bs-primary-border-subtle: rgba({$rgb_csv}, 0.32);
"
            . "}";
    }

    public static function render_settings_page(): void {
        if (!current_user_can(self::admin_menu_capability()) && !current_user_can('manage_options')) {
            return;
        }

        $settings = self::settings_view_data();

        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only query args for admin notices.
        $updated = isset($_GET['updated']) ? absint((string) wp_unslash($_GET['updated'])) === 1 : false;
        $err = isset($_GET['err']) ? sanitize_text_field((string) wp_unslash($_GET['err'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Recommended

        include BLR_BA_DIR . 'app/Views/pages/admin-settings.php';
    }

    private static function normalize_hex_color(string $value): string {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        $color = sanitize_hex_color($value);
        if ($color === null || $color === false) {
            return '';
        }

        if (strlen($color) === 4) {
            $color = sprintf(
                '#%1$s%1$s%2$s%2$s%3$s%3$s',
                $color[1],
                $color[2],
                $color[3]
            );
        }

        return strtolower($color);
    }

    /**
     * @return array{0:int,1:int,2:int}
     */
    private static function hex_to_rgb(string $hex): array {
        $hex = ltrim(self::sanitize_theme_primary($hex), '#');

        return [
            hexdec(substr($hex, 0, 2)),
            hexdec(substr($hex, 2, 2)),
            hexdec(substr($hex, 4, 2)),
        ];
    }

    /**
     * @param array{0:int,1:int,2:int} $rgb
     * @return array{0:int,1:int,2:int}
     */
    private static function mix_with_white(array $rgb, float $amount): array {
        $amount = max(0, min(1, $amount));

        return [
            (int) round($rgb[0] + ((255 - $rgb[0]) * $amount)),
            (int) round($rgb[1] + ((255 - $rgb[1]) * $amount)),
            (int) round($rgb[2] + ((255 - $rgb[2]) * $amount)),
        ];
    }

    /**
     * @param array{0:int,1:int,2:int} $rgb
     */
    private static function rgb_to_csv(array $rgb): string {
        return implode(', ', array_map('intval', $rgb));
    }

    /**
     * @param array{0:int,1:int,2:int} $rgb
     */
    private static function rgb_to_hex(array $rgb): string {
        return sprintf('#%02x%02x%02x', $rgb[0], $rgb[1], $rgb[2]);
    }
}
