135 lines
3.9 KiB
PHP
135 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\AiEditing;
|
|
|
|
use App\Models\AiStyle;
|
|
use App\Models\Event;
|
|
use App\Models\EventPackage;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class AiStyleAccessService
|
|
{
|
|
public function __construct(private readonly AiStylingEntitlementService $entitlements) {}
|
|
|
|
public function canUseStyle(Event $event, AiStyle $style): bool
|
|
{
|
|
$entitlement = $this->entitlements->resolveForEvent($event);
|
|
if (! $entitlement['allowed']) {
|
|
return false;
|
|
}
|
|
|
|
$allowedSources = $this->allowedSources($style);
|
|
if (! in_array((string) $entitlement['granted_by'], $allowedSources, true)) {
|
|
return false;
|
|
}
|
|
|
|
$requiredPackageFeatures = $this->requiredPackageFeatures($style);
|
|
if ($requiredPackageFeatures === []) {
|
|
return true;
|
|
}
|
|
|
|
$packageFeatures = $this->resolveEventPackageFeatures($event);
|
|
|
|
foreach ($requiredPackageFeatures as $requiredFeature) {
|
|
if (! in_array($requiredFeature, $packageFeatures, true)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param Collection<int, AiStyle> $styles
|
|
* @return Collection<int, AiStyle>
|
|
*/
|
|
public function filterStylesForEvent(Event $event, Collection $styles): Collection
|
|
{
|
|
return $styles
|
|
->filter(fn (AiStyle $style): bool => $this->canUseStyle($event, $style))
|
|
->values();
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function allowedSources(AiStyle $style): array
|
|
{
|
|
$metadataSources = $this->normalizeStringList(Arr::get($style->metadata ?? [], 'entitlements.allowed_sources', []));
|
|
if ($metadataSources !== []) {
|
|
return $metadataSources;
|
|
}
|
|
|
|
if (is_bool(Arr::get($style->metadata ?? [], 'entitlements.allow_with_addon'))) {
|
|
return Arr::get($style->metadata ?? [], 'entitlements.allow_with_addon')
|
|
? ['package', 'addon']
|
|
: ['package'];
|
|
}
|
|
|
|
return $style->is_premium ? ['package'] : ['package', 'addon'];
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function requiredPackageFeatures(AiStyle $style): array
|
|
{
|
|
return $this->normalizeStringList(
|
|
Arr::get($style->metadata ?? [], 'entitlements.required_package_features', [])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function resolveEventPackageFeatures(Event $event): array
|
|
{
|
|
$eventPackage = $event->relationLoaded('eventPackage') && $event->eventPackage
|
|
? $event->eventPackage
|
|
: $event->eventPackage()->with('package')->first();
|
|
|
|
if (! $eventPackage instanceof EventPackage) {
|
|
return [];
|
|
}
|
|
|
|
$package = $eventPackage->relationLoaded('package') ? $eventPackage->package : $eventPackage->package()->first();
|
|
|
|
return $this->normalizeFeatureList($package?->features);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function normalizeFeatureList(mixed $value): array
|
|
{
|
|
if (is_string($value)) {
|
|
$decoded = json_decode($value, true);
|
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
$value = $decoded;
|
|
}
|
|
}
|
|
|
|
if (! is_array($value)) {
|
|
return [];
|
|
}
|
|
|
|
if (array_is_list($value)) {
|
|
return $this->normalizeStringList($value);
|
|
}
|
|
|
|
return $this->normalizeStringList(array_keys(array_filter($value, static fn (mixed $enabled): bool => (bool) $enabled)));
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function normalizeStringList(array $values): array
|
|
{
|
|
return array_values(array_unique(array_filter(array_map(
|
|
static fn (mixed $value): string => trim((string) $value),
|
|
$values
|
|
))));
|
|
}
|
|
}
|