<?php

namespace App\Domains\Catalog\Controllers;

use App\Domains\Catalog\Enums\GroupLayout;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Category;
use App\Domains\Catalog\Models\FeaturedGroup;
use App\Domains\Catalog\Models\FeaturedItem;
use App\Domains\Catalog\Requests\CatalogItemDetailRequest;
use App\Domains\Catalog\Requests\CategoryPageRequest;
use App\Domains\Catalog\Requests\FeaturedGroupPageRequest;
use App\Domains\Catalog\Requests\SearchCatalogItemsRequest;
use App\Domains\Catalog\Requests\StoreCatalogItemRequest;
use App\Domains\Catalog\Resources\CatalogFeaturedGroupResource;
use App\Domains\Catalog\Resources\CatalogFeaturedItemResource;
use App\Domains\Catalog\Resources\CatalogItemDetailResource;
use App\Domains\Catalog\Resources\CatalogItemResource;
use App\Domains\Catalog\Resources\CatalogSearchItemResource;
use App\Domains\Catalog\Services\CatalogService;
use App\Domains\Tenant\Models\Tenant;
use App\Http\Controllers\Controller;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Pagination\LengthAwarePaginator;

class CatalogController extends Controller
{
    private const ITEMS_PER_PAGE = 12;

    public function index(Tenant $tenant): JsonResponse
    {
        $featuredGroups = FeaturedGroup::query()
            ->where('tenant_code', $tenant->codigo)
            ->orderBy('group_order')
            ->get();

        return response()->json($featuredGroups->map(
            fn (FeaturedGroup $featuredGroup): array => (new CatalogFeaturedGroupResource(
                $featuredGroup,
                $this->featuredItemsResponse($featuredGroup, 1),
            ))->resolve()
        ));
    }

    public function search(
        SearchCatalogItemsRequest $request,
        Tenant $tenant,
        CatalogService $catalogService,
    ): AnonymousResourceCollection {
        return CatalogSearchItemResource::collection(
            $catalogService->search(
                $tenant,
                $request->validated('q'),
                $tenant->search_items_per_page,
                (int) $request->validated('page', 1),
            )
        );
    }

    public function category(
        CategoryPageRequest $request,
        Tenant $tenant,
        Category $category,
        CatalogService $catalogService,
    ): AnonymousResourceCollection {
        abort_unless($category->tenant_code === $tenant->codigo, 404);

        return CatalogSearchItemResource::collection(
            $catalogService->categoryItems(
                $tenant,
                $category,
                $tenant->search_items_per_page,
                (int) $request->validated('page', 1),
            )
        )->additional([
            'category' => [
                'id' => $category->id,
                'nombre' => $category->nombre,
                'categoria_id' => $category->categoria_id,
            ],
            'layout' => $tenant->search_product_layout->value,
            'group_layout' => $tenant->search_group_layout->value,
        ]);
    }

    public function featuredGroupItems(
        FeaturedGroupPageRequest $request,
        Tenant $tenant,
        FeaturedGroup $featuredGroup,
    ): JsonResponse {
        abort_unless($featuredGroup->tenant_code === $tenant->codigo, 404);

        $page = (int) $request->validated('page', 1);

        return response()->json($this->featuredItemsResponse($featuredGroup, $page));
    }

    public function show(
        CatalogItemDetailRequest $request,
        Tenant $tenant,
        CatalogItem $catalogItem,
        CatalogService $catalogService,
    ): CatalogItemDetailResource {
        abort_unless($catalogItem->tenant_code === $tenant->codigo, 404);

        $variantId = $request->validated('variant_id');

        return CatalogItemDetailResource::make(
            $catalogService->getDetail(
                $catalogItem,
                $variantId === null ? null : (int) $variantId,
            )
        );
    }

    public function store(
        StoreCatalogItemRequest $request,
        Tenant $tenant,
        CatalogService $catalogService,
    ): JsonResponse {
        $catalogItem = $catalogService->create([
            ...$request->validated(),
            'tenant_code' => $tenant->codigo,
        ]);

        return CatalogItemResource::make($catalogItem)
            ->response()
            ->setStatusCode(201);
    }

    /** @return array<array-key, mixed> */
    private function featuredItemsResponse(FeaturedGroup $featuredGroup, int $page): array
    {
        if ($featuredGroup->group_layout !== GroupLayout::Paginated) {
            $featuredItems = $this->featuredItemsQuery($featuredGroup)->get();

            $featuredItems->each(
                fn ($featuredItem) => $featuredItem->setRelation('featuredGroup', $featuredGroup)
            );

            return CatalogFeaturedItemResource::collection($featuredItems)->resolve();
        }

        $paginator = $this->paginateFeaturedItems($featuredGroup, $page);

        $paginator->getCollection()->each(
            fn ($featuredItem) => $featuredItem->setRelation('featuredGroup', $featuredGroup)
        );

        return CatalogFeaturedItemResource::collection($paginator)
            ->response()
            ->getData(true);
    }

    private function paginateFeaturedItems(
        FeaturedGroup $featuredGroup,
        int $page,
    ): LengthAwarePaginator {
        $paginator = $this->featuredItemsQuery($featuredGroup)->paginate(
            perPage: self::ITEMS_PER_PAGE,
            pageName: 'page',
            page: $page,
        );

        return $paginator->withPath(route('catalog.featured-groups.items.index', [
            'tenant' => $featuredGroup->tenant_code,
            'featuredGroup' => $featuredGroup->id,
        ]));
    }

    /** @return HasMany<FeaturedItem, FeaturedGroup> */
    private function featuredItemsQuery(FeaturedGroup $featuredGroup): HasMany
    {
        return $featuredGroup->featuredItems()->with([
            'catalogItem.inventory',
            'catalogItem.attachments',
            'catalogItem.variants.inventory',
            'catalogItem.variants.attachments',
            'catalogItem.variants.definitions.itemAttribute.attribute',
            'catalogItem.bundleComponents.catalogItem',
            'catalogItem.bundleComponents.variant.catalogItem',
        ]);
    }
}
