<?php

namespace App\Domains\Ticket\Services;

use App\Domains\Auth\Models\User;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Ticket\Exceptions\TicketGenerationException;
use App\Domains\Ticket\Models\Ticket;
use Carbon\CarbonInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class TicketGeneratorService
{
    /**
     * @return Collection<int, Ticket>
     */
    public function generate(
        CatalogItem $catalogItem,
        User $user,
        int $quantity = 1,
        ?int $sourceVariantId = null,
    ): Collection {
        if ($quantity < 1) {
            throw TicketGenerationException::invalidQuantity();
        }

        $now = now();

        return DB::transaction(function () use ($catalogItem, $user, $quantity, $sourceVariantId, $now): Collection {
            $catalogItems = $this->resolveCatalogItems($catalogItem, $quantity, $now);

            return $catalogItems->map(fn (CatalogItem $item): Ticket => Ticket::query()->create([
                'tenant_code' => $item->tenant_code,
                'ticket' => (string) Str::uuid(),
                'name' => $item->nombre,
                'description' => (string) ($item->descripcion ?? ''),
                'source_catalog_item_id' => $catalogItem->getKey(),
                'source_variant_id' => $sourceVariantId,
                'starts_at' => $item->minimum_use_date,
                'expires_at' => $item->maximum_use_date,
                'used_at' => null,
                'user_id' => $user->getKey(),
            ]));
        });
    }

    /**
     * @return Collection<int, CatalogItem>
     */
    private function resolveCatalogItems(
        CatalogItem $catalogItem,
        int $quantity,
        CarbonInterface $now,
    ): Collection {
        if (! $catalogItem->isBundle()) {
            $this->validateCatalogItem($catalogItem, $now);

            return Collection::times($quantity, fn (): CatalogItem => $catalogItem);
        }

        $catalogItem->loadMissing('bundleComponents.catalogItem');

        if ($catalogItem->bundleComponents->isEmpty()) {
            throw TicketGenerationException::emptyBundle($catalogItem);
        }

        return $catalogItem->bundleComponents
            ->flatMap(function ($component) use ($quantity, $now): Collection {
                $componentItem = $component->catalogItem;
                $this->validateCatalogItem($componentItem, $now);

                return Collection::times(
                    $quantity * $component->quantity,
                    fn (): CatalogItem => $componentItem,
                );
            })
            ->values();
    }

    private function validateCatalogItem(CatalogItem $catalogItem, CarbonInterface $now): void
    {
        if (! $catalogItem->has_tickets) {
            throw TicketGenerationException::ticketsDisabled($catalogItem);
        }

        if ($catalogItem->maximum_use_date?->lessThanOrEqualTo($now)) {
            throw TicketGenerationException::maximumUseDateReached($catalogItem);
        }
    }
}
