<?php

use Automattic\WooCommerce\Utilities\NumberUtil;
defined('ABSPATH') || exit;
require_once dirname(__FILE__) . '/legacy/class-wc-legacy-coupon.php';
class WC_Coupon extends WC_Legacy_Coupon
{
    protected $data = array('code' => '', 'amount' => 0, 'date_created' => null, 'date_modified' => null, 'date_expires' => null, 'discount_type' => 'fixed_cart', 'description' => '', 'usage_count' => 0, 'individual_use' => false, 'product_ids' => array(), 'excluded_product_ids' => array(), 'usage_limit' => 0, 'usage_limit_per_user' => 0, 'limit_usage_to_x_items' => null, 'free_shipping' => false, 'product_categories' => array(), 'excluded_product_categories' => array(), 'exclude_sale_items' => false, 'minimum_amount' => '', 'maximum_amount' => '', 'email_restrictions' => array(), 'used_by' => array(), 'virtual' => false);
    const E_WC_COUPON_INVALID_FILTERED = 100;
    const E_WC_COUPON_INVALID_REMOVED = 101;
    const E_WC_COUPON_NOT_YOURS_REMOVED = 102;
    const E_WC_COUPON_ALREADY_APPLIED = 103;
    const E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY = 104;
    const E_WC_COUPON_NOT_EXIST = 105;
    const E_WC_COUPON_USAGE_LIMIT_REACHED = 106;
    const E_WC_COUPON_EXPIRED = 107;
    const E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET = 108;
    const E_WC_COUPON_NOT_APPLICABLE = 109;
    const E_WC_COUPON_NOT_VALID_SALE_ITEMS = 110;
    const E_WC_COUPON_PLEASE_ENTER = 111;
    const E_WC_COUPON_MAX_SPEND_LIMIT_MET = 112;
    const E_WC_COUPON_EXCLUDED_PRODUCTS = 113;
    const E_WC_COUPON_EXCLUDED_CATEGORIES = 114;
    const E_WC_COUPON_USAGE_LIMIT_COUPON_STUCK = 115;
    const E_WC_COUPON_USAGE_LIMIT_COUPON_STUCK_GUEST = 116;
    const WC_COUPON_SUCCESS = 200;
    const WC_COUPON_REMOVED = 201;
    protected $cache_group = 'coupons';
    public function __construct($data = '')
    {
        parent::__construct($data);
        if ($data instanceof WC_Coupon) {
            $this->set_id(absint($data->get_id()));
            $this->read_object_from_database();
            return;
        }
        $coupon = apply_filters('woocommerce_get_shop_coupon_data', false, $data, $this);
        if ($coupon) {
            $this->read_manual_coupon($data, $coupon);
            return;
        }
        if (is_int($data) && 'shop_coupon' === get_post_type($data)) {
            $this->set_id($data);
        } elseif (!empty($data)) {
            $id = wc_get_coupon_id_by_code($data);
            if (!$id && 'shop_coupon' === get_post_type($data)) {
                $this->set_id($data);
            } else {
                $this->set_id($id);
                $this->set_code($data);
            }
        } else {
            $this->set_object_read(true);
        }
        $this->read_object_from_database();
    }
    protected function read_object_from_database()
    {
        $this->data_store = WC_Data_Store::load('coupon');
        if ($this->get_id() > 0) {
            $this->data_store->read($this);
        }
    }
    public function is_type($type)
    {
        return $this->get_discount_type() === $type || is_array($type) && in_array($this->get_discount_type(), $type, true);
    }
    protected function get_hook_prefix()
    {
        return 'woocommerce_coupon_get_';
    }
    public function get_code($context = 'view')
    {
        return $this->get_prop('code', $context);
    }
    public function get_description($context = 'view')
    {
        return $this->get_prop('description', $context);
    }
    public function get_discount_type($context = 'view')
    {
        return $this->get_prop('discount_type', $context);
    }
    public function get_amount($context = 'view')
    {
        return wc_format_decimal($this->get_prop('amount', $context));
    }
    public function get_date_expires($context = 'view')
    {
        return $this->get_prop('date_expires', $context);
    }
    public function get_date_created($context = 'view')
    {
        return $this->get_prop('date_created', $context);
    }
    public function get_date_modified($context = 'view')
    {
        return $this->get_prop('date_modified', $context);
    }
    public function get_usage_count($context = 'view')
    {
        return $this->get_prop('usage_count', $context);
    }
    public function get_individual_use($context = 'view')
    {
        return $this->get_prop('individual_use', $context);
    }
    public function get_product_ids($context = 'view')
    {
        return $this->get_prop('product_ids', $context);
    }
    public function get_excluded_product_ids($context = 'view')
    {
        return $this->get_prop('excluded_product_ids', $context);
    }
    public function get_usage_limit($context = 'view')
    {
        return $this->get_prop('usage_limit', $context);
    }
    public function get_usage_limit_per_user($context = 'view')
    {
        return $this->get_prop('usage_limit_per_user', $context);
    }
    public function get_limit_usage_to_x_items($context = 'view')
    {
        return $this->get_prop('limit_usage_to_x_items', $context);
    }
    public function get_free_shipping($context = 'view')
    {
        return $this->get_prop('free_shipping', $context);
    }
    public function get_product_categories($context = 'view')
    {
        return $this->get_prop('product_categories', $context);
    }
    public function get_excluded_product_categories($context = 'view')
    {
        return $this->get_prop('excluded_product_categories', $context);
    }
    public function get_exclude_sale_items($context = 'view')
    {
        return $this->get_prop('exclude_sale_items', $context);
    }
    public function get_minimum_amount($context = 'view')
    {
        return $this->get_prop('minimum_amount', $context);
    }
    public function get_maximum_amount($context = 'view')
    {
        return $this->get_prop('maximum_amount', $context);
    }
    public function get_email_restrictions($context = 'view')
    {
        return $this->get_prop('email_restrictions', $context);
    }
    public function get_used_by($context = 'view')
    {
        return $this->get_prop('used_by', $context);
    }
    public function get_virtual($context = 'view')
    {
        return (bool) $this->get_prop('virtual', $context);
    }
    public function get_discount_amount($discounting_amount, $cart_item = null, $single = false)
    {
        $discount = 0;
        $cart_item_qty = is_null($cart_item) ? 1 : $cart_item['quantity'];
        if ($this->is_type(array('percent'))) {
            $discount = (float) $this->get_amount() * ($discounting_amount / 100);
        } elseif ($this->is_type('fixed_cart') && !is_null($cart_item) && WC()->cart->subtotal_ex_tax) {
            if (wc_prices_include_tax()) {
                $discount_percent = wc_get_price_including_tax($cart_item['data']) * $cart_item_qty / WC()->cart->subtotal;
            } else {
                $discount_percent = wc_get_price_excluding_tax($cart_item['data']) * $cart_item_qty / WC()->cart->subtotal_ex_tax;
            }
            $discount = (float) $this->get_amount() * $discount_percent / $cart_item_qty;
        } elseif ($this->is_type('fixed_product')) {
            $discount = min($this->get_amount(), $discounting_amount);
            $discount = $single ? $discount : $discount * $cart_item_qty;
        }
        return apply_filters('woocommerce_coupon_get_discount_amount', NumberUtil::round(min($discount, $discounting_amount), wc_get_rounding_precision()), $discounting_amount, $cart_item, $single, $this);
    }
    public function set_code($code)
    {
        $this->set_prop('code', wc_format_coupon_code($code));
    }
    public function set_description($description)
    {
        $this->set_prop('description', $description);
    }
    public function set_discount_type($discount_type)
    {
        if ('percent_product' === $discount_type) {
            $discount_type = 'percent';
        }
        if (!in_array($discount_type, array_keys(wc_get_coupon_types()), true)) {
            $this->error('coupon_invalid_discount_type', __('Invalid discount type', 'woocommerce'));
        }
        $this->set_prop('discount_type', $discount_type);
    }
    public function set_amount($amount)
    {
        $amount = wc_format_decimal($amount);
        if (!is_numeric($amount)) {
            $amount = 0;
        }
        if ($amount < 0) {
            $this->error('coupon_invalid_amount', __('Invalid discount amount', 'woocommerce'));
        }
        if ('percent' === $this->get_discount_type() && $amount > 100) {
            $this->error('coupon_invalid_amount', __('Invalid discount amount', 'woocommerce'));
        }
        $this->set_prop('amount', $amount);
    }
    public function set_date_expires($date)
    {
        $this->set_date_prop('date_expires', $date);
    }
    public function set_date_created($date)
    {
        $this->set_date_prop('date_created', $date);
    }
    public function set_date_modified($date)
    {
        $this->set_date_prop('date_modified', $date);
    }
    public function set_usage_count($usage_count)
    {
        $this->set_prop('usage_count', absint($usage_count));
    }
    public function set_individual_use($is_individual_use)
    {
        $this->set_prop('individual_use', (bool) $is_individual_use);
    }
    public function set_product_ids($product_ids)
    {
        $this->set_prop('product_ids', array_filter(wp_parse_id_list((array) $product_ids)));
    }
    public function set_excluded_product_ids($excluded_product_ids)
    {
        $this->set_prop('excluded_product_ids', array_filter(wp_parse_id_list((array) $excluded_product_ids)));
    }
    public function set_usage_limit($usage_limit)
    {
        $this->set_prop('usage_limit', absint($usage_limit));
    }
    public function set_usage_limit_per_user($usage_limit)
    {
        $this->set_prop('usage_limit_per_user', absint($usage_limit));
    }
    public function set_limit_usage_to_x_items($limit_usage_to_x_items)
    {
        $this->set_prop('limit_usage_to_x_items', is_null($limit_usage_to_x_items) ? null : absint($limit_usage_to_x_items));
    }
    public function set_free_shipping($free_shipping)
    {
        $this->set_prop('free_shipping', (bool) $free_shipping);
    }
    public function set_product_categories($product_categories)
    {
        $this->set_prop('product_categories', array_filter(wp_parse_id_list((array) $product_categories)));
    }
    public function set_excluded_product_categories($excluded_product_categories)
    {
        $this->set_prop('excluded_product_categories', array_filter(wp_parse_id_list((array) $excluded_product_categories)));
    }
    public function set_exclude_sale_items($exclude_sale_items)
    {
        $this->set_prop('exclude_sale_items', (bool) $exclude_sale_items);
    }
    public function set_minimum_amount($amount)
    {
        $this->set_prop('minimum_amount', wc_format_decimal($amount));
    }
    public function set_maximum_amount($amount)
    {
        $this->set_prop('maximum_amount', wc_format_decimal($amount));
    }
    public function set_email_restrictions($emails = array())
    {
        $emails = array_filter(array_map('sanitize_email', array_map('strtolower', (array) $emails)));
        foreach ($emails as $email) {
            if (!is_email($email)) {
                $this->error('coupon_invalid_email_address', __('Invalid email address restriction', 'woocommerce'));
            }
        }
        $this->set_prop('email_restrictions', $emails);
    }
    public function set_used_by($used_by)
    {
        $this->set_prop('used_by', array_filter($used_by));
    }
    public function set_virtual($virtual)
    {
        $this->set_prop('virtual', (bool) $virtual);
    }
    public function read_manual_coupon($code, $coupon)
    {
        foreach ($coupon as $key => $value) {
            switch ($key) {
                case 'excluded_product_ids':
                case 'exclude_product_ids':
                    if (!is_array($coupon[$key])) {
                        wc_doing_it_wrong($key, $key . ' should be an array instead of a string.', '3.0');
                        $coupon['excluded_product_ids'] = wc_string_to_array($value);
                    }
                    break;
                case 'exclude_product_categories':
                case 'excluded_product_categories':
                    if (!is_array($coupon[$key])) {
                        wc_doing_it_wrong($key, $key . ' should be an array instead of a string.', '3.0');
                        $coupon['excluded_product_categories'] = wc_string_to_array($value);
                    }
                    break;
                case 'product_ids':
                    if (!is_array($coupon[$key])) {
                        wc_doing_it_wrong($key, $key . ' should be an array instead of a string.', '3.0');
                        $coupon[$key] = wc_string_to_array($value);
                    }
                    break;
                case 'individual_use':
                case 'free_shipping':
                case 'exclude_sale_items':
                    if (!is_bool($coupon[$key])) {
                        wc_doing_it_wrong($key, $key . ' should be true or false instead of yes or no.', '3.0');
                        $coupon[$key] = wc_string_to_bool($value);
                    }
                    break;
                case 'expiry_date':
                    $coupon['date_expires'] = $value;
                    break;
            }
        }
        $this->set_props($coupon);
        $this->set_code($code);
        $this->set_id(0);
        $this->set_virtual(true);
    }
    public function increase_usage_count($used_by = '', $order = null)
    {
        if ($this->get_id() && $this->data_store) {
            $new_count = $this->data_store->increase_usage_count($this, $used_by, $order);
            $this->data['usage_count'] = $new_count;
            if (isset($this->changes['usage_count'])) {
                unset($this->changes['usage_count']);
            }
        }
    }
    public function decrease_usage_count($used_by = '')
    {
        if ($this->get_id() && $this->get_usage_count() > 0 && $this->data_store) {
            $new_count = $this->data_store->decrease_usage_count($this, $used_by);
            $this->data['usage_count'] = $new_count;
            if (isset($this->changes['usage_count'])) {
                unset($this->changes['usage_count']);
            }
        }
    }
    public function get_error_message()
    {
        return $this->error_message;
    }
    public function is_valid()
    {
        $discounts = new WC_Discounts(WC()->cart);
        $valid = $discounts->is_coupon_valid($this);
        if (is_wp_error($valid)) {
            $this->error_message = $valid->get_error_message();
            return false;
        }
        return $valid;
    }
    public function is_valid_for_cart()
    {
        return apply_filters('woocommerce_coupon_is_valid_for_cart', $this->is_type(wc_get_cart_coupon_types()), $this);
    }
    public function is_valid_for_product($product, $values = array())
    {
        if (!$this->is_type(wc_get_product_coupon_types())) {
            return apply_filters('woocommerce_coupon_is_valid_for_product', false, $product, $this, $values);
        }
        $valid = false;
        $product_cats = wc_get_product_cat_ids($product->is_type('variation') ? $product->get_parent_id() : $product->get_id());
        $product_ids = array($product->get_id(), $product->get_parent_id());
        if (count($this->get_product_ids()) && count(array_intersect($product_ids, $this->get_product_ids()))) {
            $valid = true;
        }
        if (count($this->get_product_categories()) && count(array_intersect($product_cats, $this->get_product_categories()))) {
            $valid = true;
        }
        if (!count($this->get_product_ids()) && !count($this->get_product_categories())) {
            $valid = true;
        }
        if (count($this->get_excluded_product_ids()) && count(array_intersect($product_ids, $this->get_excluded_product_ids()))) {
            $valid = false;
        }
        if (count($this->get_excluded_product_categories()) && count(array_intersect($product_cats, $this->get_excluded_product_categories()))) {
            $valid = false;
        }
        if ($this->get_exclude_sale_items() && $product->is_on_sale()) {
            $valid = false;
        }
        return apply_filters('woocommerce_coupon_is_valid_for_product', $valid, $product, $this, $values);
    }
    public function add_coupon_message($msg_code)
    {
        $msg = $msg_code < 200 ? $this->get_coupon_error($msg_code) : $this->get_coupon_message($msg_code);
        if (!$msg) {
            return;
        }
        if ($msg_code < 200) {
            wc_add_notice($msg, 'error');
        } else {
            wc_add_notice($msg);
        }
    }
    public function get_coupon_message($msg_code)
    {
        switch ($msg_code) {
            case self::WC_COUPON_SUCCESS:
                $msg = __('Coupon code applied successfully.', 'woocommerce');
                break;
            case self::WC_COUPON_REMOVED:
                $msg = __('Coupon code removed successfully.', 'woocommerce');
                break;
            default:
                $msg = '';
                break;
        }
        return apply_filters('woocommerce_coupon_message', $msg, $msg_code, $this);
    }
    public function get_coupon_error($err_code)
    {
        switch ($err_code) {
            case self::E_WC_COUPON_INVALID_FILTERED:
                $err = __('Coupon is not valid.', 'woocommerce');
                break;
            case self::E_WC_COUPON_NOT_EXIST:
                $err = sprintf(__('Coupon "%s" does not exist!', 'woocommerce'), esc_html($this->get_code()));
                break;
            case self::E_WC_COUPON_INVALID_REMOVED:
                $err = sprintf(__('Sorry, it seems the coupon "%s" is invalid - it has now been removed from your order.', 'woocommerce'), esc_html($this->get_code()));
                break;
            case self::E_WC_COUPON_NOT_YOURS_REMOVED:
                $err = sprintf(__('Sorry, it seems the coupon "%s" is not yours - it has now been removed from your order.', 'woocommerce'), esc_html($this->get_code()));
                break;
            case self::E_WC_COUPON_ALREADY_APPLIED:
                $err = __('Coupon code already applied!', 'woocommerce');
                break;
            case self::E_WC_COUPON_ALREADY_APPLIED_INDIV_USE_ONLY:
                $err = sprintf(__('Sorry, coupon "%s" has already been applied and cannot be used in conjunction with other coupons.', 'woocommerce'), esc_html($this->get_code()));
                break;
            case self::E_WC_COUPON_USAGE_LIMIT_REACHED:
                $err = __('Coupon usage limit has been reached.', 'woocommerce');
                break;
            case self::E_WC_COUPON_EXPIRED:
                $err = __('This coupon has expired.', 'woocommerce');
                break;
            case self::E_WC_COUPON_MIN_SPEND_LIMIT_NOT_MET:
                $err = sprintf(__('The minimum spend for this coupon is %s.', 'woocommerce'), wc_price($this->get_minimum_amount()));
                break;
            case self::E_WC_COUPON_MAX_SPEND_LIMIT_MET:
                $err = sprintf(__('The maximum spend for this coupon is %s.', 'woocommerce'), wc_price($this->get_maximum_amount()));
                break;
            case self::E_WC_COUPON_NOT_APPLICABLE:
                $err = __('Sorry, this coupon is not applicable to your cart contents.', 'woocommerce');
                break;
            case self::E_WC_COUPON_USAGE_LIMIT_COUPON_STUCK:
                if (is_user_logged_in() && wc_get_page_id('myaccount') > 0) {
                    $err = sprintf(__('Coupon usage limit has been reached. If you were using this coupon just now but order was not complete, you can retry or cancel the order by going to the <a href="%s">my account page</a>.', 'woocommerce'), wc_get_endpoint_url('orders', '', wc_get_page_permalink('myaccount')));
                } else {
                    $err = $this->get_coupon_error(self::E_WC_COUPON_USAGE_LIMIT_REACHED);
                }
                break;
            case self::E_WC_COUPON_USAGE_LIMIT_COUPON_STUCK_GUEST:
                $err = __('Coupon usage limit has been reached. Please try again after some time, or contact us for help.', 'woocommerce');
                break;
            case self::E_WC_COUPON_EXCLUDED_PRODUCTS:
                $products = array();
                if (!WC()->cart->is_empty()) {
                    foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
                        if (in_array(intval($cart_item['product_id']), $this->get_excluded_product_ids(), true) || in_array(intval($cart_item['variation_id']), $this->get_excluded_product_ids(), true) || in_array(intval($cart_item['data']->get_parent_id()), $this->get_excluded_product_ids(), true)) {
                            $products[] = $cart_item['data']->get_name();
                        }
                    }
                }
                $err = sprintf(__('Sorry, this coupon is not applicable to the products: %s.', 'woocommerce'), implode(', ', $products));
                break;
            case self::E_WC_COUPON_EXCLUDED_CATEGORIES:
                $categories = array();
                if (!WC()->cart->is_empty()) {
                    foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
                        $product_cats = wc_get_product_cat_ids($cart_item['product_id']);
                        $intersect = array_intersect($product_cats, $this->get_excluded_product_categories());
                        if (count($intersect) > 0) {
                            foreach ($intersect as $cat_id) {
                                $cat = get_term($cat_id, 'product_cat');
                                $categories[] = $cat->name;
                            }
                        }
                    }
                }
                $err = sprintf(__('Sorry, this coupon is not applicable to the categories: %s.', 'woocommerce'), implode(', ', array_unique($categories)));
                break;
            case self::E_WC_COUPON_NOT_VALID_SALE_ITEMS:
                $err = __('Sorry, this coupon is not valid for sale items.', 'woocommerce');
                break;
            default:
                $err = '';
                break;
        }
        return apply_filters('woocommerce_coupon_error', $err, $err_code, $this);
    }
    public static function get_generic_coupon_error($err_code)
    {
        switch ($err_code) {
            case self::E_WC_COUPON_NOT_EXIST:
                $err = __('Coupon does not exist!', 'woocommerce');
                break;
            case self::E_WC_COUPON_PLEASE_ENTER:
                $err = __('Please enter a coupon code.', 'woocommerce');
                break;
            default:
                $err = '';
                break;
        }
        return apply_filters('woocommerce_coupon_error', $err, $err_code, null);
    }
}