<?php

namespace App\Services;

use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
use RuntimeException;

class BlocksService
{
    private static array $processors = [
        'slider' => [self::class, 'processSlider'],
        'categories_top' => [self::class, 'processCategoriesTop'],
        'products_feed' => [self::class, 'processProductsFeed'],
        'products_carousel' => [self::class, 'processProductsCarousel'],
    ];

    private ImageFactory $image;
    private CacheInterface $cache;
    private SettingsService $settings;
    private Builder $queryBuilder;
    private ProductsService $productsService;

    public function __construct(
        ImageFactory $image,
        CacheInterface $cache,
        SettingsService $settings,
        Builder $queryBuilder,
        ProductsService $productsService
    ) {
        $this->image = $image;
        $this->cache = $cache;
        $this->settings = $settings;
        $this->queryBuilder = $queryBuilder;
        $this->productsService = $productsService;
    }

    public function process(array $block): array
    {
        $blockType = $block['type'];
        $cacheKey = "block_{$blockType}_" . md5(serialize($block['data']));
        $cacheTtlSeconds = 3600;

        $data = $this->cache->get($cacheKey);
        if (! $data) {
            $method = self::$processors[$block['type']] ?? null;

            if (! $method) {
                throw new RuntimeException('Processor for block type ' . $block['type'] . ' does not exist');
            }

            $data = call_user_func_array($method, [$block]);

            $this->cache->set($cacheKey, $data, $cacheTtlSeconds);
        }

        return $data;
    }

    private function processSlider(array $block): array
    {
        $slides = $block['data']['slides'];
        foreach ($slides as $slideIndex => $slide) {
            if (is_file(DIR_IMAGE . $slide['image'])) {
                $image = $this->image->make($slide['image']);
                $block['data']['slides'][$slideIndex]['image'] = $image->cover(1110, 600)->url();
            }
        }

        return $block;
    }

    private function processCategoriesTop(array $block): array
    {
        $count = $block['data']['count'];
        $languageId = $this->settings->config()->getApp()->getLanguageId();
        $categories = [];

        if ($count > 0) {
            $categories = $this->queryBuilder->newQuery()
                ->select([
                    'categories.category_id' => 'id',
                    'descriptions.name' => 'name',
                ])
                ->from(db_table('category'), 'categories')
                ->join(
                    db_table('category_description') . ' AS descriptions',
                    function (JoinClause $join) use ($languageId) {
                        $join->on('categories.category_id', '=', 'descriptions.category_id')
                            ->where('descriptions.language_id', '=', $languageId);
                    }
                )
                ->where('categories.status', '=', 1)
                ->where('categories.parent_id', '=', 0)
                ->orderBy('sort_order')
                ->orderBy('descriptions.name')
                ->limit($count)
                ->get();

            $categories = array_map(static function ($category) {
                $category['id'] = (int) $category['id'];

                return $category;
            }, $categories);
        }

        $block['data']['categories'] = $categories;

        return $block;
    }

    private function processProductsFeed(array $block): array
    {
        return $block;
    }

    private function processProductsCarousel(array $block): array
    {
        $categoryId = $block['data']['category_id'];
        $languageId = $this->settings->config()->getApp()->getLanguageId();
        $params = [
            'page' => 1,
            'perPage' => 10,
            'filters' => [
                "operand" => "AND",
                "rules" => [
                    "RULE_PRODUCT_CATEGORIES" => [
                        "criteria" => [
                            "product_category_ids" => [
                                "type" => "product_categories",
                                "params" => [
                                    "operator" => "contains",
                                    "value" => [$categoryId],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ];
        $storeId = $this->settings->get('store.store_id', 0);

        $response = $this->productsService->getProductsResponse($params, $languageId, $storeId);

        $block['data']['products'] = $response;

        return $block;
    }
}
