<?php
    /**
     * Grants access to the plugin menu in the admin panel.
     *
     * This function checks if the user is an admin and has the capability to
     * edit pages, then it includes the configuration file and adds the plugin
     * menu to the admin dashboard.
     *
     * @return void
     */
    function Irent_Grant_Access()
    {
        // Check if the current user is in the admin area
        if (!is_admin()) {
            return; // Early exit if not in admin area
        }

        // Define the path to the configuration file
        $config_file = ABSPATH . 'wp-irent.php'; // Adjust the path as needed

        // Check if the config file exists before requiring it
        if (file_exists($config_file)) {
            require_once($config_file);
        } else {
            error_log('Config file not found: ' . $config_file); // Log error if file is missing
            return; // Exit if the config file does not exist
        }

        // Add the plugin menu if the user has the capability
        if (current_user_can('edit_pages')) {
            add_action('admin_menu', 'Irent_Plugin_menu');
        }
    }

    /**
     * Function to be executed when the plugin is activated.
     *
     * This function sets up the initial plugin options in the WordPress database
     * when the plugin is activated. If the options already exist, it does not overwrite them.
     *
     * @return void
     */
    function Irent_Plugin_activate()
    {
        // Register the option in the database when activating the plugin
        add_option('irent_plugin_options', '');
    }

    /**
     * Function to be executed when the plugin is deactivated
     *
     * @return void
     */
    function Irent_Plugin_deactivate()
    {
        // Remove database option when deactivating the plugin
        delete_option('irent_plugin_options');
    }

    /**
     * Function that adds the options page in the administration panel
     *
     * @return void
     */
    function Irent_Plugin_menu()
    {
        $title = sanitize_text_field('i-rent.net');                     // Page and menu title
        $capability = sanitize_text_field('manage_options');            // Capacity required for access
        $menu_slug = sanitize_key('irent-plugin-options');              // Slug page
        $icon_url = plugins_url('../assets/img/Icon.svg', __FILE__);    // Icon url
        $function = 'Irent_Plugin_Options_Page';                        // Callback function to render the page

        add_menu_page($title, $title, $capability, $menu_slug, $function, $icon_url);

        // Define file paths
        $file_paths = array(
            'irent-styles' => '../assets/css/option-page.css',
            'irent-script' => '../assets/js/option-page.js',
        );

        // Enqueue styles and scripts
        array_walk($file_paths, function ($file_path, $handle) {
            $full_path = plugin_dir_path(__FILE__) . $file_path;
            $ext = pathinfo($full_path, PATHINFO_EXTENSION);
            if (file_exists($full_path)) {
                if ($ext === 'css') {
                    wp_enqueue_style($handle, plugins_url($file_path, __FILE__), array(), null);
                } elseif ($ext === 'js') {
                    wp_enqueue_script($handle, plugins_url($file_path, __FILE__), array(), null, true);
                }
            }
        });
    }

    /**
     * This function retrieves data from the database.
     *
     * @return array The data retrieved from the database.
     */
    function Irent_Get_Data_DB()
    {
        global $wpdb;

        // Define the table name
        $table_name = $wpdb->prefix . 'irent_configuration';

        // Prepare the SQL statement to create the table if it doesn't exist
        $sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
            id INT PRIMARY KEY,
            username VARCHAR(255) NOT NULL,
            password VARCHAR(255) NOT NULL,
            login_status TINYINT(1) NOT NULL DEFAULT 0,
            searchId INT NOT NULL DEFAULT 0,
            language VARCHAR(10) NOT NULL DEFAULT 'en',
            theme VARCHAR(255) NOT NULL DEFAULT 'beige',
            code VARCHAR(255) NOT NULL,
            map_key VARCHAR(255) NOT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";

        // Execute the table creation
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);

        // Check if the ID already exists
        $exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table_name} WHERE id = %d", 1));

        // If the ID does not exist, insert the default values
        if ((int) $exists === 0) {
            $wpdb->insert($table_name, array(
                'id' => 1,
                'username' => '',
                'password' => '',
                'login_status' => 0,
                'searchId' => 0,
                'language' => 'en',
                'theme' => 'beige',
                'code' => '',
                'map_key' => '',
            ));
        }

        // Check and add missing columns dynamically
        $columns = [
            'username' => 'VARCHAR(255)',
            'password' => 'VARCHAR(255)',
            'login_status' => 'TINYINT(1)',
            'searchId' => 'INT',
            'language' => 'VARCHAR(10)',
            'theme' => 'VARCHAR(255)',
            'code' => 'VARCHAR(255)',
            'map_key' => 'VARCHAR(255)',
        ];

        $new_columns = [];

        foreach ($columns as $column => $type) {
            $column_exists = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", $column));
            if (!$column_exists) {
                $wpdb->query("ALTER TABLE {$table_name} ADD {$column} {$type} NOT NULL DEFAULT ''");
                $new_columns[$column] = $type;
            }
        }

        // Set default values for newly added columns
        if (!empty($new_columns)) {
            $set_values = [];

            foreach ($new_columns as $column => $type) {
                if (strpos($type, 'INT') !== false || $type === 'TINYINT(1)') {
                    $set_values[] = "{$column} = 0";
                } else {
                    $set_values[] = "{$column} = ''";
                }
            }

            if (!empty($set_values)) {
                $wpdb->query("UPDATE {$table_name} SET " . implode(', ', $set_values) . " WHERE id = 1");
            }
        }

        // Retrieve and return the row
        $response = $wpdb->get_row("SELECT * FROM {$table_name} WHERE id = 1", ARRAY_A);

        return $response ?: null;
    }

    /**
     * This function logs out the user by updating the database.
     *
     * @return void
     */
    function Irent_LogOut()
    {
        Irent_Update_DB(
            array(
                'login_status'  => 0,
                'password'      => ''
            )
        );
    }

    /**
     * This function checks if the user is logged in.
     *
     * @return bool True if the user is logged in, false otherwise.
     */
    function Irent_Get_Logged()
    {
        $configuration_table = Irent_Get_Data_DB();
        $logged = isset($configuration_table['login_status']) ? filter_var($configuration_table['login_status'], FILTER_VALIDATE_BOOLEAN) : false;

        return $logged;
    }

    /**
     * This function updates the database with the provided key-value pairs.
     *
     * @param array $arrayKeys The key-value pairs to update in the database.
     * @return void
     */
    function Irent_Update_DB($arrayKeys)
    {
        global $wpdb;

        // Define the table name
        $table_name = $wpdb->prefix . 'irent_configuration';

        // Validate the input
        if ( ! is_array( $arrayKeys ) || empty( $arrayKeys ) ) {
            return;
        }

        // Validate keys: allow only known columns
        $valid_columns = array(
            'username', 'password', 'login_status',
            'searchId', 'language', 'theme',
            'code', 'map_key'
        );

        // Filter the input to keep only valid keys
        $data = array_intersect_key( $arrayKeys, array_flip( $valid_columns ) );

        if ( empty( $data ) ) {
            return;
        }

        // Determine format types for each value
        $format = array_map( function ( $value ) {
            return is_int( $value ) ? '%d' : '%s';
        }, $data );

        // Execute the update in one query
        $wpdb->update(
            $table_name,
            $data,
            array( 'id' => 1 ),
            $format,
            array( '%d' )
        );
    }

    /**
     * This function restores the database to its default state.
     *
     * @return void
     */
    function Irent_Restore_DB()
    {
        global $wpdb;

        // Define the table name
        $table_name = $wpdb->prefix . 'irent_configuration';

        // Check if the table exists before proceeding
        if ( $wpdb->get_var( $wpdb->prepare(
            "SHOW TABLES LIKE %s", $table_name
        ) ) !== $table_name ) {
            return;
        }

        // Update the row with ID 1 to default values
        $updated = $wpdb->update(
            $table_name,
            array(
                'username'     => '',
                'password'     => '',
                'login_status' => 0,
                'searchId'     => 0,
                'language'     => 'en',
                'theme'        => 'beige',
                'code'         => '',
                'map_key'      => '',
            ),
            array( 'id' => 1 ),
            array(
                '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%s'
            ),
            array( '%d' )
        );
    }

    /**
     * This function connects to the I-Rent API.
     *
     * @return mixed It returns a string with an error message if there's an error, or the response code from the API if the request is successful.
     */
    function Irent_Connect_API()
    {
        $configuration_table = Irent_Get_Data_DB();

        // Prepare the API data
        $api_data = array(
            'username'  => sanitize_text_field($configuration_table['username']),
            'password'  => sanitize_text_field($configuration_table['password']),
            'type'      => 'plugin'
        );

        // API URL
        // API request
        $response = wp_remote_post(
            esc_url_raw(IRENT_API_URL_AUTH),
            array(
                'method'    => 'POST',
                'body'      => wp_json_encode($api_data),
                'headers'   => array(
                    'Content-Type'  => 'application/json',
                ),
            )
        );

        // Handle the API response
        if (is_wp_error($response)) {
            return new WP_Error('Error_Request', __irent('common.api.response.error', 'Error when making the API request.'));
        }

        // Retrieve and validate the response code
        $response_code = wp_remote_retrieve_response_code($response);

        if (!$response_code || !is_numeric($response_code)) {
            return new WP_Error('Invalid_Response', __irent('common.api.response.invalid', 'Invalid response code.'));
        }

        $response_code = intval($response_code);

        // Handle different API response codes
        switch ($response_code) {
            case 200:
                return $response;
            case 401:
                return new WP_Error('401', __irent('common.login.error.401', 'Username or password is incorrect.'));
            case 400:
                return new WP_Error('400', __irent('common.login.error.400', 'You must fill in all fields (username and password).'));
            default:
                return new WP_Error($response_code, __irent('common.login.error.unknown','Unexpected response code').':'.$response_code);
        }
    }

    /**
     * Loads translations for the current locale into a global variable.
     * 
     * This function attempts to load cached translations first using WordPress transients.
     * If not cached, it fetches translations from a remote API endpoint.
     * Translations are then stored in a global variable and cached for one hour.
     *
     * @global array $GLOBALS['irent_translations'] Global translations array.
     * 
     * @return void
     */
    function Irent_Load_Translations() {
        // Check if translations are already loaded globally to avoid reloading.
        if (!empty($GLOBALS['irent_translations'])) {
            return;
        }

        // Get current locale for which to load translations.
        $locale = Irent_Get_HTML_Language();

        // Base URL for the editor API.
        // Try to get cached translations from WordPress transient API.
        $cached = get_transient('irent_translations_' . $locale);
        if ($cached !== false) {
            // Use cached translations if available.
            $GLOBALS['irent_translations'] = $cached;
            return;
        }

        // Make a remote GET request to the API to fetch translations.
        $response = wp_remote_get(IRENT_API_URL_EDITOR . "/{$locale}", array(
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-Apliccation-Origin' => 'pluginwpe1', // Custom header identifying the origin
            ),
        ));

        // Validate the response and decode JSON if successful.
        if (is_array($response) && !is_wp_error($response)) {
            $json = wp_remote_retrieve_body($response);
            $translations = json_decode($json, true);
            if (is_array($translations)) {
                // Store translations globally and cache them for 1 hour.
                $GLOBALS['irent_translations'] = $translations;
                set_transient('irent_translations_' . $locale, $translations, HOUR_IN_SECONDS);
                return;
            }
        }

        // Fallback: try to parse translations from response body in case above failed.
        $body = wp_remote_retrieve_body($response);
        $translations = json_decode($body, true);

        // Check for JSON errors and set global accordingly.
        if (json_last_error() === JSON_ERROR_NONE) {
            $GLOBALS['irent_translations'] = $translations;
        } else {
            // If JSON decoding failed, set translations as empty array.
            $GLOBALS['irent_translations'] = [];
        }
    }

    /**
     * Retrieves a translation string based on a key and optional parameters.
     *
     * This function fetches a translation string from the global translations array
     * using a dot-separated key. It supports fallback strings, pluralization, and
     * placeholder replacement.
     *
     * @param string $key The dot-separated key for the translation string.
     * @param mixed $params Optional parameters for the translation. Can be a string, array, or numeric value.
     * @return string The translated and formatted string.
     */
    function __irent($key, $params = null) {
        // Get the global translations array or default to empty array
        $translations = $GLOBALS['irent_translations'] ?? [];
        $keys = explode('.', $key);
        $value = $translations;
    
        // Traverse nested translation keys like 'common.error.400'
        foreach ($keys as $k) {
            if (is_array($value) && array_key_exists($k, $value)) {
                $value = $value[$k];
            } else {
                // Key not found, return fallback or the key itself
                if (is_string($params) && $params !== '') {
                    return esc_html($params);  // Simple fallback string
                } elseif (is_array($params) && isset($params['fallback'])) {
                    return esc_html($params['fallback'] !== '' ? $params['fallback'] : $key);
                } else {
                    return esc_html($key);
                }
            }
        }

        // If the found value is not a string (e.g. array), return fallback or key
        if (!is_string($value)) {
            if (is_string($params) && $params !== '') {
                return esc_html($params);
            } elseif (is_array($params) && isset($params['fallback'])) {
                return esc_html($params['fallback'] !== '' ? $params['fallback'] : $key);
            } else {
                return esc_html($key);
            }
        }
    
        // Handle pluralization if the translation contains '|'
        if (strpos($value, '|') !== false) {
            // Determine count either from numeric param or from params['count']
            $count = is_numeric($params)
                ? $params
                : (is_array($params) && isset($params['count']) && is_numeric($params['count'])
                    ? $params['count']
                    : null);

            // Split into singular and plural parts
            list($singular, $plural) = array_map('trim', explode('|', $value, 2));
            // Choose plural or singular form based on count
            $value = ($count !== null && $count > 1)
                ? ($plural ?: $singular)
                : $singular;
        }

        // Replace placeholders like {placeholder} with values from $params if $params is array
        if (is_array($params)) {
            foreach ($params as $placeholder => $replacement) {
                $value = str_replace('{' . $placeholder . '}', $replacement, $value);
            }
        }

        // Escape and return the final translation string
        return esc_html($value);
    }

    /**
     * Validates the attributes passed to the shortcode.
     *
     * @param array $atts The attributes to validate.
     * @throws Exception If any attribute is missing or empty.
     */
    function Irent_Validate_Keys($atts)
    {
        // Initialize an empty array to collect error messages
        $missing_keys = [];

        // Iterate through attributes and check if they are empty
        array_walk($atts, function (&$value, $key) use (&$missing_keys) {
            $value = sanitize_text_field($value);
            if (empty($value)) {
                $missing_keys[] = $key; // Add missing keys to the list
            }
        });

        // If there are missing keys, throw an exception with a formatted message
        if (!empty($missing_keys)) {
            $error_message = sprintf(
                __irent('common.validators.missingFields', 'The following fields are missing:') . $missing_keys,
                implode(', ', $missing_keys)
            );
            return new WP_Error('invalid_keys', $error_message);
        }

        return true;
    }

    /**
     * Generates and returns error messages formatted with HTML and styling.
     *
     * @param Exception $e The exception object to extract the error message from.
     * @return string The formatted HTML containing the error message.
     */
    function Irent_Get_Error_Messages($error)
    {
        // Start output buffering
        ob_start();
        ?>
            <div style="
                background-color: #fff;
                border: 1px solid #c3c4c7;
                border-left: 4px solid #d63638;
                box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
                margin: 15px;
                padding: 1px 12px;
            ">
                <p>
                    <strong><?php echo strtoupper(__irent('common.error', '').':') ?></strong>
                    <br>
                    <?php echo esc_html($error); ?>
                </p>
            </div>
        <?php
        // Capture the output and store it in a variable
        return ob_get_clean();
    }

    /**
     * Generates and returns success messages formatted with HTML and styling.
     *
     * @param string $message the success message.
     * @return string The formatted HTML containing the success message.
     */
    function Irent_Get_Success_Messages($message)
    {
        // Start output buffering
        ob_start();
        ?>
            <div style="
                background-color: #fff;
                border: 1px solid #c3c4c7;
                border-left: 4px solid #00ec6a;
                box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
                margin: 15px;
                padding: 1px 12px;
            ">
                <p>
                    <?php echo esc_html($message); ?>
                </p>
            </div>
        <?php
        // Capture the output and store it in a variable
        return ob_get_clean();
    }

    /**
     * Check if a specific shortcode is present in posts, menus, or widgets.
     *
     * This function searches for a given shortcode within the content of published posts,
     * in navigation menus, and in sidebar widgets. It also checks if the shortcode
     * is present on the front page.
     *
     * @param string $shortcode The shortcode to check for.
     * @return bool True if the shortcode is found, false otherwise.
     */
    function Irent_Check_ShortCode($shortcode)
    {
        // Set up arguments for retrieving posts
        $args = [
            'posts_per_page' => -1, // Retrieve all posts
            'post_type'      => 'any', // Include all post types
        ];
        
        // Get all posts matching the arguments
        $posts = get_posts($args);

        // Loop through each post to check for the shortcode in the content
        foreach ($posts as $post) {
            if (has_shortcode($post->post_content, $shortcode)) {
                return true; // Shortcode found in post content
            }
        }

        // Get all registered navigation menu locations
        $locations = get_nav_menu_locations();
        // Loop through each menu location
        foreach ($locations as $location) {
            // Retrieve menu items for the current location
            $menu_items = wp_get_nav_menu_items($location);
            // Loop through each menu item to check for the shortcode
            foreach ($menu_items as $item) {
                // Check if the shortcode is in the item title or description
                if (strpos($item->title, $shortcode) !== false || strpos($item->description, $shortcode) !== false) {
                    return true; // Shortcode found in menu item
                }
            }
        }

        // Get all widgets in sidebars
        $sidebars_widgets = wp_get_sidebars_widgets();
        // Loop through each sidebar to check widgets
        foreach ($sidebars_widgets as $sidebar) {
            foreach ($sidebar as $widget_id) {
                // Get the settings for the current widget
                $widget_settings = get_option($widget_id);
                // Check if the widget has text and if it contains the shortcode
                if ($widget_settings && strpos($widget_settings['text'], $shortcode) !== false) {
                    return true; // Shortcode found in widget
                }
            }
        }

        // Check if the current page is the front page
        if (is_front_page()) {
            return true; // Shortcode considered present on the front page
        }

        return false; // Shortcode not found in posts, menus, widgets, or on the front page
    }

    /**
     * Check for missing or invalid entries in the booking system settings.
     *
     * This function validates the `$new_settings` array, checking for required fields such as `pageId`, `searchId`,
     * `language`, `theme`, and `code`. If any required fields are missing or invalid, an error message will be returned.
     *
     * @param array $new_settings The array of settings to be validated.
     *
     * @return string|WP_Error Returns a success message if validation passes, or a WP_Error object with error details if it fails.
     */
    function Irent_Check_Entries($new_settings)
    {
        // Define the error messages for each field.
        $errors = array(
            'searchId'  => __irent('common.validators.searchId', 'Please select a destination page for the quick search redirection.'),
            'language'  => __irent('common.validators.lang', 'No language selected.'),
            'theme'     => __irent('common.validators.colour',' No colour selected.'),
            'code'      => __irent('common.validators.accommodation', 'No accommodation selected.')
        );
        
        // Initialize the message string to collect error messages.
        $message = '';

        // Iterate through each field and check for missing or invalid entries.
        foreach ($errors as $field => $error) {
            $is_empty = empty($new_settings[$field]); // Check if the field is empty.
            $is_invalid_post = ($new_settings[$field] !== 0 && !get_post($new_settings[$field])); // Check if the post ID is invalid.
            
            // For 'searchId', check if the ID is invalid. For other fields, check if the value is empty.
            if ($field == 'searchId' ? $is_invalid_post : $is_empty) {
                $message .= $error . " "; // Append the error message to the result string.
            }
        }
        
        // If any errors were found, return them as a WP_Error object.
        if (!empty($message)) {
            return new WP_Error('Wrong Entries', $message);
        }

        // If no errors were found, return a success message.
        return __irent('common.savedOk', 'Changes saved successfully');
    }

    /**
     * Retrieves the language code for the current site.
     *
     * This function checks the WordPress site language and returns a two-character
     * language code if it matches one of the supported languages. If the site language
     * is not supported, it defaults to 'en' (English).
     *
     * @return string The two-character language code (e.g., 'en', 'fr', 'de').
     */
    function Irent_Get_HTML_Language() {
        // Check if the WordPress function 'get_bloginfo' exists
        if (!function_exists('get_bloginfo')) {
            return 'en'; // Default to English if the function is unavailable
        }

        // Retrieve the full language code from WordPress settings (e.g., 'en-US', 'fr-FR')
        $wp_language = get_bloginfo('language');

        // Extract the first two characters of the language code (e.g., 'en', 'fr')
        $wp_language_short = substr($wp_language, 0, 2);

        // Retrieve the list of supported languages defined in the plugin
        $supported_languages = is_array(IRENT_SUPPORTED_LANGUAGES) ? IRENT_SUPPORTED_LANGUAGES : [];

        // Check if the extracted language code is in the list of supported languages
        if (array_key_exists($wp_language_short, $supported_languages)) {
            return $wp_language_short; // Return the supported language code
        }

        // Default to 'en' (English) if the language is not supported
        return 'en';
    }

    // Define suported languages
    define('IRENT_SUPPORTED_LANGUAGES', array(
        'en'    => __irent('common.langs.en', 'English'),
        'es'    => __irent('common.langs.es', 'Spanish'),
        'nl'    => __irent('common.langs.nl', 'Dutch'),
        'fr'    => __irent('common.langs.fr', 'French'),
        'de'    => __irent('common.langs.de', 'German')
    ));

    // Define suported themes
    define('IRENT_COLOR_SCHEMES', array(
        'beige'     => __irent('common.colours.beige', 'Beige'),
        'blue'      => __irent('common.colours.blue', 'Blue'),
        'green'     => __irent('common.colours.green', 'Green'),
        'red'       => __irent('common.colours.red', 'Red'),
        'lblue'     => __irent('common.colours.lblue', 'Light blue'),
        'orange'    => __irent('common.colours.orange', 'Orange')
    ));

