<?php

class WooProductItem extends ProductItem {
    protected $_options;
    public $productData;

    /**
     * @param array $options Options
     */
    protected function __construct( $options ) {
        parent::__construct($options);
        $this->_options = $options;
    }

    /**
     * Process product item
     *
     * @param string $content  Page content
     * @param array  $products Site products
     *
     * @return string|string[]|null
     */
    protected function _processProductItem($content, $products) {
        $this->products = $products;
        $reProductItem = '/<\!--product_item-->([\s\S]+?)<\!--\/product_item-->/';
        preg_match_all($reProductItem, $content, $matches, PREG_SET_ORDER);
        $allTemplates = count($matches);
        if ($allTemplates > 0) {
            $allProductsHtml = '';
            global $products_control_query;
            if (($products_control_query && method_exists($products_control_query, 'have_posts')) || $products) {
                if (count($products) < 1) {
                    return ''; // remove cell, if products is missing
                }
                $productsCount = isset($this->_options['count']) ? (int) $this->_options['count'] : '';
                if ($productsCount && count($products) > $productsCount) {
                    $products = array_slice($products, 0, $productsCount);
                }

                $i = 0;
                while(count($products) > 0) :
                    $tmplIndex = $i % $allTemplates;
                    $productItemHtml = $matches[$tmplIndex][1];
                    $products_control_query->the_post();
                    $product = array_shift($products);
                    $this->productData = np_data_product($product->ID);
                    if (count($this->productData) > 0) {
                        $allProductsHtml .= $this->_replaceProductItemControls($productItemHtml);
                    }
                    $i++;
                endwhile;
            }
        }
        $content = preg_replace('/<!--product_item-->([\s\S]+)<!--\/product_item-->/', $allProductsHtml, $content);
        $content = NpAdminActions::processPagination($content, 'products', $this->_options['siteProductsProcess']);
        $content = $this->processSorting($content);
        $content = $this->processCategoriesFilter($content);
        $content = $this->_buildGridAutoRows($products, $this->_options, $content);
        return $content;
    }

    /**
     * Process sorting for products controls
     *
     * @param string $content
     *
     * @return string $content
     */
    public function processSorting($content) {
        $content = preg_replace_callback(
            '/<\!--products_sorting-->([\s\S]+?)<\!--\/products_sorting-->/',
            function ($sortingMatch) {
                $sortingHtml = $sortingMatch[1];
                preg_match('/<option[\s\S]*?>[\s\S]+?<\/option>/', $sortingHtml, $sortingOptions);
                $firstOptionHtml = $sortingOptions[0];
                $sortingHtml = preg_replace('/<option[\s\S]*?>[\s\S]*<\/option>/', '{sortingOptions}', $sortingHtml);
                $sorting_options = array(
                    'default'    => __('Default sorting', 'woocommerce'),
                    'popularity' => __('Sort by popularity', 'woocommerce'),
                    'rating'     => __('Sort by average rating', 'woocommerce'),
                    'date'       => __('Sort by latest', 'woocommerce'),
                    'price'      => __('Sort by price: low to high', 'woocommerce'),
                    'price-desc' => __('Sort by price: high to low', 'woocommerce'),
                );
                $sortingOptionsHtml = '';
                $activeSorting = isset($_GET['sorting']) ? $_GET['sorting'] : false;
                foreach ($sorting_options as $name => $sorting_option) {
                    $doubleOptionHtml = $firstOptionHtml;
                    $doubleOptionHtml = preg_replace('/value=[\'"][\s\S]*?[\'"]/', 'value="' . $name . '"', $doubleOptionHtml);
                    $doubleOptionHtml = preg_replace('/(<option[\s\S]*?>)[\s\S]+?<\/option>/', '$1' . $sorting_option . '</option>', $doubleOptionHtml);
                    if ($activeSorting && $name === $activeSorting) {
                        $doubleOptionHtml = str_replace('<option', '<option selected="selected"', $doubleOptionHtml);
                    }
                    $sortingOptionsHtml .= $doubleOptionHtml;
                }
                $sortingHtml = str_replace('{sortingOptions}', $sortingOptionsHtml, $sortingHtml);
                return $sortingHtml;
            },
            $content
        );
        return $content;
    }

    /**
     * Replace placeholder for product item controls
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replaceProductItemControls($content) {
        $product = $this->productData['product'];
        if (!$product) {
            return $content;
        }
        $content = $this->_replaceTitle($content);
        $content = $this->_replaceFullDesc($content);
        $content = $this->_replaceShortDesc($content);
        $content = $this->_replaceImage($content);
        $content = $this->_replaceButton($content);
        $content = $this->_replacePrice($content);
        $content = $this->_replaceGallery($content);
        $content = $this->_replaceVariations($content);
        $content = $this->_replaceTabs($content);
        $content = $this->_replaceQuantity($content);
        $content = $this->_replaceCategory($content);
        $content = $this->_setProductBadge($content);
        $content = $this->_replaceOutOfStock($content);
        $content = $this->_replaceSku($content);
        if ($this->_options['productName'] === 'product-details') {
            $content = $this->_addProductSchema($content);
        }
        return $content;
    }

    /**
     * Replace placeholder for product title
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replaceTitle($content) {
        return preg_replace_callback(
            '/<!--product_title-->([\s\S]+?)<!--\/product_title-->/',
            function ($titleMatch) {
                $titleHtml = $titleMatch[1];
                $titleHtml = $this->_replaceTitleUrl($titleHtml);
                $titleHtml = $this->_replaceTitleContent($titleHtml);
                return $titleHtml;
            },
            $content
        );
    }

    /**
     * Replace placeholder for product title content
     *
     * @param string $content title html
     *
     * @return string $content
     */
    private function _replaceTitleContent($content) {
        $productTitle = $this->productData['title'];
        $productTitle = $productTitle ? $productTitle : $this->productData['product']->post_title;
        if (isset($productTitle) && $productTitle != '') {
            $content = preg_replace('/(<a\b[^>]*>).*?(<\/a>)/s', '$1' . $productTitle . '$2', $content);
        }
        return $content;
    }

    /**
     * Replace placeholder for product title url
     *
     * @param string $content title html
     *
     * @return string $content
     */
    protected function _replaceTitleUrl($content) {
        $postUrl = get_permalink($this->productData['product']->ID);
        $postUrl = $postUrl ? $postUrl : '#';
        if ($postUrl) {
            $content = preg_replace('/href=[\'|"][\s\S]+?[\'|"]/', 'href="' . $postUrl . '"', $content);
        }
        return $content;
    }

    /**
     * Replace placeholder for product description
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replaceFullDesc($content) {
        return preg_replace_callback(
            '/<!--product_description-->([\s\S]+?)<!--\/product_description-->/',
            function ($textMatch) {
                $product = $this->productData['product'];
                $textHtml = $textMatch[1];
                $productContent = $this->productData['fullDesc'];
                if (isset($productContent)) {
                    $stock_html = wc_get_stock_html($product);
                    if ($stock_html && $stock_html !== '') {
                        $productContent .= '</br>' . $stock_html;
                    }
                    $textHtml = preg_replace('/<!--product_description_content-->([\s\S]+?)<!--\/product_description_content-->/s', $productContent, $textHtml);
                }
                return $textHtml;
            },
            $content
        );
    }

    /**
     * Replace placeholder for product short description
     *
     * @param string $content
     *
     * @return string $content
     */
    private function _replaceShortDesc($content) {
        return preg_replace_callback(
            '/<!--product_content-->([\s\S]+?)<!--\/product_content-->/',
            function ($textMatch) {
                $product = $this->productData['product'];
                $textHtml = $textMatch[1];
                $productContent = $this->productData['shortDesc'];
                if (isset($productContent)) {
                    $stock_html = wc_get_stock_html($product);
                    if ($stock_html && $stock_html !== '') {
                        $productContent .= '</br>' . $stock_html;
                    }
                    $textHtml = preg_replace('/<!--product_content_content-->([\s\S]+?)<!--\/product_content_content-->/s', $productContent, $textHtml);
                }
                return $textHtml;
            },
            $content
        );
    }

    /**
     * Replace placeholder for product image
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replaceImage($content) {
        return preg_replace_callback(
            '/<!--product_image-->([\s\S]+?)<!--\/product_image-->/',
            function ($imageMatch) {
                $imageHtml = $imageMatch[1];
                $url = $this->productData['image_url'];
                if (!$url) {
                    return '<div class="none-post-image" style="display: none;"></div>';
                }
                $isBackgroundImage = strpos($imageHtml, '<div') !== false ? true : false;
                $link = get_permalink($this->productData['product']->ID);
                if ($isBackgroundImage) {
                    $imageHtml = str_replace('<div', '<div data-product-control="' . $link . '"', $imageHtml);
                    if (strpos($imageHtml, 'data-bg') !== false) {
                        $imageHtml = preg_replace('/(data-bg=[\'"])([\s\S]+?)([\'"])/', '$1url(' . $url . ')$3', $imageHtml);
                    } else {
                        $imageHtml = str_replace('<div', '<div' . ' style="background-image:url(' . $url . ')"', $imageHtml);
                    }
                } else {
                    if (strpos($imageHtml, 'data-product-control') !== false) {
                        $imageHtml = preg_replace('/data-product-control="[\s\S]*?"/', '<div data-product-control="' . $link . '"', $imageHtml);
                        $imageHtml = preg_replace('/(src=[\'"])([\s\S]+?)([\'"])/', '$1' . $url . '$3 style="cursor:pointer;" ', $imageHtml);
                    } else {
                        $imageHtml = preg_replace('/(src=[\'"])([\s\S]+?)([\'"])/', '$1' . $url . '$3 style="cursor:pointer;" data-product-control="' . $link . '"', $imageHtml);
                    }
                }
                if ($this->_options['showSecondImage']) {
                    $url = $this->productData['second_image_url'];
                    if ($isBackgroundImage) {
                        $secondImageHtml = '<img class="u-product-second-image" src="' . $url . '"/>';
                    } else {
                        $secondImageHtml = preg_replace('/(class=[\'"])([\s\S]+?)([\'"])/', '$1u-product-second-image$3', $imageHtml);
                        $secondImageHtml = preg_replace('/(src=[\'"])([\s\S]+?)([\'"])/', '$1' . $url . '$3 style="cursor:pointer;" data-product-control="' . $link . '"', $secondImageHtml);
                    }
                    $imageHtml = $imageHtml . $secondImageHtml;
                }
                return $imageHtml;
            },
            $content
        );
    }

    /**
     * Replace placeholder for product button add to cart
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replaceButton($content) {
        return preg_replace_callback(
            '/<!--product_button-->([\s\S]+?)<!--\/product_button-->/',
            function ($buttonMatch) {
                global $product;
                $product = $this->productData['product'];
                $button_html = $buttonMatch[1];
                $controlOptions = array();
                if (preg_match('/<\!--options_json--><\!--([\s\S]+?)--><\!--\/options_json-->/', $button_html, $matches)) {
                    $controlOptions = json_decode($matches[1], true);
                    $button_html = str_replace($matches[0], '', $button_html);
                }
                $button_html = str_replace(array('u-dialog-link', 'u-payment-button'), array('', ''), $button_html);
                if (isset($controlOptions['content']) && $controlOptions['content']) {
                    $this->productData['add_to_cart_text'] = $controlOptions['content'];
                }
                $buttonText = sprintf(__('%s', 'woocommerce'), $this->productData['add_to_cart_text']);
                if ($this->_options['typeControl'] === "products") {
                    $button_html = preg_replace('/href=[\'|"][\s\S]*?[\'|"]/', 'href="%s"', $button_html);
                    $button_html = preg_replace('/<!--product_button_content-->([\s\S]+?)<!--\/product_button_content-->/', '%s', $button_html);
                }
                $goToProduct = false;
                if (isset($controlOptions['clickType']) && $controlOptions['clickType'] === 'go-to-page') {
                    $goToProduct = true;
                }
                if ($goToProduct) {
                    $button_html = sprintf(
                        $button_html,
                        get_permalink($this->productData['product']->ID),
                        $buttonText
                    );
                } else {
                    if ($this->_options['typeControl'] === "products") {
                        $button_html = preg_replace('/class=[\'|"]([\s\S]+?)[\'|"]/', 'class="$1 %s"', $button_html);
                        $button_html = str_replace('href', 'data-quantity="1" data-product_id="%s" data-product_sku="%s" href', $button_html);
                    }
                    $button_html = NpDataProduct::getProductButtonHtml($button_html, $product, $this->_options['typeControl'], $controlOptions);
                    if ($this->_options['typeControl'] === "product" && $this->productData['type'] !== "variable") {
                        ob_start();
                        woocommerce_template_single_add_to_cart();
                        $form = ob_get_clean();
                        $form = preg_replace('/<p class="stock.+">[\s\S]+?<\/p>/', '', $form);
                        return $form = preg_replace('/(<form[\s\S]*?>)([\s\S]+?)(<\/form>)/', '$1' . $button_html . '<div style="display:none;">$2</div>' . '$3', $form);
                    }
                }
                return $button_html;
            },
            $content
        );
    }

    /**
     * Replace placeholder for product price
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _replacePrice($content) {
        return preg_replace_callback(
            '/<!--product_price-->([\s\S]+?)<!--\/product_price-->/',
            function ($priceHtml) {
                $product = $this->productData['product'];
                $priceHtml = $priceHtml[1];
                $price = $this->productData['price'];
                $price_old = $this->productData['price_old'];
                if ($product->get_regular_price() == $product->get_price()) {
                    $price_old = '';
                }
                $priceHtml = preg_replace('/<\!--product_old_price-->([\s\S]+?<div[\s\S]+?>)[\s\S]+?(<\/div>)<\!--\/product_old_price-->/', '$1 ' . $price_old . ' $2', $priceHtml, 2);
                return preg_replace('/<\!--product_regular_price-->([\s\S]+?<div[\s\S]+?>)[\s\S]+?(<\/div>)<\!--\/product_regular_price-->/', ('$1 ' . $price . ' $2'), $priceHtml);
            },
            $content
        );
    }

    /**
     * @param array $params
     *
     * @return array $products
     */
    public function getProducts($params) {
        global $products_control_query;
        $params = $this->_prepareSortingQuery($params);
        $params = $this->_prepareCategoriesFilterQuery($params);
        $products_control_query = NpAdminActions::getWpQuery($params);
        return isset($products_control_query->posts) ? $products_control_query->posts : array();
    }

    /**
     * Add to WP_QUERY $params sorting types
     *
     * @param array $params
     *
     * @return array $params
     */
    public function _prepareSortingQuery($params)
    {
        $sorting = isset($_GET['sorting']) ? sanitize_text_field($_GET['sorting']) : '';
        if ($sorting) {
            switch ($sorting) {
            case 'popularity':
                $params['meta_key'] = 'total_sales';
                $params['orderby'] = 'meta_value_num';
                break;
            case 'rating':
                $params['meta_key'] = '_wc_average_rating';
                $params['orderby'] = 'meta_value_num';
                $params['order'] = 'DESC';
                break;
            case 'date':
                $params['orderby'] = 'date';
                break;
            case 'price':
                $params['orderby'] = 'meta_value_num';
                $params['order'] = 'ASC';
                $params['meta_query'] = array(
                    'relation' => 'OR',
                    array(
                        'key' => '_price',
                        'compare' => 'NOT EXISTS',
                    ),
                    array(
                        'key' => '_price',
                        'value' => array(0, PHP_INT_MAX),
                        'compare' => 'BETWEEN',
                        'type' => 'NUMERIC'
                    ),
                );
                break;
            case 'price-desc':
                $params['orderby'] = 'meta_value_num';
                $params['order'] = 'DESC';
                $params['meta_query'] = array(
                    'relation' => 'OR',
                    array(
                        'key' => '_price',
                        'compare' => 'NOT EXISTS',
                    ),
                    array(
                        'key' => '_price',
                        'value' => array(0, PHP_INT_MAX),
                        'compare' => 'BETWEEN',
                        'type' => 'NUMERIC'
                    ),
                );
                break;
            default:
                $params['order'] = 'DESC';
                break;
            }
        }
        return $params;
    }

    /**
     * Add to WP_QUERY $params for categories filter
     *
     * @param array $params
     *
     * @return array $params
     */
    public function _prepareCategoriesFilterQuery($params)
    {
        $filter = isset($_GET['categoryId']) ? sanitize_text_field($_GET['categoryId']) : '';
        if ($filter) {
            switch ($filter) {
            case 'all':
                break;
            case 'featured':
                $tax_query[] = array(
                    array(
                        'taxonomy' => 'product_visibility',
                        'field'    => 'name',
                        'terms'    => 'featured',
                        'operator' => 'IN',
                    ),
                );
                $params['tax_query'] = $tax_query;
                break;
            default:
                $tax_query[] = array(
                    array(
                        'taxonomy' => 'product_cat',
                        'field'    => 'term_id',
                        'terms'    => $filter,
                    ),
                );
                $params['tax_query'] = $tax_query;
                break;
            }
        }
        return $params;
    }

    /**
     * Add product schema for seo
     *
     * @param string $content
     *
     * @return string $content
     */
    protected function _addProductSchema($content) {
        $product = $this->productData['product'];
        $meta = $this->productData['meta'] ?: get_post_meta($product->get_id());
        $stock = isset($meta['_stock_status'][0]) && $meta['_stock_status'][0] == 'instock' ? 'InStock' : 'OutOfStock';
        if ($product->get_regular_price() != null) {
            $price = $product->get_regular_price();
        } elseif ($product->get_price() != null) {
            $price = $product->get_price();
        }
        if (($price > $product->get_sale_price()) && ($product->get_sale_price() != null)) {
            $price = $product->get_sale_price();
        }
        $product_description = $this->productData['fullDesc'] != null ? $this->productData['fullDesc'] : '';
        $image = $this->productData['image_url'] ?: '';
        global $current_post_object;
        $product_json = array(
            "@context" => "http://schema.org",
            "@type" => "Product",
            "name" => get_the_title($this->productData['product']->ID),
            "image" => $image,
            "offers" => array(
                "@type" => "Offer",
                "priceCurrency" => get_woocommerce_currency(),
                "price" => $price,
                "itemCondition" => "http://schema.org/NewCondition",
                "availability" => "http://schema.org/" . $stock,
                "url" => get_permalink($current_post_object->ID),
                "description" => $product_description,
            ),
        );
        $content .= '<script type="application/ld+json">' . json_encode($product_json) . "</script>\n";
        return $content;
    }
}