<?php
/**
 * MainWP Child Woocomerce Status
 *
 * MainWP WooCommerce Status Extension handler.
 *
 * @link https://mainwp.com/extension/woocommerce-status/
 *
 * @package MainWP\Child
 *
 * Credits
 *
 * Plugin-Name: WooCommerce
 * Plugin URI: https://woocommerce.com/
 * Author: Automattic
 * Author URI: https://woocommerce.com
 */

namespace MainWP\Child;

// phpcs:disable PSR1.Classes.ClassDeclaration, WordPress.WP.AlternativeFunctions -- Required to achieve desired results, pull request solutions appreciated.

/**
 * Class MainWP_Child_WooCommerce_Status
 *
 * MainWP WooCommerce Status Extension handler.
 */
class MainWP_Child_WooCommerce_Status {

    /**
     * Public static variable to hold the single instance of the class.
     *
     * @var mixed Default null
     */
    public static $instance = null;

    /**
     * Method instance()
     *
     * Create a public static instance.
     *
     * @return mixed Class instance.
     */
    public static function instance() {
        if ( null === static::$instance ) {
            static::$instance = new self();
        }

        return static::$instance;
    }

    /**
     * MainWP_Child_WooCommerce_Status constructor.
     *
     * Run any time class is called.
     */
    public function __construct() {
        add_action( 'mainwp_child_deactivation', array( $this, 'child_deactivation' ) );
    }

    /**
     * MainWP Child Plugin deactivation hooks.
     */
    public function child_deactivation() {
    }

    /**
     * MainWP Child Woocommerce actions: sync_data, report_data, update_wc_db.
     *
     * @uses \MainWP\Child\MainWP_Helper::write()
     */
    public function action() {
        $information = array();
        if ( ! class_exists( '\WooCommerce' ) || ! defined( 'WC_VERSION' ) ) {
            $information['error'] = 'NO_WOOCOMMERCE';
            MainWP_Helper::write( $information );
        }

        $is_ver220  = $this->is_version_220();
        $mwp_action = MainWP_System::instance()->validate_params( 'mwp_action' );
        if ( ! empty( $mwp_action ) ) {
            switch ( $mwp_action ) {
                case 'sync_data':
                    $information = ! $is_ver220 ? $this->sync_data() : $this->sync_data_two();
                    break;
                case 'report_data':
                    $information = ! $is_ver220 ? $this->report_data() : $this->report_data_two();
                    break;
                case 'update_wc_db':
                    $information = $this->update_wc_db();
                    break;
                default:
                    break;
            }
        }
        MainWP_Helper::write( $information );
    }

    /**
     * Compare woocommerce versions.
     *
     * By default, version_compare returns -1 if the first version is lower than the second,
     *  0 if they are equal, and 1 if the second is lower.
     *  When using the optional operator argument, the function will return true if the relationship is
     *  the one specified by the operator, false otherwise.
     *
     * @return bool|int Comparison response.
     */
    public function is_version_220() {
        return version_compare( WC()->version, '2.2.0', '>=' );
    }

    /**
     * Sync Woocommerce data.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function sync_data() {

        /**
         * Object, providing access to the WordPress database.
         *
         * @global object $wpdb WordPress Database instance.
         */
        global $wpdb;

        $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php';
        if ( file_exists( $file ) ) {
            include_once $file; // NOSONAR -- WP compatible.
        } else {
            return false;
        }

        // Define allowed WooCommerce order statuses.
        $allowed_statuses = array( 'completed', 'processing', 'on-hold', 'refunded', 'cancelled', 'failed', 'pending' );

        // Apply the filter and validate against whitelist.
        $filtered_statuses = apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) );
        $safe_statuses     = array_intersect( $filtered_statuses, $allowed_statuses );

        // Ensure at least one status exists, otherwise use default.
        if ( empty( $safe_statuses ) ) {
            $safe_statuses = array( 'completed' );
        }

        // Build dynamic placeholders for IN clause.
        $placeholders = implode( ',', array_fill( 0, count( $safe_statuses ), '%s' ) );

        // Prepare dates.
        $month_start = date( 'Y-m-01' ); // phpcs:ignore -- local time.
        $month_end   = date( 'Y-m-d H:i:s' ); // phpcs:ignore -- local time.

        // Generate cache key.
        $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); //phpcs:ignore --NOSONAR --safe for key.

        // Try to get cached value.
        $sales = wp_cache_get( $cache_key, 'mainwp_woocommerce' );

        // If cache miss, execute the query and cache result.
        if ( false === $sales ) {
            $sales = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->prepare(
                    "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts
                    LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
                    LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
                    LEFT JOIN {$wpdb->terms} AS term USING( term_id )
                    LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
                    WHERE posts.post_type = 'shop_order'
                    AND posts.post_status = 'publish'
                    AND tax.taxonomy = 'shop_order_status'
                    AND term.slug IN ( {$placeholders} )
                    AND postmeta.meta_key = '_order_total'
                    AND posts.post_date >= %s
                    AND posts.post_date <= %s",
                    array_merge( $safe_statuses, array( $month_start, $month_end ) )
                )
            );

            wp_cache_set( $cache_key, $sales, 'mainwp_woocommerce', HOUR_IN_SECONDS );
        }

        // Generate cache key for top seller.
        $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); //phpcs:ignore --NOSONAR --safe for key.

        // Try to get cached value.
        $top_seller = wp_cache_get( $cache_key_top, 'mainwp_woocommerce' );

        // If cache miss, execute the query and cache result.
        if ( false === $top_seller ) {
            $top_seller = $wpdb->get_row( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->prepare(
                    "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id
                    FROM {$wpdb->posts} as posts
                    LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
                    LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
                    LEFT JOIN {$wpdb->terms} AS term USING( term_id )
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
                    WHERE posts.post_type = 'shop_order'
                    AND posts.post_status = 'publish'
                    AND tax.taxonomy = 'shop_order_status'
                    AND term.slug IN ( {$placeholders} )
                    AND order_item_meta.meta_key = '_qty'
                    AND order_item_meta_2.meta_key = '_product_id'
                    AND posts.post_date >= %s
                    AND posts.post_date <= %s
                    GROUP BY product_id
                    ORDER BY qty DESC
                    LIMIT 1",
                    array_merge( $safe_statuses, array( $month_start, $month_end ) )
                )
            );

            wp_cache_set( $cache_key_top, $top_seller, 'mainwp_woocommerce', HOUR_IN_SECONDS );
        }

        if ( ! empty( $top_seller ) ) {
            $top_seller->name = get_the_title( $top_seller->product_id );
        }

        // Counts.
        $on_hold_count    = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count;
        $processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count;

        // Get products using a query.
        $stock   = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
        $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )";

        $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query.

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )";

        $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) );  //phpcs:ignore -- safe query.

        $data = array(
            'sales'          => $sales,
            'formated_sales' => wc_price( $sales ),
            'top_seller'     => $top_seller,
            'onhold'         => $on_hold_count,
            'awaiting'       => $processing_count,
            'stock'          => $stock,
            'nostock'        => $nostock,
            'lowstock'       => $lowinstock_count,
            'outstock'       => $outofstock_count,
        );

        $data = apply_filters( 'mainwp_child_woocom_sync_data', $data );

        $information['data'] = $data;

        return $information;
    }

    /**
     * Woocommerce report data.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function report_data() {

        /**
         * Object, providing access to the WordPress database.
         *
         * @global object $wpdb WordPress Database instance.
         */
        global $wpdb;

        $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php';
        if ( file_exists( $file ) ) {
            include_once $file; // NOSONAR -- WP compatible.
        } else {
            return false;
        }
        // phpcs:disable WordPress.Security.NonceVerification
        $start_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : '';
        $end_date   = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : '';

        $start_date = date( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore -- local time.
        $end_date   = date( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore -- local time.
        // phpcs:enable

        // Define allowed WooCommerce order statuses.
        $allowed_statuses = array( 'completed', 'processing', 'on-hold', 'refunded', 'cancelled', 'failed', 'pending' );

        // Apply the filter and validate against whitelist.
        $filtered_statuses = apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) );
        $safe_statuses     = array_intersect( $filtered_statuses, $allowed_statuses );

        // Ensure at least one status exists, otherwise use default.
        if ( empty( $safe_statuses ) ) {
            $safe_statuses = array( 'completed' );
        }

        // Build dynamic placeholders for IN clause.
        $placeholders = implode( ',', array_fill( 0, count( $safe_statuses ), '%s' ) );

        // Generate cache key.
        $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $start_date . '_' . $end_date ); //phpcs:ignore --NOSONAR --safe for key.

        // Try to get cached value.
        $sales = wp_cache_get( $cache_key, 'mainwp_woocommerce' );

        // If cache miss, execute the query and cache result.
        if ( false === $sales ) {
            $sales = $wpdb->get_var( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->prepare(
                    "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts
                    LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
                    LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
                    LEFT JOIN {$wpdb->terms} AS term USING( term_id )
                    LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
                    WHERE posts.post_type = 'shop_order'
                    AND posts.post_status = 'publish'
                    AND tax.taxonomy = 'shop_order_status'
                    AND term.slug IN ( {$placeholders} )
                    AND postmeta.meta_key = '_order_total'
                    AND posts.post_date >= %s
                    AND posts.post_date <= %s",
                    array_merge( $safe_statuses, array( $start_date, $end_date ) )
                )
            );

            wp_cache_set( $cache_key, $sales, 'mainwp_woocommerce', HOUR_IN_SECONDS );
        }

        // Generate cache key for top seller.
        $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $start_date . '_' . $end_date ); //phpcs:ignore --NOSONAR --safe for key.

        // Try to get cached value.
        $top_seller = wp_cache_get( $cache_key_top, 'mainwp_woocommerce' );

        // If cache miss, execute the query and cache result.
        if ( false === $top_seller ) {
            $top_seller = $wpdb->get_row( //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->prepare(
                    "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id
                    FROM {$wpdb->posts} as posts
                    LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
                    LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
                    LEFT JOIN {$wpdb->terms} AS term USING( term_id )
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
                    LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
                    WHERE posts.post_type = 'shop_order'
                    AND posts.post_status = 'publish'
                    AND tax.taxonomy = 'shop_order_status'
                    AND term.slug IN ( {$placeholders} )
                    AND order_item_meta.meta_key = '_qty'
                    AND order_item_meta_2.meta_key = '_product_id'
                    AND posts.post_date >= %s
                    AND posts.post_date <= %s
                    GROUP BY product_id
                    ORDER BY qty DESC
                    LIMIT 1",
                    array_merge( $safe_statuses, array( $start_date, $end_date ) )
                )
            );

            wp_cache_set( $cache_key_top, $top_seller, 'mainwp_woocommerce', HOUR_IN_SECONDS );
        }

        if ( ! empty( $top_seller ) ) {
            $top_seller->name = get_the_title( $top_seller->product_id );
        }

        // Counts.
        $on_hold_count    = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count;
        $processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count;

        // Get products using a query.
        $stock   = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
        $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )";

        $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query.

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )";

        $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query.

        $data = array(
            'sales'          => $sales,
            'formated_sales' => wc_price( $sales ),
            'top_seller'     => $top_seller,
            'onhold'         => $on_hold_count,
            'awaiting'       => $processing_count,
            'stock'          => $stock,
            'nostock'        => $nostock,
            'lowstock'       => $lowinstock_count,
            'outstock'       => $outofstock_count,
        );

        $data = apply_filters( 'mainwp_child_woocom_report_data', $data );

        $information['data'] = $data;

        return $information;
    }

    /**
     * Sync Woocommerce data for current month.
     */
    public function sync_data_two() {
        $start_date = date( 'Y-m-01 00:00:00', time() ); // phpcs:ignore -- local time.
        $end_date   = date( 'Y-m-d H:i:s', time() ); // phpcs:ignore -- local time.

        $start_date = strtotime( $start_date );
        $end_date   = strtotime( $end_date );

        return $this->get_woocom_data( $start_date, $end_date );
    }

    /**
     * Sync Woocomerce data for specific date range.
     */
    public function report_data_two() {
        // phpcs:disable WordPress.Security.NonceVerification
        $start_date = isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : '';
        $end_date   = isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : '';
        // phpcs:enable

        return $this->get_woocom_data( $start_date, $end_date );
    }

    /**
     * Check if woocomerce DB needs to be updated.
     *
     * @return bool true|false.
     */
    public function check_db_update() {
        if ( version_compare( get_option( 'woocommerce_db_version' ), WC_VERSION, '<' ) ) {
            return true;
        }
        return false;
    }

    /**
     * Get top seller.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_top_seller( $start_date, $end_date ) { //phpcs:ignore -- NOSONAR - ignore complex.

        $top_seller = false;

        $page       = 0;
        $total_page = 1;
        $top_count  = 0;

        while ( $page < $total_page ) {
            ++$page;
            $args = array(
                'before'   => $end_date,
                'after'    => $start_date,
                'page'     => $page,
                'per_page' => 1000,
            );

            $product_data = false;

            $compat_ver_after_93 = false;
            if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '9.3.0', '>=' ) ) {
                $compat_ver_after_93 = true;
            }

            if ( $compat_ver_after_93 ) {
                if ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Products\DataStore' ) ) {
                    $data_store   = new \Automattic\WooCommerce\Admin\API\Reports\Products\DataStore();
                    $product_data = $data_store->get_data( $args );
                }
            } elseif ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Products\Query' ) ) {
                    $report       = new \Automattic\WooCommerce\Admin\API\Reports\Products\Query( $args );
                    $product_data = $report->get_data();
            }

            $products = array();

            if ( is_object( $product_data ) ) {
                $products = ! empty( $product_data->data ) ? $product_data->data : array();
                if ( ! is_array( $products ) ) {
                    $products = array();
                }
                foreach ( $products as $prod_sel ) {
                    if ( is_array( $prod_sel ) && isset( $prod_sel['items_sold'] ) && $prod_sel['items_sold'] > $top_count ) {
                        $top_seller = $prod_sel;
                        $top_count  = $prod_sel['items_sold'];
                    }
                }
                if ( ! empty( $product_data->pages ) && $product_data->pages > $total_page ) {
                    $total_page = $product_data->pages;
                }
            } else {
                break;
            }
        }

        $top_data = array();
        if ( ! empty( $top_seller ) ) {
            $top_data         = array(
                'product_id' => $top_seller['product_id'],
                'qty'        => $top_seller['items_sold'],
            );
            $product          = wc_get_product( $top_seller['product_id'] );
            $top_data['name'] = ! empty( $product ) ? $product->get_name() : 'N/A';
        }
        return $top_data;
    }

    /**
     * Get Woocommerce 8 reports.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_woocom_reports( $start_date, $end_date ) {

        if ( class_exists( '\Automattic\WooCommerce\Admin\Features\Features' ) && \Automattic\WooCommerce\Admin\Features\Features::is_enabled( 'analytics' ) ) {
            return $this->get_woocom_analytics( $start_date, $end_date );
        }

        $on_hold_count = 0;
        if ( function_exists( 'wc_orders_count' ) ) {
            $status_counts = array_map( 'wc_orders_count', array( 'on-hold' ) );
            $on_hold_count = array_sum( $status_counts );
        }

        $processing_count = 0;
        if ( function_exists( 'wc_processing_order_count' ) ) {
            $processing_count = wc_processing_order_count();
        }

        $total_sales = $this->get_total_sales( $start_date, $end_date );

        $top_seller = $this->get_top_sellers_report( $start_date, $end_date );

        $report     = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query();
        $stock_data = $report->get_data();

        return array(
            'sales'          => $total_sales,
            'formated_sales' => wc_price( $total_sales ),
            'top_seller'     => ! empty( $top_seller ) ? (object) $top_seller : false,
            'onhold'         => $on_hold_count,
            'awaiting'       => $processing_count,
            'lowstock'       => is_array( $stock_data ) && isset( $stock_data['lowstock'] ) ? intval( $stock_data['lowstock'] ) : 0,
            'outstock'       => is_array( $stock_data ) && isset( $stock_data['outofstock'] ) ? intval( $stock_data['outofstock'] ) : 0,
        );
    }

    /**
     * Get Woocommerce data.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_woocom_data( $start_date, $end_date ) {

        if ( class_exists( '\Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\Query' ) ) {
            $data = $this->get_woocom_reports( $start_date, $end_date );
        } else {
            $data = $this->get_woocom_reports_old( $start_date, $end_date );
        }

        $information['data']           = $data;
        $information['need_db_update'] = $this->check_db_update();
        return $information;
    }


    /**
     * Get Woocommerce reports old.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_woocom_reports_old( $start_date, $end_date ) {

        /**
         * Object, providing access to the WordPress database.
         *
         * @global object $wpdb WordPress Database instance.
         */
        global $wpdb;

        $file = WP_PLUGIN_DIR . '/woocommerce/includes/admin/reports/class-wc-admin-report.php';
        if ( file_exists( $file ) ) {
            include_once $file; // NOSONAR -- WP compatible.
        } else {
            return false;
        }

        $start_date = date( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore -- local time. Required to achieve desired results, pull request solutions appreciated.
        $end_date   = date( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore -- local time. Required to achieve desired results, pull request solutions appreciated.

        // Sales.
        $query           = array();
        $query['fields'] = "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts";
        $query['join']   = "INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id ";
        $query['where']  = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'reports' ) ) . "' ) ";
        $query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) ";
        $query['where'] .= "AND postmeta.meta_key = '_order_total' ";
        $query['where'] .= 'AND posts.post_date >=  STR_TO_DATE(' . $wpdb->prepare( '%s', $start_date ) . ", '%Y-%m-%d %H:%i:%s' ) ";
        $query['where'] .= 'AND posts.post_date <=  STR_TO_DATE(' . $wpdb->prepare( '%s', $end_date ) . ", '%Y-%m-%d %H:%i:%s' ) ";

        $sales = $wpdb->get_var( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_sales_query', $query ) ) ); // phpcs:ignore -- safe query.

        // Get top seller.
        $query            = array();
        $query['fields']  = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$wpdb->posts} as posts";
        $query['join']    = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id ";
        $query['join']   .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id ";
        $query['join']   .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id ";
        $query['where']   = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) ";
        $query['where']  .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) ";
        $query['where']  .= "AND order_item_meta.meta_key = '_qty' ";
        $query['where']  .= "AND order_item_meta_2.meta_key = '_product_id' ";
        $query['where']  .= 'AND posts.post_date >= STR_TO_DATE(' . $wpdb->prepare( '%s', $start_date ) . ", '%Y-%m-%d %H:%i:%s') ";
        $query['where']  .= 'AND posts.post_date <= STR_TO_DATE(' . $wpdb->prepare( '%s', $end_date ) . ", '%Y-%m-%d %H:%i:%s')  ";
        $query['groupby'] = 'GROUP BY product_id';
        $query['orderby'] = 'ORDER BY qty DESC';
        $query['limits']  = 'LIMIT 1';

        $top_seller = $wpdb->get_row( implode( ' ', $query ) ); // phpcs:ignore -- safe query.

        if ( ! empty( $top_seller ) ) {
            $top_seller->name = get_the_title( $top_seller->product_id );
        }

        // Counts.
        $on_hold_count    = 0;
        $processing_count = 0;

        foreach ( wc_get_order_types( 'order-count' ) as $type ) {
            $counts            = (array) wp_count_posts( $type );
            $on_hold_count    += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0;
            $processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0;
        }

        // Get products using a query.
        $stock   = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
        $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) )";

        $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query.

        $query_from = "FROM {$wpdb->posts} as posts INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id WHERE 1=1 AND posts.post_type IN ('product', 'product_variation') AND posts.post_status = 'publish' AND ( postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' AND postmeta.meta_value != '' ) AND ( ( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' ) ) ";

        $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); //phpcs:ignore -- safe query.

        $data = array(
            'sales'          => $sales,
            'formated_sales' => wc_price( $sales ),
            'top_seller'     => $top_seller,
            'onhold'         => $on_hold_count,
            'awaiting'       => $processing_count,
            'stock'          => $stock,
            'nostock'        => $nostock,
            'lowstock'       => $lowinstock_count,
            'outstock'       => $outofstock_count,
        );

        $data = apply_filters( 'mainwp_child_woocom_get_data', $data );
        return $data;
    }

    /**
     * Update Woocommerce Database.
     *
     * @return string[] Success.
     */
    private static function update_wc_db() {
        include_once WC()->plugin_path() . '/includes/class-wc-background-updater.php'; // NOSONAR -- WP compatible.
        $background_updater = new \WC_Background_Updater();

        $current_db_version = get_option( 'woocommerce_db_version' );
        $logger             = wc_get_logger();
        $update_queued      = false;

        foreach ( \WC_Install::get_db_update_callbacks() as $version => $update_callbacks ) {
            if ( version_compare( $current_db_version, $version, '<' ) ) {
                foreach ( $update_callbacks as $update_callback ) {
                    $logger->info(
                        sprintf( 'Queuing %s - %s', $version, $update_callback ),
                        array( 'source' => 'wc_db_updates' )
                    );
                    $background_updater->push_to_queue( $update_callback );
                    $update_queued = true;
                }
            }
        }

        if ( $update_queued ) {
            $background_updater->save()->dispatch();
        }

        return array( 'result' => 'success' );
    }

    /**
     * Get top seller.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_top_sellers_report( $start_date, $end_date ) {

        include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php'; // NOSONAR -- WP compatible.

        $report = new \WC_Admin_Report();

        $_GET['start_date'] = gmdate( 'Y-m-d H:i:s', $start_date );
        $_GET['end_date']   = gmdate( 'Y-m-d H:i:s', $end_date );

        $report->calculate_current_range( 'custom' );

        $top_sellers = $report->get_order_report_data(
            array(
                'data'         => array(
                    '_product_id' => array(
                        'type'            => 'order_item_meta',
                        'order_item_type' => 'line_item',
                        'function'        => '',
                        'name'            => 'product_id',
                    ),
                    '_qty'        => array(
                        'type'            => 'order_item_meta',
                        'order_item_type' => 'line_item',
                        'function'        => 'SUM',
                        'name'            => 'order_item_qty',
                    ),
                ),
                'order_by'     => 'order_item_qty DESC',
                'group_by'     => 'product_id',
                'limit'        => 1000,
                'query_type'   => 'get_results',
                'filter_range' => true,
            )
        );

        $top_product = false;

        $top_count = 0;
        foreach ( $top_sellers as $top_seller ) {
            if ( is_object( $top_seller ) && isset( $top_seller->order_item_qty ) && $top_seller->order_item_qty > $top_count ) {
                $top_product = $top_seller;
                $top_count   = $top_seller->order_item_qty;
            }
        }

        $top_data = array();
        if ( ! empty( $top_product ) ) {
            $top_data         = array(
                'product_id' => $top_product->product_id,
                'qty'        => $top_product->order_item_qty,
            );
            $product          = wc_get_product( $top_product->product_id );
            $top_data['name'] = ! empty( $product ) ? $product->get_name() : 'N/A';
        }
        return $top_data;
    }

    /**
     * Get total sales.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return int $total_sales Total sales.
     */
    public function get_total_sales( $start_date, $end_date ) {

        include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php'; // NOSONAR -- WP compatible.
        include_once WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php'; // NOSONAR -- WP compatible.

        $total_sales = 0;

        $_GET['start_date'] = gmdate( 'Y-m-d H:i:s', $start_date );
        $_GET['end_date']   = gmdate( 'Y-m-d H:i:s', $end_date );

        $report = new \WC_Report_Sales_By_Date();
        $report->calculate_current_range( 'custom' );
        $report_data = $report->get_report_data();
        if ( is_object( $report_data ) && ! empty( $report_data->total_sales ) ) {
            $total_sales = $report_data->total_sales;
        }

        return $total_sales;
    }


    /**
     * Get Woocommerce 8 analytics.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array $information Woocommerce data grabed.
     */
    public function get_woocom_analytics( $start_date, $end_date ) {
        $on_hold_count = 0;
        if ( function_exists( 'wc_orders_count' ) ) {
            $status_counts = array_map( 'wc_orders_count', array( 'on-hold' ) );
            $on_hold_count = array_sum( $status_counts );
        }

        $processing_count = 0;
        if ( function_exists( 'wc_processing_order_count' ) ) {
            $processing_count = wc_processing_order_count();
        }

        $sales_data  = $this->get_sales_data( $start_date, $end_date );
        $total_sales = $sales_data['total_sales'];
        $top_seller  = $sales_data['top_seller'];

        $report     = new \Automattic\WooCommerce\Admin\API\Reports\Stock\Stats\Query();
        $stock_data = $report->get_data();

        return array(
            'sales'          => $total_sales,
            'formated_sales' => wc_price( $total_sales ),
            'top_seller'     => ! empty( $top_seller ) ? (object) $top_seller : false,
            'onhold'         => $on_hold_count,
            'awaiting'       => $processing_count,
            'lowstock'       => is_array( $stock_data ) && isset( $stock_data['lowstock'] ) ? intval( $stock_data['lowstock'] ) : 0,
            'outstock'       => is_array( $stock_data ) && isset( $stock_data['outofstock'] ) ? intval( $stock_data['outofstock'] ) : 0,
        );
    }


    /**
     * Get sales data.
     *
     * @param string $start_date Start Date.
     * @param string $end_date End Date.
     *
     * @return array Sales data.
     */
    public function get_sales_data( $start_date, $end_date ) {

        $start_date = gmdate( 'Y-m-d H:i:s', $start_date ); // phpcs:ignore
        $end_date   = gmdate( 'Y-m-d H:i:s', $end_date ); // phpcs:ignore

        $args          = array(
            'before'    => $end_date,
            'after'     => $start_date,
            'status_is' => array( 'on-hold', 'processing' ),
            'per_page'  => 1000,
        );
        $report        = new \Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\Query( $args );
        $order_data    = $report->get_data();
        $on_hold_count = is_object( $order_data ) && ! empty( $order_data->totals->orders_count ) ? $order_data->totals->orders_count : 0;

        $args         = array(
            'before'   => $end_date,
            'after'    => $start_date,
            'fields'   => array( 'total_sales' ),
            'per_page' => 1000,
        );
        $report       = new \Automattic\WooCommerce\Admin\API\Reports\Revenue\Query( $args );
        $revenue_data = $report->get_data();
        $total_sales  = is_object( $revenue_data ) && ! empty( $revenue_data->totals->total_sales ) ? $revenue_data->totals->total_sales : 0;

        $top_seller = $this->get_top_seller( $start_date, $end_date );

        return array(
            'top_seller'    => $top_seller,
            'on_hold_count' => $on_hold_count,
            'total_sales'   => $total_sales,
        );
    }
}
