<?php
/**
 * Plugin Name: nexdirectory
 * Description: nexdirectory by Alex Player. Community events platform for WordPress with submissions, moderation, map view, and account management.
 * Plugin URI: https://nexdirectory.com/
 * Version: 2.0.0
 * Author: Alex Player
 * Author URI: https://nexdirectory.com/
 * License: GPL-2.0+
 * Text Domain: nexdirectory
 */

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

if ( ! function_exists( 'epw_fs' ) ) {
    function epw_fs() {
        global $epw_fs;

        if ( ! isset( $epw_fs ) ) {
            require_once dirname( __FILE__ ) . '/vendor/freemius/start.php';

            $epw_fs = fs_dynamic_init( array(
                'id'                  => '24509',
                'slug'                => 'nexdirectory',
                'premium_slug'        => 'nexdirectory-pro',
                'type'                => 'plugin',
                'public_key'          => 'pk_7cf1ae1d5904759747433aa5b8fba',
                'is_premium'          => true,
                'is_premium_only'     => true,
                'has_addons'          => false,
                'has_paid_plans'      => true,
                'is_org_compliant'    => false,
                'menu'                => array(
                    'slug'           => 'eep-settings',
                    'first-path'     => 'admin.php?page=eep-onboarding',
                    'support'        => false,
                ),
            ) );
        }

        return $epw_fs;
    }

    epw_fs();
    do_action( 'freemius_loaded' );
}

if (!defined('EEP_PLUGIN_FILE')) {
    define('EEP_PLUGIN_FILE', __FILE__);
}

if (!defined('EEP_DB_VERSION')) {
    define('EEP_DB_VERSION', '1.0.0');
}

if (!function_exists('eep_scalar_to_string')) {
    /**
     * Converts mixed values into a safe scalar string for sanitization helpers.
     *
     * @param mixed $value
     * @return string
     */
    function eep_scalar_to_string($value) {
        if (is_string($value)) {
            return $value;
        }
        if (is_scalar($value)) {
            return (string) $value;
        }
        if (is_object($value) && method_exists($value, '__toString')) {
            return (string) $value;
        }
        return '';
    }
}

if (!function_exists('eep_sanitize_text')) {
    /**
     * Null-safe text sanitization wrapper.
     *
     * @param mixed $value
     * @return string
     */
    function eep_sanitize_text($value) {
        return sanitize_text_field(eep_scalar_to_string($value));
    }
}

if (!function_exists('eep_sanitize_textarea')) {
    /**
     * Null-safe textarea sanitization wrapper.
     *
     * @param mixed $value
     * @return string
     */
    function eep_sanitize_textarea($value) {
        return sanitize_textarea_field(eep_scalar_to_string($value));
    }
}

if (!function_exists('eep_sanitize_title')) {
    /**
     * Null-safe title slug sanitization wrapper.
     *
     * @param mixed $title
     * @param mixed $fallback_title
     * @param string $context
     * @return string
     */
    function eep_sanitize_title($title, $fallback_title = '', $context = 'save') {
        return sanitize_title(
            eep_scalar_to_string($title),
            eep_scalar_to_string($fallback_title),
            sanitize_key((string) $context)
        );
    }
}

if (!function_exists('eep_strip_all_tags')) {
    /**
     * Null-safe wrapper for wp_strip_all_tags.
     *
     * @param mixed $text
     * @param bool $remove_breaks
     * @return string
     */
    function eep_strip_all_tags($text, $remove_breaks = false) {
        return wp_strip_all_tags(eep_scalar_to_string($text), (bool) $remove_breaks);
    }
}

if (!function_exists('eep_esc_url')) {
    /**
     * Null-safe wrapper for esc_url().
     *
     * @param mixed $url
     * @param array<int, string>|null $protocols
     * @param string $_context
     * @return string
     */
    function eep_esc_url($url, $protocols = null, $_context = 'display') {
        return esc_url(eep_scalar_to_string($url), $protocols, (string) $_context);
    }
}

if (!function_exists('eep_esc_url_raw')) {
    /**
     * Null-safe wrapper for esc_url_raw().
     *
     * @param mixed $url
     * @param array<int, string>|null $protocols
     * @return string
     */
    function eep_esc_url_raw($url, $protocols = null) {
        return esc_url_raw(eep_scalar_to_string($url), $protocols);
    }
}

if (!function_exists('eep_normalize_server_request_context')) {
    /**
     * Ensures core request globals are always strings.
     *
     * Prevents PHP 8.1+ deprecations in WordPress helpers like add_query_arg()
     * when hosting/proxy environments omit REQUEST_URI.
     *
     * @return void
     */
    function eep_normalize_server_request_context() {
        if (!isset($_SERVER['REQUEST_URI']) || !is_string($_SERVER['REQUEST_URI']) || $_SERVER['REQUEST_URI'] === '') {
            $_SERVER['REQUEST_URI'] = '/';
        }
        if (!isset($_SERVER['REQUEST_METHOD']) || !is_string($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] === '') {
            $_SERVER['REQUEST_METHOD'] = 'GET';
        }
        if (!isset($_SERVER['HTTP_HOST']) || !is_string($_SERVER['HTTP_HOST']) || trim($_SERVER['HTTP_HOST']) === '') {
            $home_host = wp_parse_url(home_url('/'), PHP_URL_HOST);
            $_SERVER['HTTP_HOST'] = is_string($home_host) && $home_host !== '' ? $home_host : 'localhost';
        }
        if (!isset($_SERVER['SERVER_NAME']) || !is_string($_SERVER['SERVER_NAME']) || trim($_SERVER['SERVER_NAME']) === '') {
            $_SERVER['SERVER_NAME'] = (string) $_SERVER['HTTP_HOST'];
        }
    }
}

if (!function_exists('eep_add_query_arg')) {
    /**
     * Null-safe wrapper for add_query_arg().
     *
     * @param mixed ...$args
     * @return string
     */
    function eep_add_query_arg(...$args) {
        eep_normalize_server_request_context();
        if (empty($args)) {
            return '';
        }

        if (is_array($args[0])) {
            $url = $args[1] ?? null;
            if ($url === null || $url === false) {
                $url = (string) ($_SERVER['REQUEST_URI'] ?? '/');
            }
            $args[1] = eep_scalar_to_string($url);
        } else {
            $url = $args[2] ?? null;
            if ($url === null || $url === false) {
                $url = (string) ($_SERVER['REQUEST_URI'] ?? '/');
            }
            $args[2] = eep_scalar_to_string($url);
        }

        return add_query_arg(...$args);
    }
}

if (!function_exists('eep_remove_query_arg')) {
    /**
     * Null-safe wrapper for remove_query_arg().
     *
     * @param mixed $key
     * @param mixed $query
     * @return string
     */
    function eep_remove_query_arg($key, $query = false) {
        eep_normalize_server_request_context();

        $normalized_key = $key;
        if (is_array($key)) {
            $normalized_key = array_map('eep_scalar_to_string', $key);
        } else {
            $normalized_key = eep_scalar_to_string($key);
        }

        if ($query === null || $query === false) {
            $query = (string) ($_SERVER['REQUEST_URI'] ?? '/');
        } else {
            $query = eep_scalar_to_string($query);
            if ($query === '') {
                $query = (string) ($_SERVER['REQUEST_URI'] ?? '/');
            }
        }

        return remove_query_arg($normalized_key, $query);
    }
}

eep_normalize_server_request_context();

require_once __DIR__ . '/includes/trait-eep-admin.php';
require_once __DIR__ . '/includes/trait-eep-content.php';
require_once __DIR__ . '/includes/trait-eep-frontend.php';
require_once __DIR__ . '/includes/trait-eep-monetization.php';

if (!function_exists('eep_register_core_cron_schedules')) {
    /**
     * Global fallback cron schedules to prevent invalid schedule errors.
     *
     * @param array<string, array<string, mixed>> $schedules
     * @return array<string, array<string, mixed>>
     */
    function eep_register_core_cron_schedules($schedules) {
        if (!is_array($schedules)) {
            $schedules = [];
        }

        if (!isset($schedules['eep_hourly'])) {
            $schedules['eep_hourly'] = [
                'interval' => HOUR_IN_SECONDS,
                'display' => __('Every Hour (EEP)', 'nexdirectory'),
            ];
        }

        if (!isset($schedules['eep_chatbot_interval'])) {
            $chatbot_hours = max(1, min(168, (int) get_option('eep_chatbot_reindex_interval_hours', 24)));
            $schedules['eep_chatbot_interval'] = [
                'interval' => $chatbot_hours * HOUR_IN_SECONDS,
                'display' => sprintf(__('Every %d hour(s) (EEP Chatbot)', 'nexdirectory'), $chatbot_hours),
            ];
        }

        return $schedules;
    }
}

add_filter('cron_schedules', 'eep_register_core_cron_schedules', 1);

if (!function_exists('eep_register_utilities_autoloader')) {
    /**
     * Registers an autoloader for SOLID utility service classes.
     *
     * @return void
     */
    function eep_register_utilities_autoloader() {
        static $registered = false;
        if ($registered) {
            return;
        }

        spl_autoload_register(static function ($class_name) {
            $class_name = is_string($class_name) ? trim($class_name) : '';
            if ($class_name === '' || strpos($class_name, 'EEP_') !== 0) {
                return;
            }

            $relative = 'class-' . strtolower(str_replace('_', '-', $class_name)) . '.php';
            $path = __DIR__ . '/includes/utilities/' . $relative;
            if (is_readable($path)) {
                require_once $path;
            }
        });

        $registered = true;
    }
}

eep_register_utilities_autoloader();

final class EEP_Plugin {
    const VERSION = '2.0.0';
    const POST_TYPE = 'nexdirectory_event';
    const TAX_TYPE = 'event_type';
    const TAX_MODE = 'event_mode';
    const TAX_TAG = 'event_tag';
    const TAX_PRICE = 'event_price_range';
    const RSVP_TABLE = 'eep_rsvps';
    const INBOX_THREADS_TABLE = 'eep_inbox_threads';
    const INBOX_MESSAGES_TABLE = 'eep_inbox_messages';
    const CHATBOT_EMBED_TABLE = 'eep_event_embeddings';
    const ANALYTICS_TABLE = 'eep_event_analytics';
    const BOOST_ORDERS_TABLE = 'eep_boost_orders';
    const TICKET_ORDERS_TABLE = 'eep_ticket_orders';
    private $is_generating_recurrences = false;

    use EEP_Admin_Trait;
    use EEP_Content_Trait;
    use EEP_Frontend_Trait;
    use EEP_Monetization_Trait;

    /**
     * Utility service keys used for runtime method delegation.
     *
     * @return array<int, string>
     */
    private function get_delegated_utility_service_keys() {
        return [
            'user_caps',
            'date',
            'media',
            'event_meta',
            'email',
            'notifier',
            'admin_ui',
            'chatbot',
            'chatbot_knowledge',
            'chatbot_ranking',
            'chatbot_state',
            'importer',
            'taxonomy',
            'nav',
            'spa',
            'logger',
            'analytics',
        ];
    }

    /**
     * Delegates missing legacy utility calls to DI services.
     *
     * @param string $name
     * @param array<int, mixed> $arguments
     * @return mixed|null
     */
    public function __call($name, $arguments) {
        $method = is_string($name) ? trim($name) : '';
        if ($method === '') {
            return null;
        }
        if (!function_exists('eep_get_bootstrap')) {
            return null;
        }

        $bootstrap = eep_get_bootstrap();
        if (!$bootstrap instanceof EEP_Plugin_Bootstrap || !method_exists($bootstrap, 'get_service')) {
            return null;
        }

        foreach ($this->get_delegated_utility_service_keys() as $service_key) {
            $service = $bootstrap->get_service($service_key);
            if (!is_object($service) || !is_callable([$service, $method])) {
                continue;
            }
            if ($service_key === 'spa' && $method === 'render_frontend_template') {
                $template_file = $arguments[0] ?? '';
                $context = [];
                if (isset($arguments[1]) && is_array($arguments[1])) {
                    $context = $arguments[1];
                }
                return $service->render_frontend_template($template_file, $context, $this);
            }
            return $service->{$method}(...$arguments);
        }

        return null;
    }

    /**
     * Wrapper so legacy method_exists checks continue to work after DI refactor.
     *
     * @param int $event_id
     * @param string $metric
     * @param array<string, mixed> $args
     * @return bool
     */
    public function record_analytics_event($event_id, $metric, array $args = []) {
        $result = $this->__call(__FUNCTION__, [$event_id, $metric, $args]);
        return (bool) $result;
    }

    /**
     * Wrapper so archive/map batch tracking resolves through analytics service.
     *
     * @param array<int, mixed> $event_ids
     * @param string $metric
     * @param array<string, mixed> $args
     * @return int
     */
    public function record_analytics_for_event_ids(array $event_ids, $metric, array $args = []) {
        $result = $this->__call(__FUNCTION__, [$event_ids, $metric, $args]);
        return is_numeric($result) ? (int) $result : 0;
    }

    /**
     * Platform analytics report wrapper.
     *
     * @param int $days
     * @return array<string, mixed>
     */
    public function get_platform_analytics_report($days = 30) {
        $result = $this->__call(__FUNCTION__, [$days]);
        return is_array($result) ? $result : [];
    }

    /**
     * Owner analytics report wrapper.
     *
     * @param int $owner_id
     * @param int $days
     * @return array<string, mixed>
     */
    public function get_owner_analytics_report($owner_id, $days = 30) {
        $result = $this->__call(__FUNCTION__, [$owner_id, $days]);
        return is_array($result) ? $result : [];
    }

    /**
     * Top owner performance wrapper.
     *
     * @param string $since_gmt
     * @param int $limit
     * @return array<int, array<string, mixed>>
     */
    public function get_top_owner_performance_rows($since_gmt, $limit = 10) {
        $result = $this->__call(__FUNCTION__, [$since_gmt, $limit]);
        return is_array($result) ? $result : [];
    }

    public function __construct() {
        add_action('after_setup_theme', [$this, 'maybe_enable_theme_menu_support_for_wp_menu_source'], 100);
        add_action('init', [self::class, 'add_roles'], 1);
        add_action('init', [$this, 'register_content']);
        add_action('init', [$this, 'register_shortcodes']);
        add_action('init', [$this, 'register_query_vars']);
        add_action('init', [$this, 'maybe_upgrade_schema'], 20);
        add_action('elementor/elements/categories_registered', [$this, 'register_elementor_category']);
        add_action('elementor/widgets/register', [$this, 'register_elementor_widgets']);
        add_action('admin_menu', [$this, 'register_settings_page']);
        add_action('admin_menu', [$this, 'register_wp_menu_source_appearance_menu'], 99);
        add_action('admin_init', [$this, 'maybe_redirect_unlicensed_pricing_page'], 1);
        add_action('admin_init', [$this, 'register_settings']);
        add_action('load-post-new.php', [$this, 'redirect_event_add_new_to_frontend_submit']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_page_assets']);
        add_action('admin_notices', [$this, 'render_admin_ticket_purchase_notices']);
        add_action('wp_enqueue_scripts', [$this, 'enqueue_assets'], 99);
        add_action('admin_post_eep_submit_event', [$this, 'handle_submit_event']);
        add_action('admin_post_nopriv_eep_submit_event', [$this, 'handle_submit_event']);
        add_action('admin_post_eep_update_event', [$this, 'handle_update_event']);
        add_action('admin_post_eep_update_event_webapp', [$this, 'handle_update_event_webapp']);
        add_action('admin_post_eep_invite_event_team_member', [$this, 'handle_invite_event_team_member']);
        add_action('admin_post_eep_update_event_team_member', [$this, 'handle_update_event_team_member']);
        add_action('admin_post_eep_remove_event_team_member', [$this, 'handle_remove_event_team_member']);
        add_action('admin_post_eep_accept_event_team_invite', [$this, 'handle_accept_event_team_invite']);
        add_action('admin_post_nopriv_eep_accept_event_team_invite', [$this, 'handle_accept_event_team_invite']);
        add_action('admin_post_eep_delete_event', [$this, 'handle_delete_event']);
        add_action('admin_post_eep_toggle_event_live_draft', [$this, 'handle_toggle_event_live_draft']);
        add_action('admin_post_eep_autosave_event', [$this, 'handle_autosave_event']);
        add_action('admin_post_eep_save_filters', [$this, 'handle_save_filters']);
        add_action('admin_post_eep_moderate_event', [$this, 'handle_moderate_event']);
        add_action('admin_post_eep_moderate_events_bulk', [$this, 'handle_moderate_events_bulk']);
        add_action('admin_post_eep_account_update', [$this, 'handle_account_update']);
        add_action('admin_post_eep_inbox_reply', [$this, 'handle_inbox_reply']);
        add_action('admin_post_eep_manage_user_role', [$this, 'handle_manage_user_role']);
        add_action('admin_post_eep_manage_user_event', [$this, 'handle_manage_user_event']);
        add_action('admin_post_eep_toggle_verified', [$this, 'handle_toggle_verified']);
        add_action('admin_post_eep_toggle_user_edit_trust', [$this, 'handle_toggle_user_edit_trust']);
        add_action('admin_post_eep_manage_user_note', [$this, 'handle_manage_user_note']);
        add_action('admin_post_eep_rsvp_event', [$this, 'handle_rsvp_event']);
        add_action('admin_post_eep_report_event', [$this, 'handle_report_event']);
        add_action('admin_post_eep_moderate_report', [$this, 'handle_moderate_report']);
        add_action('admin_post_eep_claim_listing', [$this, 'handle_claim_listing']);
        add_action('admin_post_nopriv_eep_claim_listing', [$this, 'handle_claim_listing']);
        add_action('admin_post_eep_moderate_claim', [$this, 'handle_moderate_claim']);
        add_action('admin_post_eep_contact_holder', [$this, 'handle_contact_holder']);
        add_action('admin_post_eep_resend_verification', [$this, 'handle_resend_verification']);
        add_action('admin_post_eep_import_events', [$this, 'handle_import_events']);
        add_action('admin_post_eep_import_demo_events', [$this, 'handle_import_demo_events']);
        add_action('admin_post_eep_export_events', [$this, 'handle_export_events']);
        add_action('admin_post_eep_export_demo_events', [$this, 'handle_export_demo_events']);
        add_action('admin_post_eep_export_event_attendees', [$this, 'handle_export_event_attendees']);
        add_action('admin_post_eep_email_event_attendees', [$this, 'handle_email_event_attendees']);
        add_action('admin_post_eep_export_platform_analytics', [$this, 'handle_export_platform_analytics']);
        add_action('admin_post_eep_export_owner_analytics', [$this, 'handle_export_owner_analytics']);
        add_action('admin_post_eep_upload_image', [$this, 'handle_upload_image']);
        add_action('admin_post_eep_send_test_email', [$this, 'handle_send_test_email']);
        add_action('admin_post_eep_test_map_connection', [$this, 'handle_test_map_connection']);
        add_action('admin_post_eep_test_captcha_connection', [$this, 'handle_test_captcha_connection']);
        add_action('admin_post_eep_test_routing_connection', [$this, 'handle_test_routing_connection']);
        add_action('admin_post_eep_reset_smooth_scroll_defaults', [$this, 'handle_reset_smooth_scroll_defaults']);
        add_action('admin_post_eep_chatbot_reindex_now', [$this, 'handle_chatbot_reindex_now']);
        add_action('admin_post_eep_chatbot_clear_index', [$this, 'handle_chatbot_clear_index']);
        add_action('admin_post_eep_chatbot_test_connection', [$this, 'handle_chatbot_test_connection']);
        add_action('admin_post_eep_generate_pages', [$this, 'handle_generate_pages']);
        add_action('admin_post_eep_open_transfer_owner', [$this, 'handle_open_transfer_owner']);
        add_action('admin_post_eep_reassign_listing_owner', [$this, 'handle_reassign_listing_owner']);
        add_action('admin_post_eep_regenerate_extension_app_pass', [$this, 'handle_regenerate_extension_app_pass']);
        add_action('admin_post_eep_white_label_unlock', [$this, 'handle_white_label_unlock']);
        add_action('admin_post_eep_white_label_lock', [$this, 'handle_white_label_lock']);
        add_action('admin_post_eep_white_label_reset_defaults', [$this, 'handle_white_label_reset_defaults']);
        add_action('admin_post_eep_white_label_force_reset', [$this, 'handle_white_label_force_reset']);
        add_action('admin_post_eep_mark_onboarding_complete', [$this, 'handle_mark_onboarding_complete']);
        add_action('admin_post_eep_save_onboarding_preferences', [$this, 'handle_onboarding_save_preferences']);
        add_action('admin_post_eep_set_onboarding_homepage', [$this, 'handle_set_onboarding_homepage']);
        add_action('admin_post_eep_dismiss_ticket_purchase_notice', [$this, 'handle_dismiss_ticket_purchase_notice']);
        add_action('admin_post_eep_boost_checkout', [$this, 'handle_boost_checkout']);
        add_action('admin_post_eep_ticket_checkout', [$this, 'handle_ticket_checkout']);
        add_action('admin_post_nopriv_eep_ticket_checkout', [$this, 'handle_ticket_checkout']);
        add_action('admin_post_eep_boost_toggle_autorenew', [$this, 'handle_toggle_boost_autorenew']);
        add_action('admin_post_eep_boost_cancel_subscription', [$this, 'handle_cancel_boost_subscription']);
        add_action('admin_post_eep_auth_login', [$this, 'handle_login']);
        add_action('admin_post_nopriv_eep_auth_login', [$this, 'handle_login']);
        add_action('admin_post_eep_auth_signup', [$this, 'handle_signup']);
        add_action('admin_post_nopriv_eep_auth_signup', [$this, 'handle_signup']);
        add_action('admin_post_eep_auth_forgot_password', [$this, 'handle_forgot_password']);
        add_action('admin_post_nopriv_eep_auth_forgot_password', [$this, 'handle_forgot_password']);
        add_action('admin_post_eep_auth_reset_password', [$this, 'handle_reset_password']);
        add_action('admin_post_nopriv_eep_auth_reset_password', [$this, 'handle_reset_password']);
        add_action('transition_post_status', [$this, 'on_event_status_transition'], 10, 3);
        add_action('parse_request', [$this, 'handle_indexnow_key_request']);
        add_action('template_redirect', [$this, 'handle_calendar_export']);
        add_action('template_redirect', [$this, 'handle_event_webapp_pwa_asset_requests'], 1);
        add_action('template_redirect', [$this, 'handle_email_verification']);
        add_action('template_redirect', [$this, 'handle_boost_payment_return']);
        add_action('template_redirect', [$this, 'handle_ticket_payment_return']);
        add_action('template_redirect', [$this, 'handle_ticket_reference_lookup']);
        add_action('template_redirect', [$this, 'redirect_native_event_archive'], 1);
        add_action('eep_event_reminder_cron', [$this, 'send_event_reminders']);
        add_action('eep_send_single_reminder_email', [$this, 'send_single_reminder_email'], 10, 3);
        add_action('eep_chatbot_reindex_cron', [$this, 'run_chatbot_reindex_cron']);
        add_action('eep_boost_renewal_cron', [$this, 'run_boost_renewal_cron']);
        add_action('rest_api_init', [$this, 'register_rest_routes']);
        add_filter('cron_schedules', [$this, 'register_custom_cron_schedules']);
        add_filter('query_vars', [$this, 'add_query_vars']);
        add_filter('posts_clauses', [$this, 'filter_posts_clauses_for_boost_ranking'], 20, 2);
        add_filter('ajax_query_attachments_args', [$this, 'filter_frontend_media_library']);
        add_filter('all_plugins', [$this, 'filter_plugins_list_white_label']);
        add_filter('plugin_row_meta', [$this, 'filter_plugin_row_meta_white_label'], 10, 2);
        add_filter('admin_footer_text', [$this, 'filter_admin_footer_text'], 99);

        add_filter('single_template', [$this, 'single_template']);
        add_filter('the_content', [$this, 'append_event_meta_to_single']);
        add_filter('pre_get_document_title', [$this, 'filter_home_document_title'], 98);
        add_filter('pre_get_document_title', [$this, 'filter_single_event_document_title'], 99);
        add_action('wp_head', [$this, 'output_homepage_meta_tags'], 4);
        add_action('wp_head', [$this, 'output_single_event_meta_tags'], 5);
        add_action('wp_head', [$this, 'output_event_schema']);
    }


    public static function activate() {
        self::add_roles();
        $plugin = new self();
        add_option('eep_enable_frontend_sticky_nav', '1', '', false);
        add_option('eep_frontend_sticky_nav_include_auth_cta', '1', '', false);
        add_option('eep_frontend_sticky_nav_show_for_guests', '1', '', false);
        add_option('eep_frontend_sticky_nav_show_for_members', '1', '', false);
        $plugin->register_content();
        $plugin->ensure_default_terms();
        $plugin->ensure_default_pages();
        if (method_exists($plugin, 'sync_route_options_to_existing_pages')) {
            $plugin->sync_route_options_to_existing_pages();
        }
        $plugin->create_rsvp_table();
        $plugin->create_inbox_tables();
        $plugin->create_chatbot_embeddings_table();
        $plugin->create_analytics_table();
        $plugin->create_boost_orders_table();
        $plugin->create_ticket_orders_table();
        update_option('eep_db_version', EEP_DB_VERSION, false);
        update_option('eep_plugin_version', self::VERSION, false);
        $plugin->schedule_reminders();
        $plugin->schedule_boost_renewals();
        flush_rewrite_rules();
    }


    public static function deactivate() {
        wp_clear_scheduled_hook('eep_event_reminder_cron');
        wp_clear_scheduled_hook('eep_send_single_reminder_email');
        wp_clear_scheduled_hook('eep_chatbot_reindex_cron');
        wp_clear_scheduled_hook('eep_boost_renewal_cron');
        if (function_exists('as_unschedule_all_actions')) {
            as_unschedule_all_actions('eep_event_reminder_cron', [], 'eep');
            as_unschedule_all_actions('eep_send_single_reminder_email', [], 'eep');
            as_unschedule_all_actions('eep_chatbot_reindex_cron', [], 'eep');
            as_unschedule_all_actions('eep_boost_renewal_cron', [], 'eep');
        }
        flush_rewrite_rules();
    }


    public static function add_roles() {
        $custom_caps = [
            'eep_submit_events',
            'eep_moderate_events',
            'eep_manage_organizers',
        ];
        $submitter_can_submit = (string) get_option('eep_role_event_submitter_can_submit', '1') === '1';
        $author_can_submit = (string) get_option('eep_role_author_can_submit', '1') === '1';
        $editor_can_moderate = (string) get_option('eep_role_editor_can_moderate', '1') === '1';
        $editor_can_manage_organizers = (string) get_option('eep_role_editor_manage_organizers', '1') === '1';

        add_role(
            'event_submitter',
            __('Event Submitter', 'nexdirectory'),
            [
                'read' => true,
                'upload_files' => true,
                'eep_submit_events' => $submitter_can_submit,
            ]
        );

        $role = get_role('event_submitter');
        if ($role instanceof WP_Role) {
            $role->add_cap('read', true);
            $role->add_cap('upload_files', true);
            if ($submitter_can_submit) {
                $role->add_cap('eep_submit_events', true);
            } else {
                $role->remove_cap('eep_submit_events');
            }
        }

        foreach (['author', 'editor', 'administrator'] as $role_name) {
            $submit_role = get_role($role_name);
            if (!$submit_role instanceof WP_Role) {
                continue;
            }
            if ($role_name === 'author') {
                if ($author_can_submit) {
                    $submit_role->add_cap('eep_submit_events', true);
                } else {
                    $submit_role->remove_cap('eep_submit_events');
                }
            } else {
                $submit_role->add_cap('eep_submit_events', true);
            }
        }

        $editor_role = get_role('editor');
        if ($editor_role instanceof WP_Role) {
            if ($editor_can_moderate) {
                $editor_role->add_cap('eep_moderate_events', true);
            } else {
                $editor_role->remove_cap('eep_moderate_events');
            }
            if ($editor_can_manage_organizers) {
                $editor_role->add_cap('eep_manage_organizers', true);
            } else {
                $editor_role->remove_cap('eep_manage_organizers');
            }
        }

        $admin_role = get_role('administrator');
        if ($admin_role instanceof WP_Role) {
            foreach ($custom_caps as $cap_name) {
                $admin_role->add_cap($cap_name, true);
            }
        }
    }

}


if (!function_exists('eep_get_plugin_instance')) {
    function eep_get_plugin_instance() {
        return isset($GLOBALS['eep_plugin_instance']) && $GLOBALS['eep_plugin_instance'] instanceof EEP_Plugin
            ? $GLOBALS['eep_plugin_instance']
            : null;
    }
}

if (!function_exists('eep_bootstrap_services')) {
    /**
     * Initializes and runs the DI bootstrap container.
     *
     * @return EEP_Plugin_Bootstrap|null
     */
    function eep_bootstrap_services() {
        if (isset($GLOBALS['eep_bootstrap']) && $GLOBALS['eep_bootstrap'] instanceof EEP_Plugin_Bootstrap) {
            return $GLOBALS['eep_bootstrap'];
        }
        if (!class_exists('EEP_Plugin_Bootstrap')) {
            return null;
        }

        $bootstrap = new EEP_Plugin_Bootstrap();
        $bootstrap->run();
        $GLOBALS['eep_bootstrap'] = $bootstrap;

        return $bootstrap;
    }
}

if (!function_exists('eep_get_bootstrap')) {
    /**
     * Returns the plugin bootstrap container instance.
     *
     * @return EEP_Plugin_Bootstrap|null
     */
    function eep_get_bootstrap() {
        if (isset($GLOBALS['eep_bootstrap']) && $GLOBALS['eep_bootstrap'] instanceof EEP_Plugin_Bootstrap) {
            return $GLOBALS['eep_bootstrap'];
        }
        return eep_bootstrap_services();
    }
}

if (!function_exists('eep_is_app_page')) {
    /**
     * Returns true when current request is a plugin shortcode app page.
     *
     * @return bool
     */
    function eep_is_app_page() {
        $bootstrap = function_exists('eep_get_bootstrap') ? eep_get_bootstrap() : null;
        if ($bootstrap instanceof EEP_Plugin_Bootstrap && method_exists($bootstrap, 'get_service')) {
            $nav_service = $bootstrap->get_service('nav');
            if (is_object($nav_service) && is_callable([$nav_service, 'is_app_page'])) {
                return (bool) $nav_service->is_app_page();
            }
        }
        return false;
    }
}

if (!function_exists('eep_get_app_view')) {
    /**
     * Returns normalized app view key for current request.
     *
     * @return string
     */
    function eep_get_app_view() {
        $bootstrap = function_exists('eep_get_bootstrap') ? eep_get_bootstrap() : null;
        if ($bootstrap instanceof EEP_Plugin_Bootstrap && method_exists($bootstrap, 'get_service')) {
            $nav_service = $bootstrap->get_service('nav');
            if (is_object($nav_service) && is_callable([$nav_service, 'get_app_view'])) {
                return sanitize_key((string) $nav_service->get_app_view());
            }
        }
        return '';
    }
}

if (!function_exists('eep_run_uninstall_cleanup')) {
    /**
     * Cleanup one site on uninstall.
     *
     * @param bool $remove_data Whether to purge plugin data/content.
     * @return void
     */
    function eep_run_uninstall_cleanup($remove_data = false) {
        global $wpdb;

        $remove_data = (bool) $remove_data;

        wp_clear_scheduled_hook('eep_event_reminder_cron');
        wp_clear_scheduled_hook('eep_send_single_reminder_email');
        wp_clear_scheduled_hook('eep_chatbot_reindex_cron');
        wp_clear_scheduled_hook('eep_boost_renewal_cron');
        if (function_exists('as_unschedule_all_actions')) {
            as_unschedule_all_actions('eep_event_reminder_cron', [], 'eep');
            as_unschedule_all_actions('eep_send_single_reminder_email', [], 'eep');
            as_unschedule_all_actions('eep_chatbot_reindex_cron', [], 'eep');
            as_unschedule_all_actions('eep_boost_renewal_cron', [], 'eep');
        }

        // Preserve plugin configuration/content by default so re-install keeps settings.
        if (!$remove_data) {
            return;
        }

        $option_patterns = [
            'eep_%',
            '_transient_eep_%',
            '_transient_timeout_eep_%',
        ];
        foreach ($option_patterns as $pattern) {
            $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
                "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
                $pattern
            ));
        }

        $caps = [
            'eep_submit_events',
            'eep_moderate_events',
            'eep_manage_organizers',
        ];
        foreach (['administrator', 'editor', 'author', 'event_submitter'] as $role_name) {
            $role = get_role($role_name);
            if (!$role instanceof WP_Role) {
                continue;
            }
            foreach ($caps as $cap_name) {
                $role->remove_cap($cap_name);
            }
        }
        remove_role('event_submitter');

        if (function_exists('unregister_taxonomy')) {
            unregister_taxonomy('event_type');
            unregister_taxonomy('event_mode');
            unregister_taxonomy('event_tag');
            unregister_taxonomy('event_price_range');
        }
        if (function_exists('unregister_post_type')) {
            unregister_post_type('nexdirectory_event');
        }

        $post_ids = get_posts([
            'post_type' => 'nexdirectory_event',
            'post_status' => 'any',
            'numberposts' => -1,
            'fields' => 'ids',
            'suppress_filters' => true,
        ]);
        foreach ((array) $post_ids as $post_id) {
            wp_delete_post((int) $post_id, true);
        }

        $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s",
            'eep_%'
        ));
        $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s",
            'eep_%'
        ));
        $wpdb->query($wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
            "DELETE FROM {$wpdb->comments} WHERE comment_type = %s",
            'eep_report'
        ));

        $rsvp_table = $wpdb->prefix . 'eep_rsvps';
        $analytics_table = $wpdb->prefix . 'eep_event_analytics';
        $chatbot_embed_table = $wpdb->prefix . 'eep_event_embeddings';
        $boost_orders_table = $wpdb->prefix . 'eep_boost_orders';
        $ticket_orders_table = $wpdb->prefix . 'eep_ticket_orders';
        $wpdb->query("DROP TABLE IF EXISTS {$rsvp_table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query("DROP TABLE IF EXISTS {$analytics_table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query("DROP TABLE IF EXISTS {$chatbot_embed_table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query("DROP TABLE IF EXISTS {$boost_orders_table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query("DROP TABLE IF EXISTS {$ticket_orders_table}"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    }
}

if (!function_exists('epw_fs_uninstall_cleanup')) {
    /**
     * Freemius uninstall callback.
     *
     * @return void
     */
    function epw_fs_uninstall_cleanup() {
        $should_remove_data = (string) get_option('eep_uninstall_remove_data', '0') === '1';

        if (is_multisite()) {
            $site_ids = get_sites(['fields' => 'ids']);
            foreach ((array) $site_ids as $site_id) {
                switch_to_blog((int) $site_id);
                $site_remove_data = (string) get_option('eep_uninstall_remove_data', '0') === '1';
                eep_run_uninstall_cleanup($site_remove_data);
                restore_current_blog();
            }
        } else {
            eep_run_uninstall_cleanup($should_remove_data);
        }
    }
}

if (!function_exists('eep_register_freemius_uninstall_hook')) {
    /**
     * Attach plugin cleanup to Freemius after_uninstall event.
     *
     * @return void
     */
    function eep_register_freemius_uninstall_hook() {
        static $registered = false;
        if ($registered || !function_exists('epw_fs')) {
            return;
        }

        epw_fs()->add_action('after_uninstall', 'epw_fs_uninstall_cleanup');
        $registered = true;
    }
}

add_action('plugins_loaded', 'eep_register_freemius_uninstall_hook', 20);
add_action('plugins_loaded', 'eep_bootstrap_services', 5);

register_activation_hook(__FILE__, ['EEP_Plugin', 'activate']);
register_deactivation_hook(__FILE__, ['EEP_Plugin', 'deactivate']);
$GLOBALS['eep_plugin_instance'] = new EEP_Plugin();
