<?php

namespace App\Domains\Catalog\Resources;

use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\ItemAttribute;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Catalog\Services\CatalogItemAllowanceService;
use App\Domains\Shared\Enums\FieldType;
use App\Domains\Ticket\Resources\ValidityTimeResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;

/** @mixin CatalogItem */
class CatalogItemDetailResource extends JsonResource
{
    /** @return array<string, mixed> */
    public function toArray(Request $request): array
    {
        /** @var Variant|null $selectedVariant */
        $selectedVariant = $this->resource->getRelation('selectedVariant');

        return [
            'id' => $this->id,
            'type' => $this->type->value,
            'category_id' => $this->category_id,
            'brand_id' => $this->brand_id,
            'slug' => $this->slug,
            'nombre' => $this->nombre,
            'descripcion' => $this->descripcion,
            'precio' => $this->precio,
            'category' => $this->category?->nombre,
            'brand' => $this->brand?->nombre,
            'inventory_policy' => $this->inventory_policy?->value,
            'inventory_subject' => $this->inventory_subject->value,
            'max_units_per_user' => $this->max_units_per_user,
            'has_tickets' => $this->has_tickets,
            'attributes' => $this->attributesData(),
            'maximum_addable_quantity' => $this->when(
                $selectedVariant === null,
                fn () => $this->maximumAddable($this->availableStock()),
            ),
            'unavailable_message' => $this->when(
                $selectedVariant === null,
                fn () => $this->unavailableMessage($this->availableStock()),
            ),
            'images' => $this->when(
                $selectedVariant === null,
                fn () => $this->imageUrls($this->attachments),
            ),
            'variants' => $this->variants
                ->map(fn (Variant $variant): array => $this->variantData($variant))
                ->values(),
            'selected_variant' => $this->when(
                $selectedVariant !== null,
                fn (): array => [
                    ...$this->variantData($selectedVariant),
                    'images' => $this->imageUrls($selectedVariant->attachments),
                ],
            ),
            'components' => $this->when(
                $this->isBundle(),
                fn () => $this->bundleComponents
                    ->map(function ($component): array {
                        $selectedItem = $component->variant ?? $component->catalogItem;

                        return [
                            'catalog_item_id' => $component->component_catalog_item_id,
                            'variant_id' => $component->component_variant_id,
                            'quantity' => $component->quantity,
                            'nombre' => $component->catalogItem->nombre,
                            'item_nombre' => $selectedItem->getName(),
                        ];
                    })
                    ->values(),
            ),
        ];
    }

    /** @return array<string, mixed> */
    private function attributeData(ItemAttribute $itemAttribute): array
    {
        $attribute = $itemAttribute->attribute;

        return [
            'id' => $attribute->id,
            'codigo' => $attribute->codigo,
            'nombre' => $attribute->nombre,
            'sort_order' => $itemAttribute->sort_order,
            'show_in_selector' => $itemAttribute->show_in_selector,
            'ticket_label' => $itemAttribute->ticket_label,
            'is_required' => $attribute->is_required,
            'allow_multi_select' => $itemAttribute->allow_multi_select,
            'metadata_schema' => $attribute->metadata_schema,
            'type' => $attribute->type->value,
            'options' => $attribute->type === FieldType::EventDate
                ? $this->eventDateOptions($itemAttribute)
                : $this->catalogAttributeOptions($itemAttribute),
        ];
    }

    /** @return Collection<int, array<string, mixed>> */
    private function eventDateOptions(ItemAttribute $itemAttribute): Collection
    {
        return $itemAttribute->attribute->eventDates
            ->map(fn ($eventDate, int $index): array => [
                'id' => $eventDate->id,
                'value' => (string) $eventDate->id,
                'label' => $eventDate->date->format('d/m/Y'),
                'sort_order' => $index,
                'validity_time_id' => $eventDate->validity_time_id,
                'validity_time' => ValidityTimeResource::make($eventDate->validityTime),
                'metadata' => [
                    'date' => $eventDate->date->format('Y-m-d'),
                    'time_start' => $eventDate->time_start,
                    'time_end' => $eventDate->time_end,
                ],
            ]);
    }

    /** @return Collection<int, array<string, mixed>> */
    private function catalogAttributeOptions(ItemAttribute $itemAttribute): Collection
    {
        $availableValues = $this->variants
            ->flatMap->definitions
            ->where('item_attribute_id', $itemAttribute->id)
            ->pluck('value')
            ->filter()
            ->unique();

        return $itemAttribute->attribute->options
            ->whereIn('value', $availableValues)
            ->map(fn ($option): array => [
                'id' => $option->id,
                'value' => $option->value,
                'label' => $option->label,
                'sort_order' => $option->sort_order,
                'validity_time_id' => $option->validity_time_id,
                'validity_time' => $option->validityTime === null
                    ? null
                    : ValidityTimeResource::make($option->validityTime),
                'metadata' => $option->metadata,
            ])
            ->values();
    }

    /** @return Collection<int, array<string, mixed>> */
    private function attributesData(): Collection
    {
        return $this->itemAttributes
            ->sort(function (ItemAttribute $left, ItemAttribute $right): int {
                return $left->sort_order <=> $right->sort_order
                    ?: mb_strtolower($left->attribute->nombre) <=> mb_strtolower($right->attribute->nombre);
            })
            ->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute))
            ->values();
    }

    /** @return array<string, mixed> */
    private function variantData(Variant $variant): array
    {
        $values = $variant->selectionOptions($this->itemAttributes);
        $eventDates = $variant->selectedEventDates();
        $variantStock = $this->variantStock($variant);

        return [
            'id' => $variant->id,
            'event_date_id' => $variant->event_date_id,
            'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
            'event_date_ids' => $eventDates->pluck('id')->values(),
            'event_dates' => $eventDates->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
            'descripcion' => $variant->getDescription(),
            'precio' => number_format($variant->getPrice(), 2, '.', ''),
            'maximum_addable_quantity' => $this->maximumAddable($variantStock),
            'unavailable_message' => $this->unavailableMessage($variantStock),
            'values' => $values,
        ];
    }

    /** @return Collection<int, string> */
    private function imageUrls(Collection $attachments): Collection
    {
        return $attachments
            ->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
            ->values();
    }

    private function variantStock(Variant $variant): ?int
    {
        return $this->inventory_policy === InventoryPolicy::Unlimited
            ? null
            : $variant->inventory->availableStock();
    }

    private function maximumAddable(?int $stock): ?int
    {
        return app(CatalogItemAllowanceService::class)->maximumAddableQuantity(
            $stock,
            $this->getAttribute('remaining_user_quota'),
        );
    }

    private function unavailableMessage(?int $stock): ?string
    {
        return app(CatalogItemAllowanceService::class)->unavailableMessage(
            $stock,
            $this->getAttribute('remaining_user_quota'),
        );
    }
}
