<?php

namespace App\Domains\Ticket\Services;

use Carbon\CarbonInterface;
use Illuminate\Support\Collection;

/**
 * Resultado completo de resolver la vigencia de un ticket.
 *
 * Cada ResolvedValidityGroup contiene condiciones AND. Entre los grupos se
 * aplica OR, por lo que alcanza con que uno de ellos esté activo.
 */
final readonly class ResolvedTicketValidity
{
    /** @param Collection<int, ResolvedValidityGroup> $groups */
    public function __construct(
        public Collection $groups,
        public bool $isResolvable = true,
        public bool $isUnrestricted = false,
    ) {}

    /** No existe ninguna restricción temporal configurada. */
    public static function unrestricted(): self
    {
        return new self(collect(), isUnrestricted: true);
    }

    /** La configuración fuente está incompleta o es inconsistente. */
    public static function unresolvable(): self
    {
        return new self(collect(), isResolvable: false);
    }

    /** Es válido cuando no tiene restricciones o algún grupo OR está activo. */
    public function isValid(?CarbonInterface $at = null): bool
    {
        if (! $this->isResolvable) {
            return false;
        }

        return $this->isUnrestricted || $this->groups->contains(
            fn (ResolvedValidityGroup $group): bool => $group->isValid($at)
        );
    }

    /** Sólo está vencido cuando todos los grupos OR ya vencieron. */
    public function isExpired(?CarbonInterface $at = null): bool
    {
        return $this->isResolvable
            && ! $this->isUnrestricted
            && $this->groups->isNotEmpty()
            && $this->groups->every(
                fn (ResolvedValidityGroup $group): bool => $group->isExpired($at)
            );
    }

    /** Inicio más temprano de todas las alternativas, usado como resumen. */
    public function effectiveStartsAt(?CarbonInterface $at = null): ?CarbonInterface
    {
        return $this->groups
            ->map(fn (ResolvedValidityGroup $group): ?CarbonInterface => $group->effectiveStartsAt($at))
            ->filter()
            ->sortBy(fn (CarbonInterface $startsAt): int => $startsAt->getTimestamp())
            ->first();
    }

    /** Vencimiento más tardío de todas las alternativas, usado como resumen. */
    public function effectiveExpiresAt(?CarbonInterface $at = null): ?CarbonInterface
    {
        return $this->groups
            ->map(fn (ResolvedValidityGroup $group): ?CarbonInterface => $group->effectiveExpiresAt($at))
            ->filter()
            ->sortByDesc(fn (CarbonInterface $expiresAt): int => $expiresAt->getTimestamp())
            ->first();
    }
}
