<?php 
namespace Modules\Seo\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;

class SitemapController extends Controller {

    private $locales;
    private $languages;
    private $folder = '/sitemaps/';
    private $redirects = [];
    const SITEMAP_TXT_FILE = 'sitemap.txt';
    const CHUNK_SIZE = 50000;

    public function __construct() {
        $this->languages = config('seo::default.hreflangs');
        $parseApiUrl = parse_url(env('API_URL'));
        $parseApiUrl['host'] = str_replace('glob.', '', $parseApiUrl['host']);
        foreach (getModuleLocale() as $item) {
            $item['lang'] = key_exists($item['locale'], $this->languages) ? $this->languages[$item['locale']] : 'en';
            $item['locale_bak'] = $item['locale'];
            if ($item['locale'] == 'us') {
                $item['api_url'] = $parseApiUrl['scheme'] . '://' . $parseApiUrl['host'];
                $item['lang'] = 'x-default';
                $item['locale'] = '';
            } else {
                $item['api_url'] = $parseApiUrl['scheme'] . '://' . $item['locale'] . '.' . $parseApiUrl['host'];
            }
            if ($item['enable'] && !in_array($item['locale'], ['vn', 'kr'])) {
                $this->locales[] = $item;
            }
        }
        date_default_timezone_set('UTC');
    }

    public function _getRedirects() {
        foreach ($this->locales as $localeGroup) {
            $redirect = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/redirect', 'GET');
            if (!empty($redirect)) {
                $this->redirects = array_merge($this->redirects, $redirect);
            }
        }
    }
    
    public function index(Request $request) {
        ini_set("memory_limit", "-1");
        set_time_limit(0);
        $localeGroup = null;
        if ($request->has('locale')) {
            foreach ($this->locales as $item) {
                if ($item['locale_bak'] == $request->input('locale')) {
                    $localeGroup = $item;
                    break;
                }
            }
        }

        if (!in_array($request->input('action'), ['reset', 'summary']) && !$request->input('locale')) {
            $this->_getRedirects(); // không generate các link đã bị redirect
        }

        if ($request->input('action') == 'function') {
            $this->_functionSitemap();
        } else if ($request->input('action') == 'event') {
            $this->_eventSitemap();
        } else if ($request->input('action') == 'post') {
            $this->_postSitemap();
        } else if ($request->input('action') == 'news') {
            $this->_newsSitemap();
        } else if ($request->input('action') == 'landing-page') {
            $this->_landingPageSitemap([]);
        } else if ($request->input('action') == 'category') {
            $this->_categorySitemap();
        } else if ($request->input('action') == 'filter') {
            $this->_categoryFilterSitemap();
        } else if ($request->input('action') == 'tag-lasted') {
            $this->_tagLastedSitemap();
        } else if ($request->input('action') == 'tag') {
            if ($request->has('locale') && !empty($localeGroup)) {
                $this->_tagQueue($localeGroup);
            } else {
                $this->_tagSitemap($request->all());
            }
        } else if ($request->input('action') == 'product-lasted') {
            $this->_productLastedSitemap();
        } else if ($request->input('action') == 'product') {
            if ($request->has('locale') && !empty($localeGroup)) {
                $this->_productQueue($localeGroup);
            } else {
                $this->_productSitemap($request->all());
            }
        }

        if ($request->input('action') == 'summary') {
            $this->_summarySitemap();
        }

        return response()->json(['status' => 'successful']);
    }

    public function list(Request $request){
        $key = decorCacheKey('SITEMAP::DATA::' . env('APP_LOCALE'));
        $data = cacheGet($key);
        if (!$data || $request->has('clear_cache')){
            $productCategories = [];
            $postCategories = [];
            $categories = $this->triggerSyncRequest(env('API_URL') . '/sitemap/category?'.http_build_query(['types' => ['POST', 'PRODUCT']]), 'GET');
            foreach ($categories as $categoryItem) {
                if($categoryItem['type'] == 'PRODUCT'){
                    $breadcrumb = json_decode($categoryItem['breadcrumb']);
                    $categoryItem['url'] = '';
                    if (!empty($breadcrumb)) {
                        $categoryItem['url'] = $breadcrumb[count($breadcrumb) - 1]->url;
                    }
                    $productCategories[] = $categoryItem;
                }
                if($categoryItem['type'] == 'POST'){
                    $postCategories[] = $categoryItem;
                }
            }
            $productCategories = $this->buildTree($productCategories);
            $postCategories = $this->buildTree($postCategories);
            $tags = $this->triggerSyncRequest(env('API_URL') . '/sitemap/tags?'.http_build_query(['type' => 'lasted', 'limit' => 1000]), 'GET');
            $landingPages = $this->triggerSyncRequest(env('API_URL') . '/sitemap/landing-page', 'GET');
            $newProducts = $this->triggerSyncRequest(env('API_URL') . '/sitemap/products?' . http_build_query(['type' => 'lasted', 'limit' => 200]) , 'GET');
            $newPosts = $this->triggerSyncRequest(env('API_URL') . '/sitemap/post?'. http_build_query(['limit' => 200]), 'GET');
            $sellerFilter = [
                'orders' => ['created_at' => 'ASC'], 
                'total' => config('seo::default.rss.total_item_top_seller', 500)
            ];
            
            $data = [
                'productCategories' => $productCategories,
                'postCategories' => $postCategories,
                'tags' => $tags,
                'landingPages' => $landingPages,
                'newProducts' => $newProducts,
                'newPosts' => $newPosts
            ];
            Cache::put($key, $data, 86400);
        }

        return view('seo::sitemap.index', $data);
    }

    private function _summarySitemap() {
        $files = scandir(public_path($this->folder));
        if (!is_array($files) || count($files) < 2) {
            return response()->json(['status' => 'failed']);
        }
        $mainItems = [];
        for ($i = 2; $i < count($files); $i++) {
            if (!is_dir(public_path($this->folder . $files[$i]))) continue;
            
            $subFiles = scandir(public_path($this->folder . $files[$i]));
            if (!is_array($subFiles) || count($subFiles) < 2) continue;
            
            for ($j = 2; $j < count($subFiles); $j++) {
                $filePath = $this->folder . $files[$i] . '/' . $subFiles[$j];
                if (is_dir(public_path($filePath))) continue;
                
                if (!preg_match('/[0-9]/', $filePath)) {
                    array_unshift($mainItems, url($filePath));
                } else {
                    array_push($mainItems, url($filePath));
                }
            }
        }

        if (!empty($mainItems)) {
            $sitemapContent = view('seo::sitemap.summary', ['items' => $mainItems])->render();
            $file = fopen(public_path('sitemap-indexing.xml'), 'w');
            fwrite($file, $sitemapContent);
            fclose($file);
        }
    }

    private function _newsSitemap() {
        $items = [];
        $priority = 0.3;
        $lastmod = date('c');
        $changefreq = 'daily';

        foreach ($this->locales as $localeGroup) {
            $posts = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/post?' . http_build_query(['limit' => 50]), 'GET');
            $hreflangs = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/hreflang', 'GET');

            if (!empty($posts)) {
                foreach ($posts as $post) {
                    $itemLang = (key_exists($localeGroup['locale'], $this->languages)) ? $this->languages[$localeGroup['locale']] : 'en';
                    $postUrl = '/' . $post['slug'] . '-n' . $post['id'] . '.html';
                    $tempItem = [
                        'url' => url($localeGroup['locale'] . $postUrl), 
                        'name' => $post['name'], 
                        'image_url' => $post['image_url'], 
                        'created_at' => strtotime($post['created_at']),
                        'lang' => $itemLang,
                        'hreflangs' => []
                    ];
                    if (key_exists($postUrl, $hreflangs)) {
                        foreach ($hreflangs[$postUrl] as $locale => $url) {
                            array_push($tempItem['hreflangs'], [
                                'lang' => $this->languages[$locale],
                                'url' => ($locale == 'us') ? url($postUrl) : url($url)
                            ]);
                        }
                    } else {
                        array_push($tempItem['hreflangs'], [
                            'lang' => $itemLang,
                            'url' => url($localeGroup['locale'] . $postUrl)
                        ]);
                    }
                    array_push($items, $tempItem);
                }
            }
        }

        $sitemapContent = view('seo::sitemap.news', compact('items'))->render();
        $file = fopen(public_path('google-news.xml'), 'w');
        fwrite($file, $sitemapContent);
        fclose($file);
    }

    private function _postSitemap() {
        $items = [];

        foreach ($this->locales as $localeGroup) {
            $posts = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/post', 'GET');
            if (empty($posts)) {
                continue;
            }

            foreach ($posts as $post) {
                $postUrl = '/' . $post['slug'] . '-n' . $post['id'] . '.html';
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . $postUrl;
                } else {
                    $localeUrl = $postUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, [
                        'url' => url($localeUrl),
                        'created_at' => (!empty($post['created_at'])) ? strtotime($post['created_at']) : 1,
                    ]);
                }
            }
        
        }

        $this->_writeFile($items, '/sitemaps/post', 'post', true);
    }

    private function _landingPageSitemap() {
        $items = [];

        foreach ($this->locales as $localeGroup) {
            $pages = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/landing-page', 'GET');
            if (empty($pages)) {
                continue;
            }
            foreach ($pages as $page) {
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . '/page/' . $page['slug'];
                } else {
                    $localeUrl = '/page/' . $page['slug'];
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, [
                        'url' => url($localeUrl),
                        'created_at' => (!empty($page['created_at'])) ? strtotime($page['created_at']) : 1,
                    ]);
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/landing-page', 'landing-page', true);
    }

    private function _functionSitemap() {
        $items = [];
        $functions = [
            '/c',
            '/explore',
            '/shops',
            '/page',
            '/sitemap',
            '/create-your-own',
            '/contact-us',
            '/wholesale',
            '/seller/sell-your-product',
            '/track-order',
            '/refer',
            '/promo-code',
            '/contact/ticket',
            '/sizeguide',
            '/e-card'
        ];
        foreach ($this->locales as $localeGroup) {
            foreach ($functions as $function) {
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . $function;
                } else {
                    $localeUrl = $function;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, url($localeUrl));
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/function', 'function', false);
    }

    private function _eventSitemap() {
        $items = [];
        foreach ($this->locales as $localeGroup) {
            $events = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/event', 'GET');
            if (empty($events)) {
                continue;
            }

            foreach ($events as $event) {
                $eventUrl = '/' . $event['slug'];
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . $eventUrl;
                } else {
                    $localeUrl = $eventUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, [
                        'url' => url($localeUrl),
                        'created_at' => (!empty($event['created_at'])) ? strtotime($event['created_at']) : 1,
                    ]);
                }
            }
        }
       
        $this->_writeFile($items, '/sitemaps/event', 'event', true);
    }

    /** category */
    private function _categorySitemap() {
        $items = [];
        foreach ($this->locales as $localeGroup) {
            array_push($items, [
                'url' => url($localeGroup['locale_bak']),
                'created_at' => time(),
            ]);
        }
        foreach ($this->locales as $localeGroup) {
            $categories = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/category', 'GET');
            foreach ($categories as $category) {
                $categoryUrl = '/' . $category['slug'];
                if (!empty($category['breadcrumb'])) {
                    $breadcrumbs = json_decode($category['breadcrumb']);
                    if (is_array($breadcrumbs)) {
                        $categoryUrl = $breadcrumbs[ count($breadcrumbs) - 1 ]->url;
                    }
                }
                if (!key_exists($categoryUrl, $this->redirects)) {
                    array_push($items, [
                        'url' => url($categoryUrl),
                        'created_at' => (!empty($category['created_at'])) ? strtotime($category['created_at']) : 1,
                    ]);
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/category', 'category', true);
    }

    private function _categoryFilterSitemap() {
        $items = [];
        foreach ($this->locales as $localeGroup) {
            $categories = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/category', 'GET');
            foreach ($categories as $category) {
                $categoryUrl = '/' . $category['slug'];
                if (!empty($category['breadcrumb'])) {
                    $breadcrumbs = json_decode($category['breadcrumb']);
                    if (is_array($breadcrumbs)) {
                        $categoryUrl = $breadcrumbs[ count($breadcrumbs) - 1 ]->url;
                    }
                }

                $types = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/category-filter?' . http_build_query(['category_id' => $category['id'], 'variant_id' => 5]), 'GET');
                
                foreach ($types as $type) {
                    $categoryTypeUrl = $categoryUrl . '/' . $type['slug'];
                    if (!key_exists($categoryTypeUrl, $this->redirects)) {
                        array_push($items, url($categoryTypeUrl));
                    }
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/filter', 'filter', false);
    }

    /** tags */
    private function _tagLastedSitemap() {
        $items = [];
        $limit = intval(self::CHUNK_SIZE / count($this->locales));
            
        foreach ($this->locales as $index => $localeGroup) {
            $tags = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/tags?' . http_build_query(['type' => 'lasted', 'limit' => $limit]) , 'GET');
            if (empty($tags)) continue;
            
            foreach ($tags as $tag) {
                $tagUrl = '/explore/' . $tag['slug'];
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . $tagUrl;
                } else {
                    $localeUrl = $tagUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, url($localeUrl));
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/tags', 'tags', false);
    }

    private function _tagQueue($localeGroup) {
        $limit = intval(self::CHUNK_SIZE / count($this->locales));
        $fromId = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/tags?' . http_build_query(['type' => 'summary', 'limit' => $limit]) , 'GET');
        $toId = 1;
        $indexFile = 1;
        while ($fromId > $toId) {
            $newToId = $fromId - self::CHUNK_SIZE;
            if ($newToId < $toId) $newToId = $toId;

            $fileName = (!empty($localeGroup['locale'])) ? ('tags-' . $localeGroup['locale'] . '-' . $indexFile) : ('tags-' . $indexFile);
            $endpoint = str_replace(env('APP_URL'), config('restq.origin'), route('build-sitemaps')). '?' . http_build_query([
                'action' => 'tag',
                'api_url' => $localeGroup['api_url'] . "/sitemap/tags",
                'from' => $newToId,
                'to' => $fromId,
                'code' => $localeGroup['locale'],
                'file_name' => $fileName
            ]);
            $this->triggerSyncRequest($endpoint, 'GET');

            $fromId = $newToId - 1;
            $indexFile++;
        }
    }

    private function _tagSitemap($input) {
        if (!empty($input['api_url']) && !empty($input['file_name'])) {
            $items = [];
            $tags = $this->triggerSyncRequest($input['api_url'] . '?' . http_build_query([ 'from' => $input['from'], 'to' => $input['to'] ]), 'GET');
            if (empty($tags)) return;

            foreach ($tags as $tag) {
                $tagUrl = '/explore/' . $tag['slug'];
                if (!empty($input['code'])) {
                    $localeUrl = '/' . $input['code'] . $tagUrl;
                } else {
                    $localeUrl = $tagUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, url($localeUrl));
                }
            }

            $this->_writeFile($items, '/sitemaps/tags', $input['file_name'], false);
        }
    }

    /** products */
    private function _productLastedSitemap() {
        $items = [];
        foreach ($this->locales as $index => $localeGroup) {
            $products = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/product?' . http_build_query(['type' => 'today']) , 'GET');
            if (empty($products)) continue;
            
            foreach ($products as $product) {
                $productUrl = '/' . $product['slug'] . '-p' . $product['id'];
                if (!empty($localeGroup['locale'])) {
                    $localeUrl = '/' . $localeGroup['locale'] . $productUrl;
                } else {
                    $localeUrl = $productUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, [
                        'url' => url($localeUrl),
                        'created_at' => (!empty($product['created_at'])) ? strtotime($product['created_at']) : 1,
                    ]);
                }
            }
        }

        $this->_writeFile($items, '/sitemaps/product', 'product', true);
    }

    private function _productQueue($localeGroup) {
        $limit = intval(self::CHUNK_SIZE / count($this->locales));
        $fromId = $this->triggerSyncRequest($localeGroup['api_url'] . '/sitemap/product?' . http_build_query(['type' => 'summary', 'limit' => $limit]) , 'GET');
        $toId = 1;
        $indexFile = 1;
        while ($fromId > $toId) {
            $newToId = $fromId - self::CHUNK_SIZE;
            if ($newToId < $toId) $newToId = $toId;

            $fileName = (!empty($localeGroup['locale'])) ? ('product-' . $localeGroup['locale'] . '-' . $indexFile) : ('product-' . $indexFile);
            $endpoint = str_replace(env('APP_URL'), config('restq.origin'), route('build-sitemaps')). '?' . http_build_query([
                'action' => 'product',
                'api_url' => $localeGroup['api_url'] . "/sitemap/product",
                'from' => $newToId,
                'to' => $fromId,
                'code' => $localeGroup['locale'],
                'file_name' => $fileName 
            ]);
            $this->triggerSyncRequest($endpoint, 'GET');

            $fromId = $newToId - 1;
            $indexFile++;
        }
    }

    private function _productSitemap($input) {
        if (!empty($input['api_url']) && !empty($input['file_name'])) {
            $items = [];
            $products = $this->triggerSyncRequest($input['api_url'] . '?' . http_build_query([ 'from' => $input['from'], 'to' => $input['to'] ]), 'GET');
            if (empty($products)) return;

            foreach ($products as $product) {
                $productUrl = '/' . $product['slug'] . '-p' . $product['id'];
                if (!empty($input['code'])) {
                    $localeUrl = '/' . $input['code'] . $productUrl;
                } else {
                    $localeUrl = $productUrl;
                }

                if (!key_exists($localeUrl, $this->redirects)) {
                    array_push($items, url($localeUrl));
                }
            }

            $this->_writeFile($items, '/sitemaps/product-locale', $input['file_name'], false);
        }
    }

    private function _writeFile($items, $saveToFolder, $fileName, $isSort = false, $index = 1) {
        if (empty($items)) return;

        if (!File::exists(public_path($saveToFolder))) {
            File::makeDirectory(public_path($saveToFolder), 0755, true, true);
        }
        if ($isSort) {
            usort($items, array($this, 'sortCreatedAt'));
        }

        for ($i = 0; $i < count($items); $i += self::CHUNK_SIZE) {
            $newItems = [];
            for ($j = $i; $j < $i + self::CHUNK_SIZE && $j < count($items); $j++) {
                array_push($newItems, (is_array($items[$j])) ? $items[$j]['url'] : $items[$j]);
            }

            if (empty($newItems)) continue;
            
            if ($index == 1) {
                $file = fopen(public_path($saveToFolder . '/' . $fileName . '.txt'), 'w');
            } else {
                $file = fopen(public_path($saveToFolder . '/' . ($fileName . '-' . $index) . '.txt'), 'w');
            }
            fwrite($file, implode(PHP_EOL, $newItems));
            fclose($file);

            $index++;
        }
        
    }

    public function sortCreatedAt($a, $b) {
        return $a["created_at"] < $b["created_at"];
    }

    private function buildTree($flatArray, $parentId = null) {
        $tree = [];
    
        foreach ($flatArray as $item) {
            if ($item['parent_id'] == $parentId) {
                $children = $this->buildTree($flatArray, $item['id']);
                if (!empty($children)) {
                    $item['childs'] = $children;
                }
                $tree[] = $item;
            }
        }

        return $tree;
    }
}
