<?php
/**
 * @author      Flycart (Alagesan)
 * @license     http://www.gnu.org/licenses/gpl-3.0.html
 * @link        https://www.flycart.org
 * */

namespace Wlpr\App\Helpers;

use ActionScheduler;
use DateTime;
use DateTimeZone;
use Wlpr\App\Controllers\Site\Main;

class Woocommerce
{
    public static $instance = null;

    /**
     * class instance
     * @param array $config
     * @return Woocommerce|null
     * @since 1.0.0
     */
    public static function getInstance(array $config = array())
    {
        if (!self::$instance) {
            self::$instance = new self($config);
        }
        return self::$instance;
    }

    /**
     * Get order object
     * @param $order_id
     * @return bool|\WC_Order|null
     * @since 1.0.0
     */
    function getOrder($order_id)
    {
        if (function_exists('wc_get_order')) {
            return wc_get_order($order_id);
        }
        return NULL;
    }

    public function get_order_by_bill_email( $email ) {
        global $wpdb;
        if(empty($email)){
            return false;
        }
        $order_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT ID FROM $wpdb->posts as posts LEFT JOIN $wpdb->postmeta as meta ON posts.ID = meta.post_id WHERE meta.meta_value = %s AND meta.meta_key = %s AND posts.post_status in(%s,%s)", $email, '_billing_email',"wc-processing","wc-completed" ) );

        if ( ! empty( $order_id ) ) {
            return wc_get_order( $order_id );
        }

        return false;
    }

    /**
     * Get cart items
     * @return array
     * @since 1.0.0
     */
    function getCart()
    {
        if (method_exists(WC()->cart, 'get_cart')) {
            return WC()->cart->get_cart();
        }
        return array();
    }

    /**
     * Check the coupon code is available on cart
     * @param $discount_code
     * @return bool
     * @since 1.0.0
     */
    function hasDiscount($discount_code)
    {
        if (empty($discount_code))
            return false;
        if (method_exists(WC()->cart, 'has_discount')) {
            return WC()->cart->has_discount($discount_code);
        }
        return false;
    }

    /**
     * Add discount to cart
     * @param $discount_code
     * @return bool
     * @since 1.0.0
     */
    function addDiscount($discount_code)
    {
        if (empty($discount_code))
            return false;
        if (method_exists(WC()->cart, 'add_discount')) {
            return WC()->cart->add_discount($discount_code);
        }
        return false;
    }

    /**
     * Returns the unique discount code generated for the applied discount if set
     * @return array|string|null
     * @since 1.0.0
     */
    public function get_discount_code()
    {
        if (WC()->session !== null) {
            return WC()->session->get('wlpr_points_discount_code');
        }
        return null;
    }

    public function set_discount_code($discount_code = '')
    {
        if (WC()->session !== null) {
            WC()->session->set('wlpr_points_discount_code',$discount_code);
        }
    }

    public function get_discount_amount(){
        $discount_amount = 0;
        if (WC()->session !== null) {
            $discount_amount = WC()->session->get('wlpr_discount_amount', 0);
        }
        return $discount_amount;
    }
    public function set_discount_amount($discount_amount = 0){
        if (WC()->session !== null) {
            WC()->session->set('wlpr_discount_amount', $discount_amount);
        }
    }
    /**
     * get referral coupon code from session
     * @return array|string|null
     * @since 1.0.0
     */
    public function get_refer_discount_code()
    {
        $ref_code = '';
        if (isset(WC()->session) && WC()->session !== null) {
            $ref_code =  WC()->session->get('wlpr_referral_coupon','');
        }
        return $ref_code;
    }

    /**
     * set referral coupon code to session
     * @param $discount_code
     * @return void|null
     * @since 1.0.0
     */
    public function set_refer_discount_code($discount_code)
    {
        if (isset(WC()->session) && WC()->session !== null) {
            WC()->session->set('wlpr_referral_coupon',$discount_code);
        }
    }

    /**
     * Generates a unique discount code tied to the current user ID and timestamp
     * @return string
     * @since 1.0.0
     */
    public static function generate_discount_code()
    {
        // set the discount code to the current user ID + the current time in YYYY_MM_DD_H_M format
        $discount_code = sprintf('wlpr_points_redemption_%s_%s', get_current_user_id(), date('Y_m_d_h_i', current_time('timestamp')));
        $discount_code = apply_filters('wlpr_create_discount_code',$discount_code);
        WC()->session->set('wlpr_points_discount_code', $discount_code);
        return $discount_code;
    }

    /**
     * Get the discount amount associated with the given code.
     *
     * @param string $discount_code The unique discount code generated for the applied discount.
     * @return string $discount_amount - discounted amount
     * @since 1.0.0
     */
    public function get_discount_from_code($discount_code,$order = "")
    {
        $discount_amount = 0;
        $is_cart_discount_calculated = false;
        $setting_option = get_option('wlpr_settings', '');
        if (isset(WC()->cart->coupon_discount_amounts[$discount_code])) {
            $discount_amount += WC()->cart->coupon_discount_amounts[$discount_code];
            $is_cart_discount_calculated = true;
            $tax_inclusive = (isset($setting_option['wlpr_points_tax_application']) && !empty($setting_option['wlpr_points_tax_application'])) ? $setting_option['wlpr_points_tax_application'] : 'exclusive';
            $tax_inclusive = apply_filters( 'wlpr_redeem_point_tax_type', $tax_inclusive );
            if ($tax_inclusive == 'inclusive' && isset(WC()->cart->coupon_discount_tax_amounts[$discount_code])) {
                $discount_amount += WC()->cart->coupon_discount_tax_amounts[$discount_code];
            }
        }
        if(!$is_cart_discount_calculated && isset($order) && is_object($order) && $order->get_coupons()){
            foreach ($order->get_coupons() as $order_discount){
                $coupon_code = $order_discount->get_code();
                if($coupon_code == $discount_code){
                    $discount_amount += $order_discount->get_discount();
                    $discount_tax = $order_discount->get_discount_tax();
                    $tax_inclusive = (isset($setting_option['wlpr_points_tax_application']) && !empty($setting_option['wlpr_points_tax_application'])) ? $setting_option['wlpr_points_tax_application'] : 'exclusive';
                    $tax_inclusive = apply_filters( 'wlpr_redeem_point_tax_type', $tax_inclusive );
                    if ($tax_inclusive == 'inclusive' && !empty($discount_tax)) {
                        $discount_amount += $discount_tax;
                    }
                }
            }
        }
        return $discount_amount;
    }

    /**
     * Get total amount discounted by existing fixed_product coupons.
     * @return float
     * @since 1.0.0
     */
    public function get_discount_total_from_existing_coupons()
    {
        $total_discount = 0;
        foreach (WC()->cart->get_cart() as $item) {
            $total_discount += $this->get_cart_item_discount_total($item);
        }
        return $total_discount;
    }

    /**
     * Get discount total (using fixed_product coupons) from a given cart item.
     * @param mixed $item Cart item.
     * @return float Discount total.
     * @since 1.0.0
     */
    public function get_cart_item_discount_total($item)
    {
        // Since we call get_discount_amount this could potentially result in
        // a loop.
        $this->remove_hook();
        //$this->hooks('remove');
        $product_helper = Loyalty::product();
        $discount = 0;
        foreach (WC()->cart->get_coupons() as $coupon) {
            if (strtolower($coupon->get_code()) === $this->get_discount_code() || !$coupon->is_type('fixed_product')) {
                continue;
            }
            if (!$coupon->is_valid()) {
                continue;
            }
            if ($coupon->is_valid_for_product($item['data'], $item) || $coupon->is_valid_for_cart()) {
                $product_price = $product_helper->getProductPrice($item['data']);
                $discount += (float)$coupon->get_discount_amount($product_price, $item, true) * $item['quantity'];
            }
        }
        // Add the hooks back.
        $this->hooks('add');
        return $discount;
    }

    /**
     * Add or remove callbacks to/from the hooks.s
     * @param string $verb What operation to perform (either 'add' or 'remove').
     * @since 1.0.0
     */
    protected function hooks($verb)
    {
        $site = new Main();
        $filters = array(
            array('woocommerce_get_shop_coupon_data', array($site, 'getDiscountData'), 10, 2),
            array('woocommerce_coupon_message', array($site, 'getDiscountAppliedMessage'), 10, 3),
            array('woocommerce_coupon_get_discount_amount', array($site, 'getDiscountAmount'), 10, 5),
        );
        $func = 'add' === $verb ? 'add_filter' : 'remove_filter';
        foreach ($filters as $filter) {
            call_user_func_array($func, $filter);
        }
    }

    /**
     * Check if order is renewal in case Subscriptions is enabled.
     * @param \WC_Order $order
     * @return bool
     * @since 1.0.0
     */
    function is_order_renewal($order)
    {
        if (!function_exists('wcs_order_contains_resubscribe') || !function_exists('wcs_order_contains_renewal')) {
            return false;
        }
        if (!wcs_order_contains_resubscribe($order) && !wcs_order_contains_renewal($order)) {
            return false;
        }
        return true;
    }


    /**
     * Suppress third party discount plugins from accessing the discount data
     * @since 1.0.0
     */
    private function remove_hook()
    {
        global $wp_filter;
        $site = new Main();
        $allowed_hooks = array('woocommerce_get_shop_coupon_data' => array($site, 'getDiscountData', 10),
            'woocommerce_coupon_message' => array($site, 'getDiscountAppliedMessage', 10),
            'woocommerce_coupon_get_discount_amount' => array($site, 'getDiscountAmount', 10)
        );
        foreach ($wp_filter as $hook_name => $hook_obj) {
            if (preg_match('#^woocommerce_#', $hook_name)) {
                if (isset($allowed_hooks[$hook_name])) {
                    $wp_filter[$hook_name] = $this->removeWrongCallbacks($hook_obj, $allowed_hooks[$hook_name]);
                }
            }
        }
    }

    /**
     * Remove the third party call backs
     * @param $hook_obj
     * @param $allowed_hooks
     * @return mixed
     * @since 1.0.0
     */
    function removeWrongCallbacks($hook_obj, $allowed_hooks)
    {
        $new_callbacks = array();
        foreach ($hook_obj->callbacks as $priority => $callbacks) {
            $priority_callbacks = array();
            foreach ($callbacks as $idx => $callback_details) {
                if ($this->isCallbackMatch($callback_details, $allowed_hooks)) {
                    $priority_callbacks[$idx] = $callback_details;
                }
            }
            if ($priority_callbacks) {
                $new_callbacks[$priority] = $priority_callbacks;
            }
        }
        $hook_obj->callbacks = $new_callbacks;
        return $hook_obj;
    }

    /**
     * check the call back matched or not
     * @param $callback_details
     * @param $allowed_hooks
     * @return bool
     * @since 1.0.0
     */
    function isCallbackMatch($callback_details, $allowed_hooks)
    {
        $result = true;
        $class_obj = (isset($allowed_hooks[0]) && !empty($allowed_hooks[0])) ? $allowed_hooks[0]:'';
        if (!is_array($callback_details['function']) || count($callback_details['function']) != 2 || !is_object($class_obj) || !isset($allowed_hooks[1])
            || !isset($callback_details['function'][0]) || !is_object($callback_details['function'][0])) {
            return $result;
        }
        if (get_class($class_obj) == get_class($callback_details['function'][0]) AND $allowed_hooks[1] == $callback_details['function'][1]) {
            $result = false;
        }
        return $result;
    }

    /**
     * convert UTC time to wordpress time zone
     * @param $datetime
     * @param string $format
     * @return string
     * @since 1.0.0
     */
    function convert_utc_to_wp_time($datetime, $format = 'Y-m-d H:i:s', $modify = '')
    {
        try{
            $timezone = new DateTimeZone('UTC');
            $current_time = new DateTime($datetime, $timezone);
            if (!empty($modify)) {
                $current_time->modify($modify);
            }
            $wp_time_zone = new DateTimeZone($this->get_wp_time_zone());
            $current_time->setTimezone($wp_time_zone);
            $converted_time = $current_time->format($format);
        }catch (\Exception $e){
            $converted_time = $datetime;
        }
        return $converted_time;
    }

    /**
     * convert wordpress time to UTC
     * @param $datetime
     * @param string $format
     * @return string
     * @throws \Exception
     * @since 1.0.0
     */
    function convert_wp_time_to_utc($datetime, $format = 'Y-m-d H:i:s', $modify = '')
    {
        $wp_time_zone = new DateTimeZone($this->get_wp_time_zone());
        $current_time = new DateTime($datetime, $wp_time_zone);
        if (!empty($modify)) {
            $current_time->modify($modify);
        }
        $timezone = new DateTimeZone('UTC');
        $current_time->setTimezone($timezone);
        return $current_time->format($format);
    }

    /**
     * Get wordpress time zone
     * @return mixed|string|void
     * @since 1.0.0
     */
    function get_wp_time_zone()
    {
        if (!function_exists('wp_timezone_string')) {
            $timezone_string = get_option('timezone_string');
            if ($timezone_string) {
                return $timezone_string;
            }
            $offset = (float)get_option('gmt_offset');
            $hours = (int)$offset;
            $minutes = ($offset - $hours);
            $sign = ($offset < 0) ? '-' : '+';
            $abs_hour = abs($hours);
            $abs_mins = abs($minutes * 60);
            $tz_offset = sprintf('%s%02d:%02d', $sign, $abs_hour, $abs_mins);
            return $tz_offset;
        }
        return wp_timezone_string();
    }

    function current_offset(){
        $timezone = new DateTimeZone($this->get_wp_time_zone());
        $origin_dt = new DateTime("now", $timezone);

        $init = $origin_dt->getOffset();
        $hours = floor($init / 3600);
        $sign = '';
        if($hours >= 0){
            $sign = '+';
        }
        $minutes = floor(($init / 60) % 60);
        $offset = $sign.sprintf("%02d", $hours).':'.sprintf("%02d", $minutes);
        return $offset;
    }

    /**
     * Delete schedule action
     * @param string $hook
     * @since 1.0.0
     */
    function delete_schedule($hook = '')
    {
        if (empty($hook)) {
            $hook = 'wlpr_expire_points';
        }
        $jobs = as_get_scheduled_actions(array('per_page' => 500, 'hook' => $hook));
        $total_jobs = array();
        foreach ($jobs as $job_id => $job) {
            if (get_class($job) != 'ActionScheduler_Action') {
                $total_jobs[$job_id] = $job;
            } else {
                ActionScheduler::store()->delete_action($job_id);
            }
        }
    }

    /**
     * Updates a term meta. Compatibility function for WC 3.6.
     * @param int $term_id Term ID.
     * @param string $meta_key Meta key.
     * @param mixed $meta_value Meta value.
     * @return bool
     * @since 1.0.0
     */
    function update_term_meta($term_id, $meta_key, $meta_value)
    {
        if (version_compare(WC_VERSION, '3.6', 'ge')) {
            return update_term_meta($term_id, $meta_key, $meta_value);
        } else {
            return update_woocommerce_term_meta($term_id, $meta_key, $meta_value);
        }
    }

    /**
     * Deletes a term meta. Compatibility function for WC 3.6.
     * @param int $term_id Term ID.
     * @param string $meta_key Meta key.
     * @return bool
     * @since 1.0.0
     */
    function delete_term_meta($term_id, $meta_key)
    {
        if (version_compare(WC_VERSION, '3.6', 'ge')) {
            return delete_term_meta($term_id, $meta_key);
        } else {
            return delete_woocommerce_term_meta($term_id, $meta_key);
        }
    }

    /**
     * Get a term meta
     * @param $term_id
     * @param $key
     * @param bool $single
     * @return mixed
     * @since 1.0.0
     */
    function get_term_meta($term_id, $key, $single = true)
    {
        if (version_compare(WC_VERSION, '3.6', 'ge')) {
            return get_term_meta($term_id, $key, $single);
        } else {
            return get_woocommerce_term_meta($term_id, $key, $single);
        }
    }

    /**
     * Format order statuses by removing a leading 'wc-' if present.
     *
     * @param array $statuses Order statuses.
     * @return array formatted statuses.
     */
    public static function format_order_statuses( $statuses ) {
        $formatted_statuses = array();
        foreach ( $statuses as $key => $value ) {
            $formatted_key                        = preg_replace( '/^wc-/', '', $key );
            $formatted_statuses[ $formatted_key ] = $value;
        }
        return $formatted_statuses;
    }

    function get_order_statuses(){
        return self::format_order_statuses(wc_get_order_statuses());
    }

    /**
     * remove Html Tags
     *
     * @param $html
     * @return string
     */
    public static function removeHtmlTags($html){
        if (function_exists('wp_strip_all_tags')) {
            $html = wp_strip_all_tags($html);
        }else{
            $html = strip_tags($html);
        }
        return $html;
    }

    public static function hasAdminPrivilege()
    {
        if (current_user_can('manage_woocommerce')) {
            return true;
        } else {
            return false;
        }
    }

    public static function getCleanHtml($html)
    {
        try {
            $html = html_entity_decode($html);
            $html = preg_replace('/(<(script|style|iframe)\b[^>]*>).*?(<\/\2>)/is', "$1$3", $html);
            $allowed_html = array(
                'br' => array(),
                'strong' => array(),
                'span' => array('class' => array()),
                'div' => array('class' => array()),
                'p' => array('class' => array()),
            );
            return wp_kses($html, $allowed_html);
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * check the given content is json or not
     * @param $string
     * @return bool
     */
    static function isJson($string)
    {
        json_decode($string);
        return (json_last_error() == JSON_ERROR_NONE);
    }

    /**
     * sanitize the json data
     * @param $data
     * @return bool|false|mixed|string
     */
    static function sanitizeJson($data)
    {
        $arr = array();
        if (is_array($data)) {
            $arr = $data;
        } elseif (is_object($data)) {
            $encoded = wp_json_encode($data);
            $arr = json_decode($encoded, true);
        } elseif (self::isJson($data)) {
            $arr = json_decode($data, true);
        }
        $result = array();
        if (is_array($arr) && !empty($arr)) {
            foreach ($arr as $key => $value) {
                if (is_array($value) || is_object($value)) {
                    $value = self::sanitizeJson($value);
                    $result[sanitize_key($key)] = $value;
                } else {
                    if (is_string($value)) {
                        $value = sanitize_text_field($value);
                    } elseif (is_int($value)) {
                        $value = intval($value);
                    } elseif (is_float($value)) {
                        $value = floatval($value);
                    } else {
                        $value = sanitize_text_field($value);
                    }
                    $result[sanitize_key($key)] = $value;
                }
            }
        }
        return $result;
    }

    public static function create_nonce($action = -1)
    {
        return wp_create_nonce($action);
    }

    public static function verify_nonce($nonce, $action = -1)
    {
        if (wp_verify_nonce($nonce, $action)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * check woocommerce session has started
     * @return bool
     */
    function hasSession()
    {
        if (!isset(\WC()->session) && class_exists('WC_Session_Handler')) {
            \WC()->session = new \WC_Session_Handler();
            \WC()->session->init();
        }
        if (method_exists(WC()->session, 'has_session')) {
            return WC()->session->has_session();
        }
        return false;
    }

    /**
     * Init the woocommerce session when it was not initlized
     */
    function initWoocommerceSession()
    {
        if (!$this->hasSession() && !defined('DOING_CRON')) {
            $this->setSessionCookie(true);
        }
    }

    /**
     * set customer session cookie
     * @param $value
     * @return bool
     */
    function setSessionCookie($value)
    {
        if (method_exists(WC()->session, 'set_customer_session_cookie')) {
            WC()->session->set_customer_session_cookie($value);
        }
        return true;
    }

    function getDisplayOrderId($order_id){
        return apply_filters('wlpr_display_order_id',$order_id);
    }
}