<?php

namespace App\Services\Main;

use App\Models\Article;
use App\Models\Brand;
use App\Models\Product;
use App\Models\ProductCategory;
use App\Models\Setting;
use App\Models\Story;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class MainService
{
    /** seo meta data */
    public array $seo = [
        'schema' => null,
        'site_name' => null,
        'title' => null,
        'store_name' => null,
        'description' => null,
        'keywords' => null,
        'canonical' => null,
        'author' => null,
        'image' => null,
        'type' => null,
    ];

    /** get stories */
    public function getStories()
    {
        // stories status
        $storiesStatus = Cache::remember('setting_stories_status', now()->addMonth(), function () {
            $storiesStatus = Setting::where('key', 'stories_status')->first();
            $storiesStatus = $storiesStatus != null ? $storiesStatus->value : false;
            $storiesStatus = $storiesStatus == "1" ? true : false;
            return $storiesStatus;
        });

        // stories
        $stories = Story::where('status', 'publish')->whereDate('expired_at', '>=', Carbon::now())->orderBy('created_at', 'desc')->get()->map(fn($story) => [
            'id' => $story->id,
            'type' => $story->type,
            'title' => $story->title,
            'cover' => $story->cover,
            'file' => $story->file,
            'get_widgets' => $story->get_widgets,
            'like' => $story->like,
            'is_liked' => $story->is_liked,
            'jalali_created_at' => $story->jalali_created_at,
        ]);

        return [
            'status' => $storiesStatus,
            'data' => $stories
        ];
    }

    /** get components with data */
    public function getComponents($template)
    {
        if ($template != null) {
            $components = $template->get_data['index'];

            foreach ($template->get_data['index'] as $key => $component) {
                if (array_key_exists("category_list", $component["data"])) {
                    @$components[$key]["data"]["category_list"]["categories"] = $this->getCategoryLists($component)[$component["data"]["category_list"]["id"]];
                }
                if (array_key_exists("category_products", $component["data"])) {
                    @$components[$key]["data"]["category_products"]["category"] = $this->getCategoryProducts($component)['category'];
                    @$components[$key]["data"]["category_products"]["products"] = $this->getCategoryProducts($component)['products'];
                }
                if (in_array("categories_tabs", $component["type"])) {
                    @$components[$key]["data"]["categories"] = $this->getCategoriesTabsProducts($component);
                }
                if (array_key_exists("product_list", $component["data"])) {
                    @$components[$key]["data"]["product_list"]["products"] = $this->getProductLists($component);
                }
                if (array_key_exists("brand_list", $component["data"])) {
                    @$components[$key]["data"]["brand_list"]["brands"] = $this->getBrands();
                }
                if (array_key_exists("article_list", $component["data"])) {
                    @$components[$key]["data"]["article_list"]["articles"] = $this->getArticleLists()[$component["data"]["article_list"]["id"]];
                }
            }

            return $components;
        } else {
            return [];
        }
    }

    /** get product category lists */
    public function getCategoryLists($component)
    {
        $categoryList = $component["data"]["category_list"];
        $withImage = $component["data"]["style"]["with_image"] ?? false;

        if ($categoryList['id'] == 'simple_list') {
            if (array_key_exists('category', $categoryList)) {
                $query = ProductCategory::where('status', 'publish');

                if ($withImage === true) {
                    $query->whereNotNull('image');
                }

                if ($categoryList['category']['id'] == 'all') {
                    $categories['simple_list'] = $query->inRandomOrder()
                        ->limit(12)
                        ->get()
                        ->map(fn($category) => [
                            'id' => $category->id,
                            'title' => $category->title,
                            'slug' => $category->slug,
                            'get_image' => $category->get_image,
                        ]);
                } else if ($categoryList['category']['id'] == 'main') {
                    $categories['simple_list'] = $query->where('parent_id', 0)
                        ->get()
                        ->map(fn($category) => [
                            'id' => $category->id,
                            'title' => $category->title,
                            'slug' => $category->slug,
                            'get_image' => $category->get_image,
                        ]);
                } else if (is_int($categoryList['category']['id'])) {
                    $categories['simple_list'] = $query->where('parent_id', $categoryList['category']['id'])
                        ->get()
                        ->map(fn($category) => [
                            'id' => $category->id,
                            'title' => $category->title,
                            'slug' => $category->slug,
                            'get_image' => $category->get_image,
                        ]);
                }
            }
        }

        $categories['with_products'] = ProductCategory::has('products', '>=', 4)->where('status', 'publish')->inRandomOrder()->limit(5)->get();
        $categories['with_products'] = $categories['with_products']->map(fn($category) => [
            'id' => $category->id,
            'title' => $category->title,
            'slug' => $category->slug,
            'products' => $category->products()->where('status', 'publish')->inRandomOrder()->limit(4)->get()->map(fn($product) => [
                'id' => $product->id,
                'title' => $product->title,
                'slug' => $product->slug,
                'get_images' => $product->get_images,
                'comments_info' => $product->comments_info,
                'in_stock_status' => $product->in_stock_status,
                'inventory' => $product->inventory,
                'best_normal_price' => $product->best_normal_price,
                'unique_color' => $product->unique_color,
                'best_price' => $product->best_price,
                'has_discount' => $product->has_discount,
                'is_vip' => $product->is_vip,
            ]),
        ]);

        return $categories;
    }

    /** get categories tabs products lists */
    public function getCategoriesTabsProducts($component)
    {
        $now = Carbon::now(new \DateTimeZone('Asia/Tehran'))->toDateTimeString();
        $filter = $component['data']['filter']['id'];

        $categoriesWithProduct = [];
        foreach ($component['data']['categories'] as $category) {
            $tempCategoryData = $category;
            $tempCategoryData['products'] = Product::where('status', 'publish')->where('category_id', $category['id']);

            // order by
            switch ($filter) {
                case "latest_products":
                    $tempCategoryData['products']->orderBy('created_at', 'desc');
                    break;
                case "instant_offers":
                    $tempCategoryData['products']->inRandomOrder();
                    break;
                case "vip_products":
                    $tempCategoryData['products']->where('is_vip', true)->inRandomOrder();
                    break;
                case "discounted_products":
                    $tempCategoryData['products']->whereHas('inventories', function ($query) use ($now) {
                        $query->where('status', 'publish')->where('count', '>', 0)->where('discount_price', '!=', null)->where(function ($q) use ($now) {
                            $q->where('discount_expire', '>=', $now)
                                ->orWhereNull('discount_expire');
                        });
                    })->inRandomOrder();
                    break;
                case "popular_products":
                    $tempCategoryData['products']->withCount(['productComments as comment_rating_average' => function ($query) {
                        $query->select(DB::raw('avg(rating)'));
                    }])->orderBy('comment_rating_average', 'desc');
                    break;
                case "most_viewed_products":
                    $tempCategoryData['products']->orderBy('view_count', 'desc');
                    break;
                case "most_sales_products":
                    $tempCategoryData['products']->withCount(['consignmentItems' => function ($query) {
                        $query->where(function ($query) {
                            $query->where('status', 'sent')->orWhere('status', 'delivered');
                        })->select(DB::raw('SUM(count)'));
                    }])->orderBy('consignment_items_count', 'desc');
                    break;
                default:
                    break;
            }

            $tempCategoryData['products'] = $tempCategoryData['products']->limit(6)->get()->map(fn($product) => [
                'id' => $product->id,
                'title' => $product->title,
                'slug' => $product->slug,
                'get_images' => $product->get_images,
                'comments_info' => $product->comments_info,
                'in_stock_status' => $product->in_stock_status,
                'inventory' => $product->inventory,
                'best_normal_price' => $product->best_normal_price,
                'unique_color' => $product->unique_color,
                'best_price' => $product->best_price,
                'has_discount' => $product->has_discount,
                'is_vip' => $product->is_vip,
            ]);

            $categoriesWithProduct[] = $tempCategoryData;
        }

        return $categoriesWithProduct;
    }

    /** get category with products lists */
    public function getCategoryProducts($component)
    {
        // category information
        $category = Cache::remember('category_for_products_list_' . $component['data']['category_products']['category']['id'], now()->addMonth(), function () use ($component) {
            return ProductCategory::find($component['data']['category_products']['category']['id']);
        });
        $category = [
            'id' => $category->id,
            'title' => $category->title,
            'get_image' => $category->get_image,
            'seo_description' => $category->seo_description,
        ];

        // category products
        $products = [];
        if ($category != null) {
            $products = Cache::remember('category_products_list_' . $category['id'], now()->addMonth(), function () use ($category) {
                $itemCount = 9;

                return Product::where('status', 'publish')
                    ->where('category_id', $category['id'])
                    ->orderBy('created_at', 'desc')
                    ->take($itemCount)
                    ->get()
                    ->map(fn($product) => [
                        'id' => $product->id,
                        'title' => $product->title,
                        'slug' => $product->slug,
                        'get_images' => $product->get_images,
                        'comments_info' => $product->comments_info,
                        'in_stock_status' => $product->in_stock_status,
                        'inventory' => $product->inventory,
                        'best_normal_price' => $product->best_normal_price,
                        'unique_color' => $product->unique_color,
                        'best_price' => $product->best_price,
                        'has_discount' => $product->has_discount,
                        'is_vip' => $product->is_vip,
                    ]);
            });
        }

        return [
            'category' => $category,
            'products' => $products,
        ];
    }

    /** get brand list */
    public function getBrands()
    {
        $brands = Cache::remember('brands', now()->addMonth(), function () {
            return Brand::where('status', 'publish')->inRandomOrder()->limit(12)->get()->map(fn($brand) => [
                'id' => $brand->id,
                'title' => $brand->title,
                'slug' => $brand->slug,
                'get_logo' => $brand->get_logo,
            ]);
        });

        return $brands;
    }

    /** get product lists */
    public function getProductLists($component)
    {
        $now = Carbon::now(new \DateTimeZone('Asia/Tehran'))->toDateTimeString();
        $finalProduct = [];

        // custom products list
        $products['latest_products'] = Product::where('status', 'publish')->orderBy('created_at', 'desc');
        $products['instant_offers'] = Product::where('status', 'publish')->inRandomOrder();
        $products['vip_products'] = Product::where('status', 'publish')->where('is_vip', true)->inRandomOrder();
        $products['discounted_products'] = Product::where('status', 'publish')->whereHas('inventories', function ($query) use ($now) {
            $query->where('status', 'publish')->where('count', '>', 0)->where('discount_price', '!=', null)->where(function ($q) use ($now) {
                $q->where('discount_expire', '>=', $now)
                    ->orWhereNull('discount_expire');
            });
        })->inRandomOrder();
        $products['popular_products'] = Product::where('status', 'publish')->withCount(['productComments as comment_rating_average' => function ($query) {
            $query->select(DB::raw('avg(rating)'));
        }])->orderBy('comment_rating_average', 'desc');
        $products['most_viewed_products'] = Product::where('status', 'publish')->orderBy('view_count', 'desc');
        $products['most_sales_products'] = Product::where('status', 'publish')->withCount(['consignmentItems' => function ($query) {
            $query->where(function ($query) {
                $query->where('status', 'sent')->orWhere('status', 'delivered');
            })->select(DB::raw('SUM(count)'));
        }])->orderBy('consignment_items_count', 'desc');

        // add product data to template components
        if (in_array('product_list', $component['type'])) {
            if (count($component['data']['product_list']) > 0) {
                $itemCount = 9;
                if ($component['component'] == 'ProductList2')
                    $itemCount = 12;
                else if ($component['component'] == 'ProductList3')
                    $itemCount = 8;
                else if ($component['component'] == 'ProductList5')
                    $itemCount = 10;
                else if ($component['component'] == 'SliderType3')
                    $itemCount = 5;

                if (is_int($component['data']['product_list']['id'])) {
                    $category = ProductCategory::find($component['data']['product_list']['id']);

                    if ($category == null) {
                        return [];
                    }

                    $ids = [$category->id, ...$category->getAllChildrenIds()];

                    // محصولات دارای موجودی (count > 0)
                    $inStockProducts = Product::where('status', 'publish')
                        ->whereIn('category_id', $ids)
                        ->whereHas('inventories', function ($q) {
                            $q->where('count', '>', 0);
                        })
                        ->inRandomOrder()
                        ->take($itemCount)
                        ->get();

                    // اگر تعداد محصولات موجودی کافی بود، کوئری دوم اجرا نشه
                    if ($inStockProducts->count() >= $itemCount) {
                        $finalProduct = $inStockProducts;
                    } else {
                        // تعداد باقی‌مانده برای پر کردن لیست
                        $remaining = $itemCount - $inStockProducts->count();

                        // محصولات بدون موجودی یا با count = 0
                        $outOfStockProducts = Product::where('status', 'publish')
                            ->whereIn('category_id', $ids)
                            ->where(function ($q) {
                                $q->doesntHave('inventories')
                                    ->orWhereHas('inventories', function ($q2) {
                                        $q2->where('count', '<=', 0);
                                    });
                            })
                            ->inRandomOrder()
                            ->take($remaining)
                            ->get();

                        // ترکیب نهایی
                        $finalProduct = $inStockProducts->concat($outOfStockProducts);
                    }

                    // تبدیل خروجی نهایی به آرایه مورد نیاز
                    $finalProduct = $finalProduct->map(fn($product) => [
                        'id' => $product->id,
                        'title' => $product->title,
                        'slug' => $product->slug,
                        'get_images' => $product->get_images,
                        'comments_info' => $product->comments_info,
                        'in_stock_status' => $product->in_stock_status,
                        'inventory' => $product->inventory,
                        'best_normal_price' => $product->best_normal_price,
                        'unique_color' => $product->unique_color,
                        'best_price' => $product->best_price,
                        'has_discount' => $product->has_discount,
                        'is_vip' => $product->is_vip,
                        'brand' => $product->get_simple_brand?->title ?? '',
                    ]);
                } else {
                    $baseQuery = $products[$component['data']['product_list']['id']];

                    // محصولات دارای موجودی (count > 0)
                    $inStockProducts = (clone $baseQuery)
                        ->whereHas('inventories', function ($q) {
                            $q->where('count', '>', 0);
                        })
                        ->inRandomOrder()
                        ->take($itemCount)
                        ->get();

                    // اگر محصولات دارای موجودی کافی بودند، نیازی به کوئری دوم نیست
                    if ($inStockProducts->count() >= $itemCount) {
                        $finalProduct = $inStockProducts;
                    } else {
                        // در غیر این صورت، محصولات بدون موجودی هم بیار برای پر کردن باقی‌مانده
                        $remaining = $itemCount - $inStockProducts->count();

                        $outOfStockProducts = (clone $baseQuery)
                            ->where(function ($q) {
                                $q->doesntHave('inventories')
                                    ->orWhereHas('inventories', function ($q2) {
                                        $q2->where('count', '<=', 0);
                                    });
                            })
                            ->inRandomOrder()
                            ->take($remaining)
                            ->get();

                        $finalProduct = $inStockProducts->concat($outOfStockProducts);
                    }

                    // خروجی نهایی با map
                    $finalProduct = $finalProduct->map(fn($product) => [
                        'id' => $product->id,
                        'title' => $product->title,
                        'slug' => $product->slug,
                        'get_images' => $product->get_images,
                        'comments_info' => $product->comments_info,
                        'in_stock_status' => $product->in_stock_status,
                        'inventory' => $product->inventory,
                        'best_normal_price' => $product->best_normal_price,
                        'unique_color' => $product->unique_color,
                        'best_price' => $product->best_price,
                        'has_discount' => $product->has_discount,
                        'is_vip' => $product->is_vip,
                        'brand' => $product->get_simple_brand?->title ?? '',
                    ]);
                }
            }
        }

        return $finalProduct;
    }

    /** get article lists */
    public function getArticleLists()
    {
        $articles['latest_articles'] = Article::where('status', 'publish')->orderBy('created_at', 'desc')->take(12)->get()->map(fn($article) => [
            'id' => $article->id,
            'title' => $article->title,
            'slug' => $article->slug,
            'get_image' => $article->get_image,
            'jalali_created_at' => $article->jalali_created_at,
            'user' => $article->get_user,
            'category' => $article->get_category,
        ]);

        $articles['random_articles'] = Article::where('status', 'publish')->inRandomOrder()->limit(12)->get()->map(fn($article) => [
            'id' => $article->id,
            'title' => $article->title,
            'slug' => $article->slug,
            'get_image' => $article->get_image,
            'jalali_created_at' => $article->jalali_created_at,
            'user' => $article->get_user,
            'category' => $article->get_category,
        ]);

        return $articles;
    }

    /** get search product category result */
    public function getSearchProductCategory(Request $request)
    {
        $categories = ProductCategory::query()->limit(10)->when($request->input('keyword'), function ($query, $keyword) {
            $query->where('title', 'like', "%{$keyword}%");
        })->where('status', 'publish')->get()->map(fn($category) => [
            'id' => $category->id,
            'title' => $category->title,
            'slug' => $category->slug,
        ]);

        return $categories;
    }

    /** get search product result */
    public function getSearchProduct(Request $request)
    {
        $products = Product::query()->limit(6)->when($request->input('keyword'), function ($query, $keyword) {
            $query->where(DB::raw('concat(`title`, `uuid`)'), 'like', "%{$keyword}%");
        })->where('status', 'publish')->get()->map(fn($product) => [
            'id' => $product->id,
            'title' => $product->title,
            'slug' => $product->slug,
            'get_images' => $product->get_images,
            'in_stock_status' => $product->in_stock_status,
            'inventory' => $product->inventory,
            'best_price' => $product->best_price,
        ]);

        return $products;
    }

    /** seo information */
    public function seo()
    {
        $shopFullTitle = Cache::remember('setting_shop_full_title', now()->addMonth(), function () {
            $shopFullTitle = Setting::where('key', 'shop_full_title')->first();
            return $shopFullTitle != null ? $shopFullTitle->value : null;
        });
        $shopDescription = Cache::remember('setting_shop_description', now()->addMonth(), function () {
            $shopDescription = Setting::where('key', 'shop_description')->first();
            return $shopDescription != null ? $shopDescription->value : null;
        });
        $favicon = Cache::remember('setting_favicon', now()->addMonth(), function () {
            $favicon = Setting::where('key', 'favicon')->first();
            return $favicon != null ? $favicon->get_image : null;
        });


        // schema
        $this->seo['schema'] = $this->indexSchema($shopFullTitle);

        // main
        $this->seo['site_name'] = $shopFullTitle;
        $this->seo['title'] = $shopFullTitle;
        $this->seo['description'] = $shopDescription;
        $this->seo['image'] = $favicon;

        // return information
        return $this->seo;
    }

    /** Product Schema */
    public function indexSchema($shopFullTitle)
    {
        $shopShortTitle = Cache::remember('setting_shop_title', now()->addMonth(), function () {
            $shopShortTitle = Setting::where('key', 'shop_title')->first();
            return $shopShortTitle != null ? $shopShortTitle->value : null;
        });

        $shopLogo = Cache::remember('setting_logo', now()->addMonth(), function () {
            $shopLogo = Setting::where('key', 'logo')->first();
            return $shopLogo != null ? $shopLogo['get_image'] : null;
        });

        $email = Cache::remember('setting_support_email', now()->addMonth(), function () {
            $email = Setting::where('key', 'support_email')->first();
            return $email != null ? $email->value : null;
        });

        $phone = Cache::remember('setting_support_phone', now()->addMonth(), function () {
            $phone = Setting::where('key', 'support_phone')->first();
            return $phone != null ? $phone->value : null;
        });

        $address = Cache::remember('setting_shop_address', now()->addMonth(), function () {
            $address = Setting::where('key', 'shop_address')->first();
            return $address != null ? $address->value : null;
        });

        $addressState = Cache::remember('setting_shop_address_town', now()->addMonth(), function () {
            $addressState = Setting::where('key', 'shop_address_town')->first();
            return $addressState != null ? $addressState->value : null;
        });

        $addressCity = Cache::remember('setting_shop_address_city', now()->addMonth(), function () {
            $addressCity = Setting::where('key', 'shop_address_city')->first();
            return $addressCity != null ? $addressCity->value : null;
        });

        $postalCode = Cache::remember('setting_shop_address_postal_code', now()->addMonth(), function () {
            $postalCode = Setting::where('key', 'shop_address_postal_code')->first();
            return $postalCode != null ? $postalCode->value : null;
        });

        $geoLocation = Cache::remember('setting_shop_map_location', now()->addMonth(), function () {
            $geoLocation = Setting::where('key', 'shop_map_location')->first();
            return $geoLocation != null ? unserialize($geoLocation->value) : null;
        });


        $mainCategories = [];
        $categories = Cache::remember('main_schema_product_categories', now()->addMonth(), function () {
            return ProductCategory::where('status', 'publish')->where('parent_id', 0)->orderBy('created_at', 'desc')->take(20)->get();
        });
        foreach ($categories as $key => $category) {
            $mainCategories[] = [
                "@type" => "ListItem",
                "position" => $key + 1,
                "name" => $category->title,
                "url" => urldecode(route('main.products.index', ['cat' => $category->slug]))
            ];
        }

        $schema = [
            [
                "@context" => "https://schema.org",
                "@type" => "Organization",
                "id" => asset("/#Organization"),
                "name" => $shopShortTitle,
                "alternateName" => $shopFullTitle,
                "url" => route('index'),
                "logo" => $shopLogo,
                "email" => $email,
                "contactPoint" => [
                    "@type" => "ContactPoint",
                    "telephone" => $phone,
                    "contactType" => "customer service",
                    "areaServed" => "IR",
                    "availableLanguage" => "fa"
                ],
                "address" => [
                    "@type" => "PostalAddress",
                    "streetAddress" => $address,
                    "addressLocality" => $addressCity,
                    "addressRegion" => $addressState,
                    "postalCode" => $postalCode,
                    "addressCountry" => "IR",
                    "geo" => $geoLocation != null ? [
                        "@type" => "GeoCoordinates",
                        "latitude" => $geoLocation["latitude"],
                        "longitude" => $geoLocation["longitude"]
                    ] : null,
                ]
            ],
            [
                "@context" => "https://schema.org",
                "@type" => "WebSite",
                "name" => $shopShortTitle,
                "url" => route('index'),
                "copyrightHolder" => [
                    "@type" => "Organization",
                    "name" => $shopShortTitle
                ],
                "creator" => [
                    "@type" => "Organization",
                    "name" => $shopShortTitle,
                ],
            ],
            [
                "@context" => "https://schema.org",
                "@type" => "ItemList",
                "url" => route('index'),
                "itemListElement" => $mainCategories
            ]
        ];

        return json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
}
