<?php

namespace App\Services;

use App\Models\Task;
use Closure;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class EventTasksCacheService
{
    private const CACHE_PREFIX = 'event_tasks';

    private const CACHE_TTL_SECONDS = 120;

    /**
     * @return array{tasks: array<int, array<string, mixed>>, hash: string}
     */
    public function remember(int $eventId, string $locale, Closure $resolver): array
    {
        $key = $this->cacheKey($eventId, $locale);

        return Cache::remember($key, now()->addSeconds(self::CACHE_TTL_SECONDS), $resolver);
    }

    public function forgetForEvents(iterable $eventIds): void
    {
        $locales = $this->supportedLocales();
        $ids = is_array($eventIds) ? $eventIds : iterator_to_array($eventIds, false);

        foreach (array_unique(array_map('intval', $ids)) as $eventId) {
            foreach ($locales as $locale) {
                Cache::forget($this->cacheKey($eventId, $locale));
            }
        }
    }

    public function forgetForTask(Task $task): void
    {
        $collectionEventIds = DB::table('event_task_collection')
            ->join('task_collection_task', 'event_task_collection.task_collection_id', '=', 'task_collection_task.task_collection_id')
            ->where('task_collection_task.task_id', $task->id)
            ->pluck('event_task_collection.event_id')
            ->all();

        $directEventIds = $task->assignedEvents()->pluck('events.id')->all();

        $this->forgetForEvents(array_merge($collectionEventIds, $directEventIds));
    }

    /**
     * @return array<int, string>
     */
    public function supportedLocales(): array
    {
        $configured = config('app.supported_locales');

        if (is_string($configured)) {
            $configured = explode(',', $configured);
        }

        if (is_array($configured) && ! empty($configured)) {
            return $this->sanitizeLocales($configured);
        }

        $envConfigured = env('APP_SUPPORTED_LOCALES');

        if (is_string($envConfigured) && trim($envConfigured) !== '') {
            return $this->sanitizeLocales(explode(',', $envConfigured));
        }

        $fallback = array_filter([config('app.locale'), config('app.fallback_locale')]);

        if (! empty($fallback)) {
            return $this->sanitizeLocales($fallback);
        }

        return ['de', 'en'];
    }

    private function cacheKey(int $eventId, string $locale): string
    {
        return sprintf('%s:%d:%s', self::CACHE_PREFIX, $eventId, $locale);
    }

    /**
     * @param  array<int, string>  $locales
     * @return array<int, string>
     */
    private function sanitizeLocales(array $locales): array
    {
        $sanitized = array_map(function ($value) {
            $normalized = strtolower(trim((string) $value));
            $normalized = str_replace('_', '-', $normalized);

            return explode('-', $normalized)[0] ?? $normalized;
        }, $locales);

        return array_values(array_filter(array_unique($sanitized)));
    }
}
