<?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\Channel\Channel;
use App\Models\Ecommerce\Product\Brand\Brand;
use Illuminate\Support\Facades\DB;
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(),
            'brands' => $this->brands(),
        ]);
    }

    /**
     * Categories
     */
    private function categories()
    {
        if ($this->shouldFilterByCategory()) {
            return Category::query()
                    ->find($this->request->category_id, ['id', 'parent_id'])
                    ->children()
                    ->where('is_visible', true)
                    ->ordered()
                    ->get(['id', 'parent_id', 'name'])
                    ->map(fn ($category) => [
                        'id' => $category->id,
                        'parent_id' => $category->parent_id,
                        'name' => $category->name,
                    ]);
        }

        if ($this->shouldFilterByCollection()) {
            return $this->categoriesFor($this->request->collection_id, 'collection');
        }

        if ($this->shouldFilterByBrand()) {
            return $this->categoriesForBrand($this->request->brand_id);
        }

        if ($this->shouldFilterByChannel()) {
            return $this->categoriesFor($this->request->channel_id, 'channel');
        }

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

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

        if ($this->shouldFilterByCategory()) {
            $categoryId = (int) $this->request->category_id;

            return DB::table('brands')
                ->select('brands.id', 'brands.name')
                ->join('products', 'products.brand_id', '=', 'brands.id')
                ->join('product_has_relations', 'products.id', '=', 'product_has_relations.product_id')
                ->where('product_has_relations.productable_type', 'category')
                ->where('product_has_relations.productable_id', $categoryId)
                ->where('products.is_visible', true)
                ->whereNull('products.parent_id')
                ->where('products.stock', '>', 0)
                ->groupBy('brands.id', 'brands.name', 'brands.sort_order')
                ->orderBy('brands.sort_order')
                ->get();
        }

        if ($this->shouldFilterByCollection()) {
            $collectionId = (int) $this->request->collection_id;

            return DB::table('brands')
                ->select('brands.id', 'brands.name')
                ->join('products', 'products.brand_id', '=', 'brands.id')
                ->join('product_has_relations', 'products.id', '=', 'product_has_relations.product_id')
                ->where('product_has_relations.productable_type', 'collection')
                ->where('product_has_relations.productable_id', $collectionId)
                ->where('products.is_visible', true)
                ->whereNull('products.parent_id')
                ->where('products.stock', '>', 0)
                ->groupBy('brands.id', 'brands.name', 'brands.sort_order')
                ->orderBy('brands.sort_order')
                ->get();
        }

        if ($this->shouldFilterByChannel()) {
            $channelId = (int) $this->request->channel_id;

            return DB::table('brands')
                ->select('brands.id', 'brands.name')
                ->join('products', 'products.brand_id', '=', 'brands.id')
                ->join('product_has_relations', 'products.id', '=', 'product_has_relations.product_id')
                ->where('product_has_relations.productable_type', 'channel')
                ->where('product_has_relations.productable_id', $channelId)
                ->where('products.is_visible', true)
                ->whereNull('products.parent_id')
                ->where('products.stock', '>', 0)
                ->groupBy('brands.id', 'brands.name', 'brands.sort_order')
                ->orderBy('brands.sort_order')
                ->get();
        }

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

    /**
     * Categories for a resource
     */
    private function categoriesFor(int $id, string $type)
    {
        return DB::table('categories as c')
            ->select('c.id', 'c.parent_id', 'c.name')
            ->where('c.is_visible', true)
            ->whereExists(function ($query) use ($id, $type) {
                $query->select(DB::raw(1))
                    ->from('product_has_relations as phr_cat')
                    ->join('products as p', 'p.id', '=', 'phr_cat.product_id')
                    ->join('product_has_relations as phr_chan', 'phr_chan.product_id', '=', 'p.id')
                    ->whereColumn('phr_cat.productable_id', 'c.id')
                    ->where('phr_cat.productable_type', 'category')
                    ->where('phr_chan.productable_type', $type)
                    ->where('phr_chan.productable_id', $id)
                    ->where('p.is_visible', true)
                    ->whereNull('p.parent_id')
                    ->where('p.stock', '>', 0);
            })
            ->get()
            ->map(fn ($category) => [
                'id' => $category->id,
                'parent_id' => $category->parent_id,
                'name' => tr($category->name),
            ]);
    }

    /** 
     * Brands
     */
    private function categoriesForBrand(int $id)
    {
        return DB::table('categories')
            ->select('categories.id', 'categories.parent_id', 'categories.name')
            ->join('product_has_relations', function ($join) {
                $join->on('categories.id', '=', 'product_has_relations.productable_id')
                    ->where('product_has_relations.productable_type', 'category');
            })
            ->join('products', 'product_has_relations.product_id', '=', 'products.id')
            ->where('products.brand_id', $id)
            ->where('products.is_visible', true)
            ->whereNull('products.parent_id')
            ->where('products.stock', '>', 0)
            ->where('categories.is_visible', true)
            ->distinct()
            ->get()
            ->map(fn ($category) => [
                'id' => $category->id,
                'parent_id' => $category->parent_id,
                'name' => tr($category->name),
            ]);
    }

    /**
     * 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');
    }

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