<?php

namespace App\Http\Controllers\Api\V1\Filters;

use App\Http\Controllers\Api\V1\Filters\Requests\FilterIndexRequest;
use App\Http\Controllers\Controller;
use App\Models\Ecommerce\Product\Brand\Brand;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Collection\Collection;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class FilterController extends Controller
{
    /**
     * Filter Controller
     */
    public function __construct(
        protected Request $request
    ) {}

    /**
     * Filters
     */
    public function index(FilterIndexRequest $request): JsonResponse
    {
        return response()->rest([
            'categories' => $this->categories()->map(fn ($category) => [
                'id' => $category->id,
                'parent_id' => $category->parent_id,
                'name' => $category->name,
            ]),
            'brands' => $this->brands(),
        ]);
    }

    /**
     * Categories
     */
    private function categories()
    {
        if ($this->shouldFilterByCategory()) {
            return Category::find($this->request->category_id, ['id', 'parent_id'])->children()->get(['id', 'parent_id', 'name']);
        }

        if ($this->shouldFilterByCollection()) {
            return $this->filterByCategoryResource(Collection::find($this->request->collection_id));
        }

        if ($this->shouldFilterByBrand()) {
            return $this->filterByCategoryResource(Brand::find($this->request->brand_id));
        }

        return Category::query()->where('is_visible', true)->ordered()->get(['id', 'parent_id', 'name']);
    }

    private function brands()
    {
        if ($this->shouldFilterByBrand()) {
            return Brand::where('id', $this->request->brand_id)->get(['id', 'name']);
        }

        if ($this->shouldFilterByCategory()) {
            $brands = Category::find($this->request->category_id)->products()
                ->where('products.is_visible', true)
                ->where('products.parent_id', null)
                ->where('products.stock', '>', 0)
                ->distinct('products.brand_id')
                ->pluck('products.brand_id');

            return Brand::whereIntegerInRaw('id', $brands)->get(['id', 'name']);
        }

        if ($this->shouldFilterByCollection()) {
            $brands = Collection::find($this->request->collection_id)->products()
                ->where('products.is_visible', true)
                ->where('products.parent_id', null)
                ->where('products.stock', '>', 0)
                ->distinct('products.brand_id')
                ->pluck('products.brand_id');

            return Brand::whereIntegerInRaw('id', $brands)->get(['id', 'name']);
        }

        return Brand::query()->where('is_visible', true)->ordered()->get(['id', 'name']);
    }

    /**
     * Filter by category
     */
    private function filterByCategoryResource($resource)
    {
        $products = $resource->products()
            ->where('products.is_visible', true)
            ->where('products.parent_id', null)
            ->where('products.stock', '>', 0)
            ->distinct('products.id')
            ->pluck('products.id');

        return Category::where('is_visible', true)->ordered()->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
            ->where('product_has_relations.productable_type', '=', 'category')
            ->whereIntegerInRaw('product_has_relations.product_id', $products)
            ->get(['id', 'parent_id', 'name'])
            ->unique('categories.id');
    }

    /**
     * Check if request should be filtered by category
     */
    private function shouldFilterByCategory(): bool
    {
        return $this->request->filled('category_id');
    }

    /**
     * Check if request should be filtered by collection
     */
    private function shouldFilterByCollection(): bool
    {
        return $this->request->filled('collection_id');
    }

    /**
     * Check if request should be filtered by brand
     */
    private function shouldFilterByBrand(): bool
    {
        return $this->request->filled('brand_id');
    }
}
