<?php

if ( !defined( 'ABSPATH' ) ) {

    exit;
}

if ( !class_exists( 'Pricely_Cart_Discounts_Engine' ) ) {

    class Pricely_Cart_Discounts_Engine {

        private $data_key;
        private $valid_options;
        private $cache;
        private $cart;
        private $condition_types;
        private $amount_types;
        private $coupon_prefix;
        private $coupon_ids;
        private $locale;
        private $locale_prefix;

        public function __construct() {

            $this->data_key = 'cart_discounts';
            $this->locale_prefix = 'cart-discounts-';
            $this->valid_options = array();

            $this->coupon_prefix = 'pricely-d';
            $this->coupon_ids = array();

            $this->cache = Pricely_Cart_Discounts_Cache::get_instance();
            $this->cart = Pricely_Cart::get_instance();
            $this->condition_types = Pricely_Condition_Types::get_instance();
            $this->amount_types = Pricely_Amount_Types::get_instance();
            $this->locale = Pricely_Locale::get_instance();
        }

        public function clear_cache() {

            $this->valid_options = array();

            $this->cache->clear();

            if ( class_exists( 'Pricely_Pro_Cart_Discounts_Engine' ) ) {

                Pricely_Pro_Cart_Discounts_Engine::get_instance()->clear_cache( 'cart-discounts' );
            }
        }

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

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

                return false;
            }

            //check whether there are items
            if ( !count( $items ) ) {

                return false;
            }

            //process discounts
            return $this->calculate_discounts( $data, $context, $items );
        }

        public function get_cache( $context ) {

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

                return false;
            }

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

        public function store_cache( $discounts ) {

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

        private function calculate_discounts( $data, $context, $items ) {

            $settings = $this->get_settings();

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

                // return result
                return false;
            }

            // go through the discounts options and apply them
            foreach ( $this->get_valid_discount_options( $data, $items, $context ) as $discounts_option ) {

                $option_args = array(
                    'apply_mode' => $discounts_option[ 'apply_mode' ],
                    'discount_id' => $discounts_option[ 'discount_id' ],
                    'coupon_code' => $discounts_option[ 'coupon_code' ],
                    'coupon' => $discounts_option[ 'coupon' ],
                    'settings' => $settings,
                );

                if ( !$this->can_apply_option( $option_args, $data ) ) {

                    continue;
                }

                $discounts = $this->get_discounts( $data, $items, $discounts_option, $settings );

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

                    continue;
                }


                //set the discounts information
                $option_args[ 'title' ] = $this->get_translated_title( $discounts_option[ 'title' ], $discounts_option[ 'discount_id' ] );
                $option_args[ 'tooltip' ] = $this->get_translated_tooltip( $discounts_option[ 'tooltip' ], $discounts_option[ 'discount_id' ] );


                //allows other plugins to modify the calculated discounts
                if ( has_filter( 'pricely_cart-discounts_calculated-discounts' ) ) {

                    $discounts = apply_filters( 'pricely_cart-discounts_calculated-discounts', $discounts, $discounts_option[ 'discount_id' ], $data );
                }

                $data = $this->add_discount_option( $data, $discounts, $option_args );
            }

            // return result
            if ( isset( $data[ $this->data_key ] ) ) {

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

            return false;
        }

        private function get_discounts( $data, $items, $discounts_option, $settings ) {

            if ( class_exists( 'Pricely_Pro_Cart_Discounts_Engine' ) ) {

                return Pricely_Pro_Cart_Discounts_Engine::get_instance()->get_discounts( $data, $items, $discounts_option, $settings );
            }

            $amounts_args = $this->get_args_from_options( $discounts_option );

            return $this->amount_types->calculate_amounts( $data, $items, $amounts_args );
        }

        private function get_args_from_options( $discounts_option ) {

            $ex_keys = array(
                'apply_mode',
                'enable',
                'title',
                'admin_note',
                'tooltip',
                'coupon_code',
                'coupon',
                'limit',
                'condition_args',
            );

            $amounts_args = array(
                'context' => ''
            );

            foreach ( $discounts_option as $key => $discount_option_arg ) {

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

                    continue;
                }

                if ( 'discount_id' == $key ) {

                    $amounts_args[ 'id' ] = $discount_option_arg;

                    continue;
                }

                if ( 'discounts' == $key ) {

                    $amounts_args[ 'amounts' ] = $discount_option_arg;

                    continue;
                }

                $amounts_args[ $key ] = $discount_option_arg;
            }

            $amounts_args[ 'inclusive_tax' ] = false;

            if ( !isset( $amounts_args[ 'amounts' ] ) ) {

                $amounts_args[ 'amounts' ] = array();
            }

            $keys = array_keys( $amounts_args[ 'amounts' ] );

            foreach ( $keys as $key ) {
                $amounts_args[ 'amounts' ][ $key ][ 'tax_args' ] = false;
                $amounts_args[ 'amounts' ][ $key ][ 'module' ] = 'cart-discounts';
                $amounts_args[ 'amounts' ][ $key ][ 'sub_module' ] = 'amounts';
            }

            return $amounts_args;
        }

        private function can_apply_option( $option_args, $data ) {

            $bool_val = true;

            if ( class_exists( 'Pricely_Pro_Cart_Discounts_Engine' ) ) {

                $bool_val = Pricely_Pro_Cart_Discounts_Engine::get_instance()->can_apply_option( $option_args, $data );
            }

            // allows other plugins to check if the option should apply
            if ( has_filter( 'pricely_cart-discounts_can-apply-option' ) ) {

                $bool_val = apply_filters( 'pricely_cart-discounts_can-apply-option', $bool_val, $option_args, $data );
            }

            return $bool_val;
        }

        private function add_discount_option( $data, $amounts, $option_args ) {

            if ( class_exists( 'Pricely_Pro_Cart_Discounts_Engine' ) ) {

                $data = Pricely_Pro_Cart_Discounts_Engine::get_instance()->add_discount_option( $data, $amounts, $option_args );
            } else {

                $amounts[ 'coupon' ] = $this->get_coupon_data( $option_args );

                $data[ $this->data_key ][ $option_args[ 'discount_id' ] ] = $amounts;
            }

            // allows other plugins to modify discount data once added
            if ( has_filter( 'pricely_cart-discounts_added-discount-option' ) ) {

                if ( isset( $data[ $this->data_key ][ $option_args[ 'discount_id' ] ] ) ) {

                    $data[ $this->data_key ][ $option_args[ 'discount_id' ] ] = apply_filters( 'pricely_cart-discounts_added-discount-option', $data[ $this->data_key ][ $option_args[ 'discount_id' ] ], $option_args );
                }
            }

            return $data;
        }

        private function get_coupon_data( $option_args ) {

            if ( !isset( $option_args[ 'coupon' ] ) ) {

                return false;
            }

            $coupon_code = $this->prepare_coupon_code( $option_args[ 'coupon_code' ], $option_args[ 'discount_id' ] );

            $coupon_data = array(
                'id' => $this->prepare_coupon_id( $coupon_code, $option_args[ 'discount_id' ] ),
                'coupon_code' => $coupon_code,
                'label' => $this->get_coupon_label( $option_args[ 'title' ] ),
                'tooltip' => $option_args[ 'tooltip' ],
                'auto_add' => true,
                'individual_use' => false,
                'allow_removal' => false,
            );

            return $coupon_data;
        }

        private function prepare_coupon_id( $coupon_code, $discount_id ) {

            if ( isset( $this->coupon_ids[ $coupon_code ] ) ) {

                return $this->coupon_ids[ $coupon_code ];
            }

            $this->coupon_ids[ $coupon_code ] = $discount_id;

            return $this->coupon_ids[ $coupon_code ];
        }

        private function prepare_coupon_code( $coupon_code, $discount_id ) {

            if ( empty( $coupon_code ) ) {

                $coupon_code = $this->coupon_prefix . $discount_id;
            }

            return $coupon_code;
        }

        private function get_coupon_label( $title ) {

            if ( empty( $title ) ) {

                return esc_html__( 'Cart Discount', 'pricely-dynamic-pricing-cart-discounts' );
            }

            return $title;
        }

        private function get_valid_discount_options( $data, $items, $context ) {

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

                return $this->valid_options;
            }

            // get data to validate
            $data[ 'wc' ] = $this->cart->get_data( $context );

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

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

                    continue;
                }

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

                    $discount_option[ 'context' ] = $context;
                    $this->valid_options[] = $discount_option;

                    continue;
                }

                // get conditions to validate
                $option_conditions = $discount_option[ 'condition_args' ];
                $option_conditions[ 'id' ] = $discount_option[ 'discount_id' ];
                $option_conditions[ 'module' ] = 'cart-discounts';
                $option_conditions[ 'context' ] = $context;


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

                if ( !$bool_val ) {

                    continue;
                }


                $discount_option[ 'context' ] = $context;
                $this->valid_options[] = $discount_option;
            }

            return $this->valid_options;
        }

        private function get_translated_title( $title, $discount_id ) {

            if ( empty( $title ) ) {

                return $title;
            }

            return $this->get_translated_text( $title, $discount_id, 'title' );
        }

        private function get_translated_tooltip( $tooltip, $discount_id ) {

            if ( empty( $tooltip ) ) {

                return $tooltip;
            }

            return $this->get_translated_text( $tooltip, $discount_id, 'tooltip' );
        }

        private function get_translated_text( $text, $discount_id, $text_key ) {

            $translation_id = $this->locale_prefix . $discount_id . '-' . $text_key;

            return $this->locale->translate_string( $text, $translation_id );
        }

        private function get_discount_options() {

            $rules = array();

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

                $rules[] = $rule;
            }

            return $rules;
        }

        private function get_settings() {

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

            return $this->get_options( 'cart_discounts_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 ( 'cart_discounts' == $context ) {

                return true;
            }

            if ( false === $is_cache ) {

                return false;
            }

            $cache_context_list = array(
                'promotions',
                'cart_fees',
                'cart_messages',
            );

            return in_array( $context, $cache_context_list );
        }

    }

}