<?php

if ( !defined( 'ABSPATH' ) ) {

    exit;
}

if ( !class_exists( 'Pricely_Product_Promotions_Engine' ) ) {

    class Pricely_Product_Promotions_Engine {

        private $data_key;
        private $valid_options;
        private $cache;
        private $cart;
        private $condition_types;
        private $product_filter_types;
        private $product_promotion_types;

        public function __construct() {

            $this->data_key = 'promotions';
            $this->valid_options = array();

            $this->cache = Pricely_Product_Promotions_Cache::get_instance();
            $this->cart = Pricely_Cart::get_instance();
            $this->condition_types = Pricely_Condition_Types::get_instance();
            $this->product_filter_types = Pricely_Product_Filter_Types::get_instance();
            $this->product_promotion_types = Pricely_Product_Promotion_Types::get_instance();
        }

        public function clear_cache() {

            $this->valid_options = array();

            $this->cache->clear();
        }

        public function get_data( $data, $cart_data, $context ) {

            // can't process data if not in the context
            if ( !$this->is_valid_context( $context, false ) ) {

                return false;
            }

            $cache_key = $this->get_cache_key( $data );

            //can't process empty product
            if ( empty( $cache_key ) ) {

                return false;
            }

            //process promotions
            $promotions = $this->process_promotions( $data, $cart_data );

            return $promotions;
        }

        public function get_cache( $data, $context ) {

            // can't process data if not in the context
            if ( !$this->is_valid_context( $context, true ) ) {

                return false;
            }

            $cache_key = $this->get_cache_key( $data );

            //can't process empty cache
            if ( empty( $cache_key ) ) {

                return false;
            }

            //get data from cache
            return $this->cache->get( $cache_key );
        }

        public function store_cache( $promotions, $data ) {

            $cache_key = $this->get_cache_key( $data );

            //can't store empty cache
            if ( empty( $cache_key ) ) {

                return;
            }

            //store promotions into cache for re-use
            $this->cache->set( $promotions, $cache_key );
        }

        private function process_promotions( $data, $cart_data ) {

            $settings = $this->get_settings();

            //check if prices should apply
            if ( 'no' == $settings[ 'mode' ] ) {

                // return result
                return false;
            }

            // go through the prices options and apply them
            foreach ( $this->get_valid_promotions_options( $cart_data ) as $promotion_option ) {

                $promotion_option_args = array(
                    'apply_mode' => $promotion_option[ 'apply_mode' ],
                    'setting_id' => $promotion_option[ 'setting_id' ],
                    'settings' => $settings,
                    'module' => 'product-promotions',
                );

                if ( !$this->can_apply_promotions_option( $promotion_option_args, $data ) ) {

                    continue;
                }

                $promotions = $this->get_promotions( $promotion_option, $data );

                //false promotions means null
                if ( !$promotions ) {

                    continue;
                }


                //allows other plugins to modify the processed promotions
                if ( has_filter( 'pricely_product-promotions_processed-promotions' ) ) {

                    $promotions = apply_filters( 'pricely_product-promotions_processed-promotions', $promotions, $promotion_option[ 'setting_id' ], $data );
                }

                //we are cool
                $data = $this->add_promotions_option( $data, $promotions, $promotion_option_args );
            }

            // it's all green now
            if ( isset( $data[ $this->data_key ] ) ) {

                return $data[ $this->data_key ];
            }

            // nothing, just nothing
            return false;
        }

        private function get_promotions( $promotion_option, $data ) {

            return $this->product_promotion_types->get_promotions( $this->get_args_from_options( $promotion_option ), $data );
        }

        private function get_args_from_options( $promotion_option ) {

            $ex_keys = array(
                'apply_mode',
                'enable',
                'admin_note',
                'condition_args',
            );

            $promotions_args = array();

            foreach ( $promotion_option as $key => $promotion_option_arg ) {

                if ( in_array( $key, $ex_keys ) ) {

                    continue;
                }

                $promotions_args[ $key ] = $promotion_option_arg;
            }


            if ( !isset( $promotions_args[ 'promotions' ] ) ) {

                $promotions_args[ 'promotions' ] = array();
            }

            $keys = array_keys( $promotions_args[ 'promotions' ] );

            foreach ( $keys as $key ) {

                $promotions_args[ 'promotions' ][ $key ][ 'module' ] = 'product-promotions';
            }

            return $promotions_args;
        }

        private function can_apply_promotions_option( $promotion_option_args, $data ) {

            $bool_val = true;

            if ( class_exists( 'Pricely_Pro_Product_Promotions_Engine' ) ) {

                $bool_val = Pricely_Pro_Product_Promotions_Engine::get_instance()->can_apply_promotions_option( $promotion_option_args, $data );
            }

            // allows other plugins to check if the option should apply
            if ( has_filter( 'pricely_product-promotions_can-apply-promotions-option' ) ) {

                $bool_val = apply_filters( 'pricely_product-promotions_can-apply-promotions-option', $bool_val, $promotion_option_args, $data );
            }

            return $bool_val;
        }

        private function add_promotions_option( $data, $promotions, $promotion_option_args ) {

            if ( class_exists( 'Pricely_Pro_Product_Promotions_Engine' ) ) {

                $data = Pricely_Pro_Product_Promotions_Engine::get_instance()->add_promotions_option( $data, $promotions, $promotion_option_args );
            } else {

                $data[ $this->data_key ][ $promotion_option_args[ 'setting_id' ] ] = $promotions;
            }

            // allows other plugins to modify promotions data once added
            if ( has_filter( 'pricely_product-promotions_added-promotions-option' ) ) {

                if ( isset( $data[ $this->data_key ][ $promotion_option_args[ 'setting_id' ] ] ) ) {

                    $data[ $this->data_key ][ $promotion_option_args[ 'setting_id' ] ] = apply_filters( 'pricely_product-promotions_added-promotions-option', $data[ $this->data_key ][ $promotion_option_args[ 'setting_id' ] ], $promotion_option_args );
                }
            }

            return $data;
        }

        private function get_valid_promotions_options( $data ) {

            // check for already validated options
            if ( count( $this->valid_options ) ) {

                return $this->valid_options;
            }

            // get data to validate

            $context = 'product_promotions';
            $data[ 'wc' ] = $this->cart->get_data( $context );

            // go through each options and validate them
            foreach ( $this->get_promotions_options() as $promotion_options ) {

                // allows active rules only
                if ( 'no' == $promotion_options[ 'enable' ] ) {

                    continue;
                }

                // check for required validations
                if ( !isset( $promotion_options[ 'condition_args' ][ 'conditions' ] ) ) {

                    $this->valid_options[] = $promotion_options;

                    continue;
                }

                // get conditions to validate
                $option_conditions = $promotion_options[ 'condition_args' ];
                $option_conditions[ 'id' ] = $promotion_options[ 'setting_id' ];
                $option_conditions[ 'module' ] = 'product-promotions';
                $option_conditions[ 'context' ] = $context;

                // validate option conditions
                $bool_val = $this->condition_types->validate( $option_conditions, $data );

                if ( !$bool_val ) {

                    continue;
                }

                $this->valid_options[] = $promotion_options;
            }

            return $this->valid_options;
        }

        private function get_cache_key( $data ) {

            if ( isset( $data[ 'wc' ][ 'product' ][ 'key' ] ) ) {

                return $data[ 'wc' ][ 'product' ][ 'key' ];
            }

            return '';
        }

        private function get_promotions_options() {

            $rules = array();

            foreach ( $this->get_options( 'product_promotions', array() ) as $rule ) {

                $rules[] = $rule;
            }

            return $rules;
        }

        private function get_settings() {

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

            return $this->get_options( 'product_promotions_settings', $default );
        }

        private function get_options( $option_key, $default ) {

            $pricely = Pricely::get_instance();

            return $pricely->get_options( $option_key, $default );
        }

        private function is_valid_context( $context, $is_cache ) {

            if ( 'promotions' == $context ) {

                return true;
            }

            if ( false === $is_cache ) {

                return false;
            }

            return true;
        }

    }

}