<?php

namespace App\Console\Commands;

use App\Keyword;
use App\KeywordDetail;
use App\ProductBrand;
use App\ProductBrandDetail;
use App\ProductCategoryDetail;
use App\Seo;
use Symfony\Component\DomCrawler\Crawler;
use Illuminate\Console\Command;
use GuzzleHttp\Client;
use App\ProductCategory;
use App\ProductHighlight;
use App\ProductImage;
use App\Product;
use ImageCross;

class EtsyStore extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'etsy:store {store} {--page=}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Collection store item of Etsy EC site.'; //KidsLoveRockz JewelCrystalsLtd
    private $_client;
    private $_path;
    private $_img_path;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
        $this->_client = new Client();
        $this->_path = public_path("uploads/csv/");
        $this->_img_path = public_path("uploads/files/");
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //---------delete----------
        //Product::where('id', '>=', 4)->delete();
        //Product::where('id', '>=', 4)->update(['crawler_status' => 'pending']);
        //---------delete----------

        $shopName = str_replace(" ", "", trim($this->argument('store')));
        if ($shopName == null || $shopName == "") {
            $this->error("Shop name is invalid.");
            exit();
        }

        $pageLimit = $this->option('page');
        if (!isset($pageLimit) || $pageLimit == null || trim($pageLimit) == "" || !is_numeric($pageLimit) || ((integer)$pageLimit <= 0)) {
            $errMess = "Page is invalid.\r\n";
            $errMess .= "Please go to: https://www.etsy.com/jp/shop/{$shopName}?ref=items-pagination&sort_order=date_desc\r\n";
            $errMess .= "And get total page then \r\n Run: php artisan etsy:store {$shopName} --page={page} \r\n";
            $this->error($errMess);
            exit();
        }

        print "---------------------- Starting collection item url ---------------------\r\n";
        $baseUrl = "https://www.etsy.com/jp/shop/{$shopName}?ref=items-pagination&sort_order=date_desc";
        $page = 1;
        $allLink = [];
        $count = 0;
        $pageLimit = (integer)$pageLimit;

        do {
            $response = $this->_client->request('GET', $baseUrl . "&page={$page}");
            if ($response->getStatusCode() == 200) {
                $crawler = new Crawler();
                $crawler->addHtmlContent($response->getBody());
                $linkProduct = $crawler->filter('#content .wt-animated a.listing-link')->each(function (Crawler $node, $i) {
                    return $node->attr('href');
                });
                foreach ($linkProduct as $itemLink) {
                    $allLink[] = $itemLink;
                    $count += 1;
                    print "[Url] {$count}: {$itemLink}\r\n";
                }
                $page++;
            }
        } while ($page <= $pageLimit);

        $links = array_unique($allLink);
        foreach ($links as $item) {
            $urlIt = explode("?", $item);
            $url = isset($urlIt[0]) ? $urlIt[0] : $item;
            $productInfo = str_replace("https://www.etsy.com/jp/listing/", "", $url);
            $productInfo = str_replace("http://www.etsy.com/jp/listing/", "", $productInfo);
            $productInfo = explode("/", $productInfo);
            $productId = null;
            $productSlug = null;
            if (isset($productInfo[0])) $productId = $productInfo[0];
            if (isset($productInfo[1])) $productSlug = $productInfo[1];

            $checkProduct = Product::where('crawler_site', 'etsy')
                ->where('crawler_shop', $shopName)
                ->where('crawler_url', $url)
                ->where('crawler_sku', $productId)
                ->first();

            if (!isset($checkProduct)) {
                Product::create([
                    'name'           => $productSlug,
                    'slug'           => $productSlug,
                    'sku'            => $productId,
                    'price'          => 0,
                    'crawler_url'    => $url,
                    'crawler_sku'    => $productId,
                    'crawler_shop'   => $shopName,
                    'crawler_site'   => 'etsy',
                    'crawler_status' => 'pending',
                    'is_activated'   => 0,
                    'is_deleted'     => 1,
                    'created_at'     => date('Y-m-d H:i:s'),
                    'updated_at'     => date('Y-m-d H:i:s'),
                ]);
            }
        }

        print "---------------------- Collection item information ----------------------\r\n";
        $this->getItem($shopName, 'pending'); //pending|progress|done|error|updated
        print "-------------------------------------------------------------------------\r\n";

        $this->info("Done! Collected all item of {$shopName} store.");
    }

    private function getItem($shopName = '', $itemStatus = '')
    {
        $result = null;
        $product = Product::where('crawler_site', 'etsy')
            ->where('crawler_shop', $shopName)
            ->where('crawler_status', $itemStatus)
            ->orderBy('id')
            ->first();

        if (isset($product)) {
            try {
                print "[Crawler] Get item url: {$product->crawler_url}\r\n";
                $product->update(['crawler_status' => 'progress']);
                $result = $this->getItemDetail($product->crawler_url);
                if (isset($result) && count($result) > 0) {
                    //Category
                    $categoryStr = "";
                    $lastCateName = "";
                    if (isset($result['categories']) && count($result['categories']) > 0) {
                        $categoryId = null;
                        foreach ($result['categories'] as $cIt) {
                            if ($cIt == null || $cIt == "") continue;
                            $categoryName = trim($cIt);
                            $categoryStr .= ($categoryStr == "") ? $categoryName : "|" . $categoryStr;
                            $cate = ProductCategory::where('name', $categoryName)->first();
                            if (!isset($cate)) {
                                $cate = ProductCategory::create([
                                    'name'       => $categoryName,
                                    'slug'       => $categoryName,
                                    'parent_id'  => ($categoryId != null) ? $categoryId : 0,
                                    'created_at' => date('Y-m-d H:i:s'),
                                    'updated_at' => date('Y-m-d H:i:s'),
                                ]);
                            }
                            $categoryId = $cate->id;
                            $lastCateName = $categoryName;
                        }
                        ProductCategoryDetail::create([
                            'product_id'          => $product->id,
                            'product_category_id' => $categoryId,
                            'created_at'          => date('Y-m-d H:i:s'),
                            'updated_at'          => date('Y-m-d H:i:s'),
                        ]);
                    }

                    //Highlight
                    $highlightStr = "";
                    if (isset($result['highlights']) && count($result['highlights']) > 0) {
                        $hData = [];
                        foreach ($result['highlights'] as $hIt) {
                            $hData[] = [
                                'product_id' => $product->id,
                                'content'    => trim($hIt),
                                'created_at' => date('Y-m-d H:i:s'),
                                'updated_at' => date('Y-m-d H:i:s'),
                            ];
                            $highlightStr .= ($highlightStr == "") ? trim($hIt) : "|" . trim($hIt);
                        }
                        if (count($hData) > 0) ProductHighlight::insert($hData);
                    }

                    //Images
                    $imgStr = "";
                    $thumbnail = null;
                    if (isset($result['images']) && count($result['images']) > 0) {
                        $iData = [];
                        foreach ($result['images'] as $hIt) {
                            $imgUrl = $hIt['srcZoom'];
                            if ($imgUrl == null || $imgUrl == "") $imgUrl = $hIt['dataSrc'];
                            if ($imgUrl == null || $imgUrl == "") $imgUrl = $hIt['src'];
                            $imgStr .= ($imgStr == "") ? $imgUrl : "|" . $imgUrl;
                            $newImg = md5(date('YmdHis') . rand(1, 9999999999)) . ".jpg";
                            ImageCross::make(trim($imgUrl))->orientate()->save($this->_img_path . $newImg);
                            $iData[] = [
                                'product_id' => $product->id,
                                'file_name'  => $newImg,
                                'name'       => $newImg,
                                'created_at' => date('Y-m-d H:i:s'),
                                'updated_at' => date('Y-m-d H:i:s'),
                            ];
                            if ($thumbnail == null && $hIt['type'] == 'image') $thumbnail = $newImg;
                        }
                        if (count($iData) > 0) ProductImage::insert($iData);
                    }

                    //Product
                    $description = $result['description'];
                    $videos = null;
                    if ($description != "") {
                        $videoList = $this->getYoutubeLinks($description);
                        if (isset($videoList[0]) && count($videoList[0]) > 0) {
                            foreach ($videoList[0] as $vIt) {
                                $videos .= ($videos == null) ? $vIt : "|" . $vIt;
                            }
                        }
                    }

                    $preview = md5(date('YmdHis') . rand(1, 999999999999));
                    $shortDesc = $this->getShortDesc($description);
                    $ratingAvg = 0;
                    if (trim($result['rating']) !== null && trim($result['rating']) != "") {
                        $result['rating'] = (float)$result['rating'];
                        $ratingAvg = round($result['rating'], 2);
                    }

                    $currencyRate = 206;
                    $regularPrice = trim(str_replace(",", "", $result['price']));
                    if ($result['currency'] == "VND") {
                        $regularPrice = (float) $regularPrice;
                        $regularPrice = ceil($regularPrice/$currencyRate);
                    }

                    $skuKey = trim(preg_replace('/([A-Z])/', ' $1', trim($product->crawler_shop)));
                    $skuKey = str_replace(" ", "-", $skuKey);
                    $sku = strtolower($skuKey . "-" . $product->crawler_sku);

                    $updateData = [
                        'sku'                   => $sku,
                        'slug'                  => ($product->crawler_sku . "-" . $product->slug),
                        'name'                  => $result['name'],
                        'price'                 => $regularPrice,
                        'thumbnail'             => $thumbnail,
                        'short_description'     => $shortDesc,
                        'description'           => $description,
                        'information'           => $result['note'],
                        'in_stock'              => 1,
                        'rating_avg'            => $ratingAvg,
                        'preview'               => $preview,

                        'crawler_sales'         => $result['sales'],
                        'crawler_price'         => str_replace(",", "", $result['price']),
                        'crawler_rating'        => $result['rating'],
                        'crawler_availability'  => $result['availability'],
                        'crawler_videos'        => $videos,
                        'crawler_images'        => $imgStr,
                        'crawler_categories'    => $categoryStr,
                        'crawler_highlights'    => $highlightStr,
                        'crawler_reviews_shop'  => $result['reviewsShop'],
                        'crawler_reviews_item'  => $result['reviewsItem'],
                        'crawler_status'        => 'done',
                        'crawler_collection_at' => date('Y-m-d H:i:s'),

                        'publish_at'            => date('Y-m-d H:i:s'),
                        'is_activated'          => 1,
                        'is_deleted'            => 0,
                        'updated_at'            => date('Y-m-d H:i:s'),
                    ];
                    $product->update($updateData);

                    //Brand
                    $brandName = trim($product->crawler_shop);
                    if ($brandName !== "") {
                        $brand = ProductBrand::notDeleted()->where('name', $brandName)->first();
                        if (!isset($brand)) {
                            $brand = ProductBrand::create([
                                'name'       => $brandName,
                                'slug'       => $brandName,
                                'created_at' => date('Y-m-d H:i:s'),
                                'updated_at' => date('Y-m-d H:i:s')
                            ]);
                        }
                        ProductBrandDetail::create([
                            'product_id'       => $product->id,
                            'product_brand_id' => $brand->id,
                            'created_at'       => date('Y-m-d H:i:s'),
                            'updated_at'       => date('Y-m-d H:i:s')
                        ]);
                    }

                    //Seo Meta
                    Seo::create([
                        'uri'         => "/product/" . $product->slug,
                        'title'       => $product->name,
                        'keywords'    => '',
                        'description' => '',
                        'type'        => 'product',
                        'post_id'     => $product->id,
                        'created_at'  => date('Y-m-d H:i:s'),
                        'updated_at'  => date('Y-m-d H:i:s'),
                    ]);

                    //Tag
                    if ($lastCateName != "") {
                        $tag = trim($lastCateName);
                        $checkKeyword = Keyword::where('is_deleted', 0)
                            ->where(function ($query) use ($tag) {
                                $query->where('name', $tag)
                                    ->orWhere('name', strtolower($tag))
                                    ->orWhere('name', strtoupper($tag))
                                    ->orWhere('name', ucfirst($tag));
                            })->first();
                        if (!isset($checkKeyword) || $checkKeyword->id == null) {
                            $checkKeyword = Keyword::create([
                                'name'         => $tag,
                                'is_activated' => true,
                                'is_deleted'   => false,
                                'created_at'   => date('Y-m-d H:i:s'),
                                'updated_at'   => date('Y-m-d H:i:s'),
                            ]);
                        }
                        KeywordDetail::create([
                            'keyword_id' => $checkKeyword->id,
                            'ref_id'     => $product->id,
                            'type'       => 'product',
                            'created_at' => date('Y-m-d H:i:s'),
                            'updated_at' => date('Y-m-d H:i:s'),
                        ]);
                    }
                }
            } catch (\Exception $exception) {
                $product->update(['crawler_status' => 'error']);
                logger("[Crawler] Error: " . $exception->getMessage());
                print "[Error] Url: {$product->crawler_url}\r\n";
            } finally {
                unset($product);
                unset($result);
                $this->getItem($shopName, $itemStatus);
            }
        }
    }

    public function getItemDetail($url)
    {
        $rs = [];
        $response = $this->_client->request('GET', $url);
        if ($response->getStatusCode() == 200) {
            $rs = [
                'name'         => "",
                'brand'        => "",
                'currency'     => "VND",
                'price'        => 0,
                'sales'        => 0,
                'rating'       => 0,
                'description'  => "",
                'availability' => "",
                'note'         => "",
                'categories'   => [],
                'highlights'   => [],
                'images'       => [],
                'videos'       => null,
                'thumbnail'    => null,
                'reviewsShop'  => null,
                'reviewsItem'  => null,
            ];

            $crawler = new Crawler();
            $crawler->addHtmlContent($response->getBody());

            //name
            $name = $crawler->filter('#listing-page-cart h1');
            if ($name->count() > 0) {
                $name->children()->each(function (Crawler $child) {
                    $child->getNode(0)->parentNode->removeChild($child->getNode(0));
                });
                $rs['name'] = $name->first()->text();
                $rs['name'] = str_replace("Read the full title", "", $rs['name']);
            }

            //quantity
            $availability = $crawler->filter('#listing-page-cart p.wt-text-caption b');
            if ($availability->count() > 0) $rs['availability'] = $availability->first()->text();

            //note
            $note = $crawler->filter('#listing-page-cart > div > .wt-mt-xs-3 p.wt-text-caption');
            if ($note->count() > 0) $rs['note'] = $note->first()->text();

            //highlights
            $rs['highlights'] = $crawler->filter('#product-details-content-toggle ul li .wt-ml-xs-2')->each(function (Crawler $node, $i) {
                return trim($node->text());
            });

            //description
            $description = $crawler->filter('#wt-content-toggle-product-details-read-more > p');
            if ($description->count() > 0) $rs['description'] = $description->first()->text();

            //price
            $price = $crawler->filter('#listing-page-cart p.wt-text-title-03');
            if ($price->count() > 0) $rs['price'] = trim(str_replace("₫", "", $price->first()->text()));

            //sales
            $sales = $crawler->filter('#listing-page-cart .wt-mb-xs-1 .wt-display-inline-flex-xs .wt-text-caption');
            if ($sales->count() > 0) $rs['sales'] = trim(str_replace("sales", "", str_replace("販売数：", "", $sales->first()->text())));

            //rating
            $rating = $crawler->filter('#listing-page-cart input[name=rating]');
            if ($rating->count() > 0) $rs['rating'] = $rating->first()->attr('value');

            //reviews shop
            $reviews = $crawler->filter('#reviews .wt-align-items-center h3.wt-text-body-03');
            if ($reviews->count() > 0) $rs['reviewsShop'] = $reviews->first()->text();

            //reviews item
            $reviewsItem = $crawler->filter('#same-listing-reviews-tab span');
            if ($reviewsItem->count() > 0) $rs['reviewsItem'] = $reviewsItem->first()->text();

            //images
            $rs['images'] = $crawler->filter('div#listing-right-column div.image-col ul.carousel-pane-list li')->each(function (Crawler $node, $i) {
                $itData = [];
                $img = $node->filter('img')->first();
                if (isset($img)) {
                    $itData = [
                        'id'            => $node->attr('data-image-id'),
                        'type'          => 'image',
                        'alt'           => $img->attr('alt'),
                        'src'           => $img->attr('src'),
                        'srcSet'        => $img->attr('srcset'),
                        'dataSrc'       => $img->attr('data-src'),
                        'dataSrcSet'    => $img->attr('data-srcset'),
                        'originalWidth' => $img->attr('data-original-image-width'),
                        'srcZoom'       => $img->attr('data-src-zoom-image'),
                        'videoUrl'      => null,
                        'videoType'     => null
                    ];
                }

                if ($node->filter('video')->count() > 0) {
                    $video = $node->filter('video')->first();
                    $source = $video->filter('source')->first();
                    $vId = $video->attr('id');
                    $itData = [
                        'id'            => $vId,
                        'type'          => 'video',
                        'alt'           => null,
                        'src'           => null,
                        'srcSet'        => null,
                        'dataSrc'       => null,
                        'dataSrcSet'    => null,
                        'originalWidth' => null,
                        'srcZoom'       => null,
                        'videoUrl'      => $source->attr('src'),
                        'videoType'     => $source->attr('type')
                    ];
                }

                return $itData;
            });

            //categories
            $rs['categories'] = $crawler->filter('#wt-content-toggle-tags-read-more > ul li a')->each(function (Crawler $node, $i) {
                return trim($node->text());
            });

            unset($crawler);
        }

        unset($response);
        return $rs;
    }

    private function getYoutubeLinks($input)
    {
        preg_match_all('~https?://(?:[0-9A-Z-]+\.)?(?:youtu\.be/| youtube(?:-nocookie)?\.com\S*?[^\w\s-])([\w-]{11})(?=[^\w-]|$)(?![?=&+%\w.-]*(?:[\'"][^<>]*>|</a>))[?=&+%\w.-]*~ix', $input, $matches);
        return $matches;
    }

    private function getShortDesc($desc) {
        $descStr = "";
        $list = explode("\r\n", $desc);
        $count = 0;
        foreach ($list as $key => $item) {
            if (trim($item) != "") {
                $descStr .= trim($item) . "<br/>";
                $count++;
                if ($count > 3) break;
            }
        }
        return $descStr;
    }

    private function decodeStr($str = '') {
        $str = htmlspecialchars_decode($str);
        $str = html_entity_decode($str);
        $str = str_replace("'", '’', $str);
        $str = str_replace("&#39;", '’', $str);
        $str = str_replace("'", "’", $str);
        $str = str_replace('"', '“', $str);
        return trim($str);
    }
}
