<?php

declare(strict_types=1);

namespace App\Handlers;

use App\Services\SettingsService;
use App\Support\Utils;
use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
use Openguru\OpenCartFramework\QueryBuilder\Table;
use Openguru\OpenCartFramework\Support\Str;
use Symfony\Component\HttpFoundation\JsonResponse;

class CategoriesHandler
{
    private const THUMB_SIZE = 150;

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

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

    public function index(Request $request): JsonResponse
    {
        $cacheKey = 'categories.index';

        $categories = $this->cache->get($cacheKey);

        if ($categories === null) {
            $languageId = $this->settings->config()->getApp()->getLanguageId();
            $storeId = $this->settings->get('store.oc_store_id', 0);

            $perPage = $request->get('perPage', 100);

            $categoriesFlat = $this->queryBuilder->newQuery()
                ->select([
                    'categories.category_id' => 'id',
                    'categories.parent_id' => 'parent_id',
                    'categories.image' => 'image',
                    'descriptions.name' => 'name',
                    'descriptions.description' => 'description',
                ])
                ->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);
                    }
                )
                ->join(
                    new Table(db_table('category_to_store'), 'category_to_store'),
                    function (JoinClause $join) use ($storeId) {
                        $join->on('category_to_store.category_id', '=', 'categories.category_id')
                            ->where('category_to_store.store_id', '=', $storeId);
                    }
                )
                ->where('categories.status', '=', 1)
                ->orderBy('parent_id')
                ->orderBy('sort_order')
                ->get();

            $categories = $this->buildCategoryTree($categoriesFlat);

            $categories = array_slice($categories, 0, $perPage);

            $this->cache->set($cacheKey, $categories, 60 * 60 * 24);
        }

        return new JsonResponse([
            'data' => array_map(static function ($category) {
                return [
                    'id' => (int) $category['id'],
                    'image' => $category['image'] ?? '',
                    'name' => Str::htmlEntityEncode($category['name']),
                    'description' => $category['description'],
                    'children' => $category['children'],
                ];
            }, $categories),
        ]);
    }

    public function buildCategoryTree(array $flat, $parentId = 0): array
    {
        $branch = [];

        foreach ($flat as $category) {
            if ((int) $category['parent_id'] === (int) $parentId) {
                $children = $this->buildCategoryTree($flat, $category['id']);
                if ($children) {
                    $category['children'] = $children;
                }

                $image = $this->image
                    ->make($category['image'])
                    ->resize(self::THUMB_SIZE, self::THUMB_SIZE)
                    ->url();

                $branch[] = [
                    'id' => (int) $category['id'],
                    'image' => $image,
                    'name' => Utils::htmlEntityEncode($category['name']),
                    'description' => $category['description'],
                    'children' => $category['children'] ?? [],
                ];
            }
        }

        return $branch;
    }
}
