<?php

namespace App\Services\ContentTypes;

use App\Models\ContentType;
use App\Models\ContentTypeSlugHistory;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;

class ContentTypeSlugResolver
{
    public function publicContentTypes(): Collection
    {
        return Cache::rememberForever($this->publicListCacheKey(), function () {
            return ContentType::query()
                ->ordered()
                ->get(['id', 'name', 'slug', 'description', 'order', 'hide_from_menu']);
        });
    }

    public function toolbarContentTypes(): Collection
    {
        return $this->publicContentTypes()
            ->reject(static fn (ContentType $contentType): bool => (bool) $contentType->hide_from_menu)
            ->values();
    }

    public function resolve(string $slug, bool $allowVirtual = false): ContentTypeSlugResolution
    {
        $normalizedSlug = strtolower(trim($slug));

        if ($allowVirtual && $this->isVirtualSlug($normalizedSlug)) {
            return new ContentTypeSlugResolution(
                requestedSlug: $normalizedSlug,
                isVirtual: true,
                virtualType: $normalizedSlug,
            );
        }

        $slugMap = $this->currentSlugMap();
        if (isset($slugMap[$normalizedSlug])) {
            return new ContentTypeSlugResolution(
                requestedSlug: $normalizedSlug,
                contentType: $this->publicContentTypes()->firstWhere('id', $slugMap[$normalizedSlug]),
            );
        }

        $historyMap = $this->historySlugMap();
        $redirectSlug = $historyMap[$normalizedSlug] ?? null;
        if ($redirectSlug !== null) {
            $contentTypeId = $slugMap[$redirectSlug] ?? null;

            return new ContentTypeSlugResolution(
                requestedSlug: $normalizedSlug,
                contentType: $contentTypeId !== null ? $this->publicContentTypes()->firstWhere('id', $contentTypeId) : null,
                redirectSlug: $redirectSlug,
            );
        }

        return new ContentTypeSlugResolution(requestedSlug: $normalizedSlug);
    }

    public function reservedSlugs(): array
    {
        return array_values(array_unique(array_map(
            static fn (string $slug): string => strtolower(trim($slug)),
            (array) config('content_types.reserved_slugs', [])
        )));
    }

    public function isReservedSlug(string $slug): bool
    {
        return in_array(strtolower(trim($slug)), $this->reservedSlugs(), true);
    }

    public function historicalSlugExists(string $slug, ?int $ignoreContentTypeId = null): bool
    {
        $query = ContentTypeSlugHistory::query()->where('old_slug', strtolower(trim($slug)));

        if ($ignoreContentTypeId !== null) {
            $query->where('content_type_id', '!=', $ignoreContentTypeId);
        }

        return $query->exists();
    }

    public function flushCaches(): void
    {
        Cache::forget($this->publicListCacheKey());
        Cache::forget($this->slugMapCacheKey());
        Cache::forget($this->historyMapCacheKey());
    }

    public function dynamicSitemapContentTypes(): Collection
    {
        return $this->publicContentTypes();
    }

    private function currentSlugMap(): array
    {
        return Cache::rememberForever($this->slugMapCacheKey(), function () {
            return ContentType::query()
                ->ordered()
                ->pluck('id', 'slug')
                ->mapWithKeys(static fn ($id, $slug) => [strtolower((string) $slug) => (int) $id])
                ->all();
        });
    }

    private function historySlugMap(): array
    {
        return Cache::rememberForever($this->historyMapCacheKey(), function () {
            $currentSlugById = ContentType::query()
                ->pluck('slug', 'id')
                ->mapWithKeys(static fn ($slug, $id) => [(int) $id => strtolower((string) $slug)])
                ->all();

            return ContentTypeSlugHistory::query()
                ->orderByDesc('id')
                ->get(['content_type_id', 'old_slug'])
                ->mapWithKeys(function (ContentTypeSlugHistory $history) use ($currentSlugById) {
                    $currentSlug = $currentSlugById[(int) $history->content_type_id] ?? null;

                    return $currentSlug !== null
                        ? [strtolower((string) $history->old_slug) => $currentSlug]
                        : [];
                })
                ->all();
        });
    }

    private function isVirtualSlug(string $slug): bool
    {
        return array_key_exists($slug, (array) config('content_types.virtual_types', []));
    }

    private function publicListCacheKey(): string
    {
        return (string) config('content_types.cache.public_list_key', 'content-types.public-list');
    }

    private function slugMapCacheKey(): string
    {
        return (string) config('content_types.cache.slug_map_key', 'content-types.slug-map');
    }

    private function historyMapCacheKey(): string
    {
        return (string) config('content_types.cache.history_map_key', 'content-types.slug-history-map');
    }
}
