<?php
/**
 * Author: Rymera Web Co
 *
 * @package SAAI\Helpers
 */

namespace SAAI\Helpers;

use SAAI\Classes\Memory\Data_Upload;
use SAAI\REST\Chat\Chat_Post_Types;

/**
 * Helper methods class.
 *
 * @since 1.0.0
 */
class Helper {

    /**
     * Excluded post types for chat.
     *
     * @since 1.1.2
     */
    const CHAT_EXCLUDED_POST_TYPES = array( 'attachment' );

    /**
     * Get plugin data.
     *
     * @param string|null $key       The plugin data key.
     * @param bool        $markup    If the returned data should have HTML markup applied. Default false.
     * @param bool        $translate If the returned data should be translated. Default false.
     *
     * @since 1.0.0
     * @return string[]|string
     */
    public static function get_plugin_data( $key = null, $markup = false, $translate = false ) {

        if ( ! function_exists( 'get_plugin_data' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $plugin_data = get_plugin_data( SAAI_PLUGIN_FILE, $markup, $translate );

        if ( null !== $key ) {
            return $plugin_data[ $key ] ?? '';
        }

        return $plugin_data;
    }

    /**
     * App frontend and backend common JS app localization properties.
     *
     * @param array $merge Additional data to merge.
     *
     * @since 1.0
     * @return array
     */
    public static function vite_app_common_l10n( $merge = array() ) {

        global $allowedposttags;

        $allowed_tags  = array_keys( $allowedposttags );
        $allowed_attrs = array_keys( array_merge( ...array_values( $allowedposttags ) ) );

        $allowed_tags  = apply_filters( 'wpay_kses_allowed_tags', $allowed_tags );
        $allowed_attrs = apply_filters( 'wpay_kses_allowed_attrs', $allowed_attrs );

        $defaults = array(
            'allowedTags'  => $allowed_tags,
            'allowedAttrs' => $allowed_attrs,
        );

        return wp_parse_args( $merge, $defaults );
    }

    /**
     * Loads admin template.
     *
     * @param string $name Template name relative to `templates/admin` directory.
     * @param bool   $load Whether to load the template or not.
     * @param bool   $once Whether to use require_once or require.
     *
     * @return string
     */
    public static function locate_admin_template( $name, $load = false, $once = true ) {

        //phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
        $template = SAAI_PLUGIN_DIR_PATH . 'templates/admin/' . $name;
        if ( ! file_exists( $template ) ) {
            return '';
        }

        if ( $load ) {
            if ( $once ) {
                require_once $template;
            } else {
                require $template;
            }
        }

        //phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
        return $template;
    }

    /**
     * Get the URL with UTM parameters.
     *
     * @param string $url_path     URL path from main.
     * @param string $utm_source   UTM source.
     * @param string $utm_medium   UTM medium.
     * @param string $utm_campaign UTM campaign.
     * @param string $site_url     URL - defaults to `https://storeagent.ai/`.
     *
     * @since 1.0.0
     * @return string
     */
    public static function get_utm_url( $url_path = '', $utm_source = 'saai', $utm_medium = 'action', $utm_campaign = 'default', $site_url = 'https://storeagent.ai/' ) {

        $utm_content = get_option( 'storeagentai_installed_by', false );
        $url         = trailingslashit( $site_url ) . $url_path;

        return add_query_arg(
            array(
                'utm_source'   => $utm_source,
                'utm_medium'   => $utm_medium,
                'utm_campaign' => $utm_campaign,
                'utm_content'  => $utm_content,
            ),
            trailingslashit( $url )
        );
    }

    /**
     * Get the authentication salt.
     *
     * @since 1.0.3
     * @return string
     */
    public static function get_auth_salt() {
        if ( defined( 'AUTH_SALT' ) ) {
            return AUTH_SALT;
        }

        // Fallback hash value if ever the AUTH_SALT constant is not defined.
        return wp_hash( 'storeagent-ai-for-woocommerce' );
    }

    /**
     * Create a pseudo nonce.
     *
     * @param string|int $action Action name.
     *
     * @since 1.0.0
     * @return string
     */
    public static function create_token( $action = -1 ) {

        $timeout = 12 * HOUR_IN_SECONDS;
        $time    = time();
        $token   = wp_hash( $time . $action . self::get_auth_salt(), 'nonce' );

        return "$token.$time.$timeout";
    }

    /**
     * Verify a pseudo nonce.
     *
     * @param string     $token  The token.
     * @param string|int $action Action name.
     *
     * @since 1.0.0
     * @return bool
     */
    public static function verify_token( $token, $action = -1 ) {

        [ $hash, $time, $timeout ] = explode( '.', $token );

        $time    = (int) $time;
        $timeout = (int) $timeout;
        if ( ( time() - $time ) > $timeout ) {
            return false;
        }

        return hash_equals( wp_hash( $time . $action . self::get_auth_salt(), 'nonce' ), $hash );
    }

    /**
     * Get the AI agent modules.
     *
     * @since 1.0.0
     * @return array
     */
    public static function get_ai_agents() {

        $transient_key = 'storeagent_ai_agents';
        $agents        = (array) get_transient( $transient_key );
        $agents        = array_filter( $agents );
        if ( empty( $agents ) || isset( $_REQUEST['force'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            $url      = self::get_storeagent_base_url( 'feeds' ) . '/agents.json';
            $response = ! self::is_production_environment()
                ? wp_remote_get( $url, array( 'sslverify' => false ) )
                : wp_safe_remote_get( $url );

            if ( is_wp_error( $response ) ) {
                wc_get_logger()->error( 'Error fetching modules: ' . $response->get_error_message(), array( 'source' => 'storeagent-ai-for-woocommerce' ) );

                return array();
            }

            $agents = (array) json_decode( wp_remote_retrieve_body( $response ), true );
            $agents = array_filter(
                $agents,
                function ( $agent ) {

                    return ! empty( $agent ) && in_array( 'woocommerce', $agent['platforms'], true );
                }
            );

            if ( ! empty( $agents ) ) {
                $agents = array_values( $agents );
                set_transient( $transient_key, $agents, DAY_IN_SECONDS + ( wp_rand( 0, 8 ) * HOUR_IN_SECONDS ) );
            }
        }

        return apply_filters( 'saai_helper_get_ai_agents', $agents );
    }

    /**
     * Get AI agent modules with settings.
     *
     * @since 1.0.0
     * @return array
     */
    public static function get_ai_agent_modules_with_settings() {

        $agents = self::get_ai_agents();

        // Map agent IDs to their class names for special cases (e.g., integration agents).
        $agent_class_map = array(
            'wc-vendors-product-moderator-ai' => 'SAAI\AI_Agents\Vendors\Vendors_Product_Moderator_AI',
            'coupon-generator-ai'             => 'SAAI\AI_Agents\Advanced_Coupons\Coupon_Generator_AI',
        );

        foreach ( $agents as $index => &$agent ) {
            // Check if there's a mapped class name for this agent ID.
            if ( isset( $agent_class_map[ $agent['id'] ] ) ) {
                $ai_agent_class = $agent_class_map[ $agent['id'] ];
            } else {
                // Default: derive class name from title.
                $class_name     = str_replace( ' ', '_', $agent['title'] );
                $ai_agent_class = 'SAAI\AI_Agents\\' . $class_name;
            }

            if ( ! class_exists( $ai_agent_class ) ) {
                unset( $agents[ $index ] );
                continue;
            }
            $agent['settings'] = self::get_ai_agent_settings( $agent['id'], null, $ai_agent_class::instance()->default_settings );
        }

        return array_values( $agents );
    }

    /**
     * Get AI agent settings.
     *
     * @param string $agent_id The agent ID or settings key.
     * @param null   $key      The settings property key.
     * @param array  $defaults The default settings.
     *
     * @return array|mixed
     */
    public static function get_ai_agent_settings( $agent_id, $key = null, $defaults = array() ) {

        $agent_id = ltrim( $agent_id, '_' );
        $settings = get_option( 'saai_' . str_replace( '-', '_', $agent_id ), array() );
        $settings = wp_parse_args( $settings, $defaults );

        if ( null !== $key ) {
            if ( false !== strpos( $key, '.' ) ) {
                $keys = explode( '.', $key );
                $keys = array_map( 'trim', array_filter( $keys ) );
                foreach ( $keys as $_key ) {
                    if ( ! isset( $settings[ $_key ] ) ) {
                        return null;
                    }
                    $settings = $settings[ $_key ];
                }
            } else {
                return $settings[ $key ] ?? '';
            }
        }

        return $settings;
    }

    /**
     * Check if the current environment is production.
     *
     * @return bool
     */
    public static function is_production_environment() {

        return 'production' === wp_get_environment_type();
    }

    /**
     * Get the StoreAgent Base URL.
     *
     * @param string $endpoint The endpoint to append to the base URL. If empty, the base URL is returned.
     * If 'api', the (potentially filtered) API endpoint is returned.
     *
     * @return mixed|null
     */
    public static function get_storeagent_base_url( $endpoint = '' ) {

        // If endpoint is api, return the (potentially filtered) API endpoint.
        if ( 'api' === $endpoint ) {
            $endpoint = self::get_storeagent_api_endpoint();
        }

        return apply_filters(
            'storeagent_ai_base_url',
            trailingslashit(
                ( defined( 'STOREAGENT_AI_BASE_URL' ) && STOREAGENT_AI_BASE_URL )
                    ? STOREAGENT_AI_BASE_URL
                    : 'https://app.storeagent.ai'
            )
        ) . $endpoint;
    }

    /**
     * Get the StoreAgent API endpoint.
     *
     * @return mixed|null
     */
    public static function get_storeagent_api_endpoint() {

        return apply_filters(
            'storeagent_ai_api_endpoint',
            ( ( defined( 'STOREAGENT_AI_API_ENDPOINT' ) && STOREAGENT_AI_API_ENDPOINT )
                ? STOREAGENT_AI_API_ENDPOINT
                : 'api' )
        );
    }

    /**
     * Maybe throttle a request.
     *
     * @param string $ip_address The IP address.
     *
     * @return bool
     */
    public static function maybe_throttle_request( $ip_address = '' ) {

        if ( empty( $ip_address ) ) {
            $ip_address = ! empty( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '127.0.0.1';
        }

        $last_request_time_key = 'saai_last_request_time_' . $ip_address;
        $requests_made_key     = 'saai_requests_made_' . $ip_address;

        // Get the current time.
        $current_time = time();

        // Get the last request time and the number of requests made.
        $last_request_time = get_transient( $last_request_time_key );

        $requests_made = get_transient( $requests_made_key );

        // If the number of requests made is not set, or current time is greater than the last request time set it to 0.
        if ( ( false === $requests_made ) || ( ( $current_time - $last_request_time ) > 60 ) ) {
            $requests_made = 0;
        }

        // Increment the number of requests made.
        ++$requests_made;

        // Update the last request time and the number of requests made.
        set_transient( $last_request_time_key, $current_time );
        set_transient( $requests_made_key, $requests_made );

        // If the number of requests made is greater than 25, return true.
        if ( $requests_made > 25 ) {
            return true;
        }

        return false;
    }

    /**
     * Get the data options.
     *
     * @since 1.0.3
     * @return array
     */
    public static function get_data_options() {
        $tones           = require_once SAAI_PLUGIN_DIR_PATH . 'data/options-tones.php';
        $age_focus       = require_once SAAI_PLUGIN_DIR_PATH . 'data/options-age-focus.php';
        $target_audience = require_once SAAI_PLUGIN_DIR_PATH . 'data/options-target-audience.php';
        $languages       = self::get_available_languages();

        return array(
            'tones'           => $tones,
            'age_focus'       => $age_focus,
            'target_audience' => $target_audience,
            'languages'       => $languages,
        );
    }

    /**
     * Get available languages from WordPress.
     *
     * @since 1.0.5
     * @return array Array of language codes and their display names.
     */
    public static function get_available_languages() {
        // Get current locale and its display name for default option.
        $current_locale        = get_locale();
        $default_language_name = self::get_language_display_name( $current_locale );

        // Start with default option showing actual language.
        $languages = array(
            'default' => sprintf(
                /* translators: %s: Default language name */
                __( 'Default language: %s', 'storeagent-ai-for-woocommerce' ),
                $default_language_name
            ),
        );

        if ( ! function_exists( 'wp_get_available_translations' ) ) {
            require_once ABSPATH . 'wp-admin/includes/translation-install.php';
        }

        // Get available translations from WordPress.
        $translations = wp_get_available_translations();

        // Add current site locale if not already included.
        $current_locale = get_locale();
        if ( 'en_US' !== $current_locale && ! isset( $translations[ $current_locale ] ) ) {
            $languages[ $current_locale ] = self::get_language_display_name( $current_locale );
        }

        // Add English (US) as it's always available but not in translations list.
        $languages['en_US'] = __( 'English (United States)', 'storeagent-ai-for-woocommerce' );

        // Add available translations.
        foreach ( $translations as $locale => $translation ) {
            if ( isset( $translation['native_name'] ) ) {
                $languages[ $locale ] = $translation['native_name'];
            } else {
                $languages[ $locale ] = self::get_language_display_name( $locale );
            }
        }

        // Sort languages alphabetically by display name, but keep "Default language" first.
        $default = array_slice( $languages, 0, 1, true );
        $rest    = array_slice( $languages, 1, null, true );
        asort( $rest );

        return $default + $rest;
    }

    /**
     * Get display name for a language locale.
     *
     * @since 1.0.5
     * @param string $locale The locale code.
     * @return string The display name.
     */
    private static function get_language_display_name( $locale ) {
        // Common language display names as fallback.
        $language_names = array(
            'en_US' => __( 'English (United States)', 'storeagent-ai-for-woocommerce' ),
            'en_GB' => __( 'English (United Kingdom)', 'storeagent-ai-for-woocommerce' ),
            'en_CA' => __( 'English (Canada)', 'storeagent-ai-for-woocommerce' ),
            'en_AU' => __( 'English (Australia)', 'storeagent-ai-for-woocommerce' ),
            'es_ES' => __( 'Spanish (Spain)', 'storeagent-ai-for-woocommerce' ),
            'fr_FR' => __( 'French (France)', 'storeagent-ai-for-woocommerce' ),
            'de_DE' => __( 'German (Germany)', 'storeagent-ai-for-woocommerce' ),
            'it_IT' => __( 'Italian (Italy)', 'storeagent-ai-for-woocommerce' ),
            'pt_BR' => __( 'Portuguese (Brazil)', 'storeagent-ai-for-woocommerce' ),
            'ru_RU' => __( 'Russian', 'storeagent-ai-for-woocommerce' ),
            'ja'    => __( 'Japanese', 'storeagent-ai-for-woocommerce' ),
            'zh_CN' => __( 'Chinese (Simplified)', 'storeagent-ai-for-woocommerce' ),
        );

        if ( isset( $language_names[ $locale ] ) ) {
            return $language_names[ $locale ];
        }

        // Fallback to locale code with some formatting.
        return str_replace( '_', ' - ', $locale );
    }

    /**
     * Get the default language setting.
     *
     * @since 1.0.5
     * @return string The default language code.
     */
    public static function get_default_language() {
        return get_locale();
    }

    /**
     * Resolve default language value.
     *
     * Replace 'default' language value with the actual WordPress default language.
     *
     * @since 1.0.6
     * @param string $language The language code to resolve.
     * @return string The resolved language code.
     */
    public static function resolve_default_language( $language = 'default' ) {
        if ( 'default' === $language || empty( $language ) ) {
            return self::get_default_language();
        }

        return $language;
    }

    /**
     * Recursively sanitize an array of values
     *
     * @param mixed $data The data to sanitize.
     * @return mixed The sanitized data.
     */
    public static function sanitize_recursive( $data ) {
        if ( ! is_array( $data ) ) {
            return sanitize_text_field( $data );
        }

        foreach ( $data as $key => $value ) {
            if ( is_array( $value ) ) {
                $data[ $key ] = self::sanitize_recursive( $value );
            } else {
                $data[ $key ] = sanitize_text_field( $value );
            }
        }

        return $data;
    }

    /**
     * Get public post types with labels and saved settings.
     *
     * @since 1.1.2
     * @return array Array of post types with labels and settings.
     */
    public static function get_chat_post_types_and_settings() {
        // Get all public post types.
        $post_types_objects = get_post_types(
            array(
                'public' => true,
            ),
            'objects'
        );

        // Exclude products and attachment post types.
        $excluded_post_types = self::CHAT_EXCLUDED_POST_TYPES;
        $filtered_post_types = array();

        foreach ( $post_types_objects as $post_type => $post_type_object ) {
            if ( ! in_array( $post_type, $excluded_post_types, true ) ) {
                $filtered_post_types[ $post_type ] = $post_type_object;
            }
        }

        // Get saved settings.
        $saved_settings = get_option( Chat_Post_Types::OPTION_KEY, array() );

        $result = array();

        foreach ( $filtered_post_types as $post_type => $post_type_object ) {
            // Get labels.
            $label          = $post_type_object->labels->name ?? $post_type;
            $singular_label = $post_type_object->labels->singular_name ?? $post_type;

            // Get saved settings for this post type or use defaults.
            $enabled   = isset( $saved_settings[ $post_type ]['enabled'] ) ? $saved_settings[ $post_type ]['enabled'] : 'no';
            $meta_keys = isset( $saved_settings[ $post_type ]['meta_keys'] ) ? $saved_settings[ $post_type ]['meta_keys'] : array();

            // Query for posts that are explicitly excluded (meta key = 'no').
            $excluded_posts         = array();
            $excluded_posts_details = array();
            if ( 'yes' === $enabled ) {
                $excluded_posts_query = new \WP_Query(
                    array(
                        'post_type'      => $post_type,
                        'posts_per_page' => -1,
                        'post_status'    => 'publish',
                        'meta_query'     => array(
                            array(
                                'key'     => Data_Upload::POST_AI_MEMORY_ENABLED_META,
                                'value'   => 'no',
                                'compare' => '=',
                            ),
                        ),
                        'fields'         => 'all',
                    )
                );

                if ( $excluded_posts_query->have_posts() ) {
                    while ( $excluded_posts_query->have_posts() ) {
                        $excluded_posts_query->the_post();
                        $post_id                  = get_the_ID();
                        $excluded_posts[]         = $post_id;
                        $excluded_posts_details[] = array(
                            'id'    => $post_id,
                            'title' => get_the_title(),
                        );
                    }
                    wp_reset_postdata();
                }
            }

            $include_wholesale_data = isset( $saved_settings[ $post_type ]['include_wholesale_data'] )
                ? $saved_settings[ $post_type ]['include_wholesale_data']
                : false;

            $result[ $post_type ] = array(
                'label'                  => $label,
                'singular_label'         => $singular_label,
                'enabled'                => $enabled,
                'meta_keys'              => $meta_keys,
                'include_wholesale_data' => $include_wholesale_data,
                'excluded_posts'         => $excluded_posts,
                'excluded_posts_details' => $excluded_posts_details,
                'rest_base'              => $post_type_object->rest_base ?? $post_type,
            );
        }

        return $result;
    }

    /**
     * Get post types with AI memory enabled.
     *
     * @since 1.1.2
     * @return array Array of post types with AI memory enabled.
     */
    public static function get_post_types_with_ai_memory_enabled() {
        $settings = get_option( Chat_Post_Types::OPTION_KEY, array() );

        return array_keys(
            array_filter(
                $settings,
                function ( $setting ) {
                    return 'yes' === $setting['enabled'];
                }
            )
        );
    }

    /**
     * Update post meta.
     *
     * @since 1.1.2
     * @param int    $post_id    The post ID.
     * @param string $meta_key   The meta key.
     * @param mixed  $meta_value The meta value.
     *
     * @return void
     */
    public static function update_post_meta( $post_id, $meta_key, $meta_value ) {
        if ( 'product' === get_post_type( $post_id ) ) {
            $product = wc_get_product( $post_id );
            if ( $product ) {
                $product->update_meta_data( $meta_key, $meta_value );
                $product->save_meta_data();
            }
        } else {
            update_post_meta( $post_id, $meta_key, $meta_value );
        }
    }

    /**
     * Get post meta.
     *
     * @param int    $post_id    The post ID.
     * @param string $meta_key   The meta key.
     * @param bool   $single     Whether to return a single value or an array.
     *
     * @return mixed The meta value.
     */
    public static function get_post_meta( $post_id, $meta_key, $single = true ) {
        if ( 'product' === get_post_type( $post_id ) ) {
            $product = wc_get_product( $post_id );
            if ( $product ) {
                return $product->get_meta( $meta_key, $single );
            }
        }
        return get_post_meta( $post_id, $meta_key, $single );
    }

    /**
     * Delete post meta.
     *
     * @param int    $post_id    The post ID.
     * @param string $meta_key   The meta key.
     *
     * @return void
     */
    public static function delete_post_meta( $post_id, $meta_key ) {
        if ( 'product' === get_post_type( $post_id ) ) {
            $product = wc_get_product( $post_id );
            if ( $product ) {
                $product->delete_meta_data( $meta_key );
                $product->save_meta_data();
            }
        } else {
            delete_post_meta( $post_id, $meta_key );
        }
    }
}
