<?php

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

if ( !class_exists( 'Pricely_Pipeline_On_Sale_Query' ) ) {

    class Pricely_Pipeline_On_Sale_Query {

        private $transient_id = 'on_sale';
        private $cached_data;

        public function __construct() {

            $this->cached_data = array();

            add_filter( 'transient_wc_products_onsale', array( $this, 'get_on_sale_product_ids' ), 99999, 2 );
        }

        public function get_on_sale_product_ids( $on_sale_product_ids, $transient_name ) {

            if ( 'wc_products_onsale' !== $transient_name ) {

                return $on_sale_product_ids;
            }

            $product_ids = $this->get_product_ids();

            if ( !$product_ids ) {

                return $on_sale_product_ids;
            }

            return $product_ids;
        }

        private function get_product_ids() {

            $settings = $this->get_settings();

            if ( 'no' == $settings[ 'enable' ] ) {

                return false;
            }

            $cache_key = $this->get_cache_key();

            if ( isset( $this->cached_data[ $cache_key ] ) ) {

                return $this->cached_data[ $cache_key ];
            }

            $cache_data = $this->load_cache( $cache_key );

            if ( false !== $cache_data ) {

                $this->cached_data[ $cache_key ] = $cache_data;

                return $this->cached_data[ $cache_key ];
            }

            $data = $this->load_from_db( $settings[ 'query_limit' ] );

            if ( false !== $data ) {

                $this->store_cache( $data, $cache_key, $settings[ 'cache_duration' ] );

                $this->cached_data[ $cache_key ] = $data;

                return $this->cached_data[ $cache_key ];
            }

            return false;
        }

        private function load_from_db( $query_limit ) {

            if ( !is_numeric( $query_limit ) ) {

                $query_limit = 50;
            }

            $query_product_ids = $this->query_db( $query_limit );

            if ( !is_array( $query_product_ids ) ) {

                return false;
            }

            $product_ids = array();

            foreach ( $query_product_ids as $product_id ) {

                $product = Pricely_Product::get_instance()->get_product( $product_id );

                if ( !$product ) {

                    continue;
                }

                if ( $product->is_on_sale() ) {

                    $product_ids[] = $product_id;
                }
            }

            if ( !count( $product_ids ) ) {

                return false;
            }

            return $product_ids;
        }

        private function query_db( $query_limit ) {

            global $wpdb;

            $limit = esc_sql( $query_limit );
            $exclude_term_ids = array();
            $outofstock_join = '';
            $outofstock_where = '';
            $non_published_where = '';
            $product_visibility_term_ids = wc_get_product_visibility_term_ids();

            if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids[ 'outofstock' ] ) {
                $exclude_term_ids[] = $product_visibility_term_ids[ 'outofstock' ];
            }

            if ( count( $exclude_term_ids ) ) {
                $outofstock_join = " LEFT JOIN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( " . implode( ',', array_map( 'absint', $exclude_term_ids ) ) . ' ) ) AS exclude_join ON exclude_join.object_id = id';
                $outofstock_where = ' AND exclude_join.object_id IS NULL';
            }

            // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter
            $on_sale_products = $wpdb->get_results(
                    "
			SELECT posts.ID as id, posts.post_parent as parent_id
			FROM {$wpdb->posts} AS posts
			INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
			$outofstock_join
			WHERE posts.post_type IN ( 'product', 'product_variation' )
			AND posts.post_status = 'publish'
			$outofstock_where
			AND posts.post_parent NOT IN (
				SELECT ID FROM `$wpdb->posts` as posts
				WHERE posts.post_type = 'product'
				AND posts.post_parent = 0
				AND posts.post_status != 'publish'
			)
			GROUP BY posts.ID
                        LIMIT {$limit}
			"
            );
            // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter

            return wp_parse_id_list( array_merge( wp_list_pluck( $on_sale_products, 'id' ), array_diff( wp_list_pluck( $on_sale_products, 'parent_id' ), array( 0 ) ) ) );
        }

        private function get_cache_key() {

            return md5( 'pricely' );
        }

        private function load_cache( $key ) {

            if ( $this->can_store_in_session() ) {

                $session_data = $this->get_session( 'pricely_' . $this->transient_id, false );

                if ( !$session_data ) {

                    return false;
                }

                if ( $key != $session_data[ 'key' ] ) {

                    return false;
                }

                if ( current_time( 'U' ) > $session_data[ 'expiration' ] ) {

                    return false;
                }

                return $session_data[ 'cache_data' ];
            }

            $transient_key = $this->get_transient_key( $key );

            return get_transient( 'pricely_' . $transient_key );
        }

        private function store_cache( $cache_data, $key, $duration ) {

            if ( $this->can_store_in_session() ) {

                $session_data = array(
                    'cache_data' => $cache_data,
                    'key' => $key,
                    'expiration' => $this->get_expiration( $duration )
                );

                $this->set_session( 'pricely_' . $this->transient_id, $session_data );
            } else {

                $transient_key = $this->get_transient_key( $key );

                set_transient( 'pricely_' . $transient_key, $cache_data, $this->get_duration_in_seconds( $duration ) );
            }
        }

        private function can_store_in_session() {

            $has_session = $this->get_session( 'customer', false );

            if ( !$has_session ) {

                return false;
            }

            return true;
        }

        private function get_expiration( $duration ) {

            return current_time( 'U' ) + $this->get_duration_in_seconds( $duration );
        }

        private function get_duration_in_seconds( $duration ) {

            if ( !is_numeric( $duration ) ) {

                return 30 * MINUTE_IN_SECONDS;
            }

            if ( $duration <= 0 ) {

                return MINUTE_IN_SECONDS + 5;
            }

            return $duration * MINUTE_IN_SECONDS;
        }

        private function get_transient_key( $key ) {

            if ( !empty( $key ) ) {

                return $this->transient_id . '_' . $key;
            }

            return $this->transient_id;
        }

        private function get_session( $key, $default ) {

            if ( is_null( WC()->session ) ) {

                return $default;
            }

            return WC()->session->get( $key, $default );
        }

        private function set_session( $key, $value ) {

            if ( is_null( WC()->session ) ) {

                return;
            }

            WC()->session->set( $key, $value );
        }

        private function get_settings() {

            $default = array(
                'enable' => 'no',
            );

            return Pricely::get_instance()->get_options( 'on_sale_product_query', $default );
        }

    }

    new Pricely_Pipeline_On_Sale_Query();
}