<?php

namespace App\Repositories\Ecommerce\Product;

use App\Helpers\Ecommerce\Product\Filter\ProductFilterer;
use App\Helpers\Ecommerce\Product\Sort\ProductSorter;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class ProductRepository
{
    /**
     * Product pagination count
     */
    public const PERPAGE = 32;

    /**
     * Query builder
     *
     * @var Builder
     */
    protected mixed $queryBuilder;

    /**
     * Application request
     */
    protected Request $request;

    /**
     *  Relationships to eager load
     *
     * @var array<int, string>
     */
    protected array $with = [];

    /**
     * Product repository
     */
    public function __construct(Request $request)
    {
        $this->queryBuilder = Product::query();
        $this->request = $request;
    }

    /**
     * Create new class via static method "make"
     */
    public static function make(Request $request): self
    {
        return new self($request);
    }

    /**
     * Update query builder with resource relationship
     */
    public function queryAsFromResource($resource): self
    {
        $this->queryBuilder = $resource->products();

        return $this;
    }

    /**
     * Attach relationships to eager load
     */
    public function attachEagerLoadingRelationship(array $relationships): self
    {
        $this->with = array_merge($this->with, $relationships);

        return $this;
    }

    /**
     * Load relationships
     */
    public function eagerLoadRelationships(): self
    {
        $this->queryBuilder->with($this->with);

        return $this;
    }

    /**
     * Apply Basic queries
     */
    public function applyBasicQueries(): self
    {
        $this->attachEagerLoadingRelationship([
            'brand',
            'reviews',
            'media' => function ($query) {
                $query->orderBy('order_column', 'asc');
            },
        ]);

        $this->queryBuilder
            ->where('products.is_visible', true)
            ->where('products.parent_id', null)
            ->where('products.stock', '>', 0);

        return $this;
    }

    /**
     * Apply filters
     */
    public function applyFilters(): self
    {
        $this->queryBuilder = ProductFilterer::make($this->queryBuilder, $this->request)->filter();

        return $this;
    }

    /**
     * Apply search queries
     */
    public function applySearchQueries(): self
    {
        if (request()->filled('q')) {
            $searcQuery = str_replace([
                '\\',
                '(',
                ')',
                '[',
                ']',
            ], '', request('q'));

            // Search by name
            $this->queryBuilder->where('products.name', '~*', $searcQuery);
        }

        return $this;
    }

    public function applyMultiLevelFilter(Category $category): self
    {
        $categoryId = $category->id;

        $categoryIds = collect(DB::select('
            WITH RECURSIVE category_tree AS (
                SELECT id FROM categories WHERE id = ?
                UNION
                SELECT c.id
                FROM categories c
                JOIN category_tree ct ON c.parent_id = ct.id
            )
            SELECT id FROM category_tree
        ', [$categoryId]))->pluck('id');

        $products = DB::table('products')
            ->join('product_has_relations', 'products.id', '=', 'product_has_relations.product_id')
            ->whereIn('product_has_relations.productable_id', $categoryIds)
            ->where('product_has_relations.productable_type', '=', 'category')
            ->select('products.id')
            ->distinct()
            ->pluck('id')
            ->toArray();

        $this->queryBuilder->whereIntegerInRaw('products.id', $products);

        return $this;
    }

    /**
     * Apply sorting
     */
    public function applySorting(): self
    {
        $this->queryBuilder = ProductSorter::make($this->queryBuilder, $this->request)->sort();

        return $this;
    }

    /**
     * "Where IN" clouse
     */
    public function whereIn(string $attribute, array $value): self
    {
        $this->queryBuilder->whereIn($attribute, $value);

        return $this;
    }

    /**
     * "where integer in raw" clouse
     */
    public function whereIntegerInRaw(string $attribute, array $value): self
    {
        $this->queryBuilder->whereIn($attribute, $value);

        return $this;
    }

    /**
     * Simple pagination
     */
    public function simplePaginate(): Paginator
    {
        $this->eagerLoadRelationships();

        return $this->queryBuilder->simplePaginate(
            perPage: $this->defaultPaginationPerPage(),
            page: $this->paginationPage(),
        );
    }

    /**
     * Get the results
     */
    public function get()
    {
        $this->eagerLoadRelationships();

        return $this->queryBuilder->get();
    }

    /**
     * Default Pagination Per Page
     */
    public function defaultPaginationPerPage(): int
    {
        return intval(request('perPage')) ?: self::PERPAGE;
    }

    /**
     * Pagination page
     */
    public function paginationPage(): int
    {
        return intval($this->request->page) ?: 1;
    }

    /**
     * Path to manage products on panel
     */
    public static function panelPath(): string
    {
        return sprintf('%s/resources/products', config('nova.path'));
    }

    /**
     * Calculate price
     */
    public static function calculatePrice(int|float $price, int $tax): float
    {
        return round_up(($price / (100 - $tax)) * 100);
    }

    /**
     * Shipping attributes
     */
    public static function shippingAttributes(): array
    {
        return [
            'back_order' => false,
            'weight_value' => null,
            'weight_unit' => null,
            'height_value' => null,
            'height_unit' => null,
            'width_value' => null,
            'width_unit' => null,
            'depth_value' => null,
            'depth_unit' => null,
            'volume_value' => null,
            'volume_unit' => null,
        ];
    }

    /**
     * Nova repository
     */
    public static function nova(): NovaProductRepository
    {
        return new NovaProductRepository;
    }

    /**
     * Sanatize File Name
     */
    public static function sanitizeFileName(string $filename): string
    {
        return strtolower(str_replace(['#', '/', '\\', ' ', ','], '-', $filename));
    }

    /**
     * Ajax paginate
     */
    public static function ajaxPaginate($products): JsonResponse
    {
        return response()->json([
            'pagination' => $products,
            'products' => view('web.themes.trendyol.components.products.collection.just-render', [
                'products' => $products,
            ])->render(),
        ]);
    }
}
