<?php

namespace FluxCms\Core\Services;

use FluxCms\Core\Models\CmsContent;
use Illuminate\Support\Facades\Cache;

class CmsContentService
{
    protected int $cacheTtl;

    protected string $cachePrefix;

    /**
     * In-memory grouped content — populated once per request via preloadAll().
     *
     * @var array<string, \Illuminate\Support\Collection>
     */
    protected array $memoryCache = [];

    protected bool $preloaded = false;

    public function __construct()
    {
        $this->cacheTtl = config('flux-cms.cache.ttl', 3600);
        $this->cachePrefix = config('flux-cms.cache.key_prefix', 'flux_cms');
    }

    /**
     * Resolve a CMS content value by dot-notation key.
     *
     * Format: "group.nested.key" where the first segment is the group
     * and the rest is the key within that group.
     *
     * @param  array<string, string>  $replace
     */
    public function get(string $key, array $replace = [], ?string $locale = null): mixed
    {
        $locale = $locale ?? app()->getLocale();
        $fallbackLocale = config('app.fallback_locale', 'de');

        [$group, $contentKey] = $this->parseKey($key);

        if (! $group || ! $contentKey) {
            return $this->fallbackToLang($key, $replace, $locale);
        }

        $groupData = $this->loadGroup($group);

        $content = $groupData->firstWhere('key', $contentKey);

        if (! $content) {
            return $this->fallbackToLang($key, $replace, $locale);
        }

        $value = $content->getTranslation('value', $locale)
            ?? $content->getTranslation('value', $fallbackLocale);

        if ($value === null) {
            return $this->fallbackToLang($key, $replace, $locale);
        }

        if (is_string($value) && ! empty($replace)) {
            foreach ($replace as $placeholder => $replacement) {
                $value = str_replace(":{$placeholder}", (string) $replacement, $value);
            }
        }

        return $value;
    }

    /**
     * Wie get(), aber ohne Fallback auf __() – liefert null, wenn kein CMS-Eintrag existiert.
     */
    public function getIfExists(string $key, ?string $locale = null): mixed
    {
        $locale = $locale ?? app()->getLocale();
        $fallbackLocale = config('app.fallback_locale', 'de');

        [$group, $contentKey] = $this->parseKey($key);

        if (! $group || ! $contentKey) {
            return null;
        }

        $groupData = $this->loadGroup($group);

        $content = $groupData->firstWhere('key', $contentKey);

        if (! $content) {
            return null;
        }

        $value = $content->getTranslation('value', $locale)
            ?? $content->getTranslation('value', $fallbackLocale);

        return $value;
    }

    /**
     * Get all content for a group, keyed by content key.
     *
     * @return array<string, mixed>
     */
    public function getGroup(string $group, ?string $locale = null): array
    {
        $locale = $locale ?? app()->getLocale();

        $groupData = $this->loadGroup($group);

        $result = [];
        foreach ($groupData as $content) {
            $value = $content->getTranslation('value', $locale)
                ?? $content->getTranslation('value', config('app.fallback_locale', 'de'));
            $result[$content->key] = $value;
        }

        return $result;
    }

    /**
     * Load a group — triggers a single bulk preload on first access.
     */
    protected function loadGroup(string $group): \Illuminate\Support\Collection
    {
        if (! $this->preloaded) {
            $this->preloadAll();
        }

        return $this->memoryCache[$group] ?? collect();
    }

    /**
     * Preload ALL CMS content in a single query + single cache entry,
     * then group by content group in memory.
     */
    protected function preloadAll(): void
    {
        $this->preloaded = true;

        $cacheKey = "{$this->cachePrefix}.content.__all__";

        $allContent = Cache::remember($cacheKey, $this->cacheTtl, function () {
            return CmsContent::query()->orderBy('group')->orderBy('order')->get();
        });

        foreach ($allContent->groupBy('group') as $groupName => $items) {
            $this->memoryCache[$groupName] = $items;
        }
    }

    /**
     * Clear cache for a specific group or all groups.
     */
    public function clearCache(?string $group = null): void
    {
        Cache::forget("{$this->cachePrefix}.content.__all__");
        $this->memoryCache = [];
        $this->preloaded = false;

        if ($group) {
            Cache::forget("{$this->cachePrefix}.content.{$group}");
        } else {
            $groups = CmsContent::query()->distinct()->pluck('group');
            foreach ($groups as $g) {
                Cache::forget("{$this->cachePrefix}.content.{$g}");
            }
        }
    }

    /**
     * @return array{0: string|null, 1: string|null}
     */
    protected function parseKey(string $key): array
    {
        $dotPos = strpos($key, '.');
        if ($dotPos === false) {
            return [null, null];
        }

        $group = substr($key, 0, $dotPos);
        $contentKey = substr($key, $dotPos + 1);

        return [$group, $contentKey];
    }

    /**
     * @param  array<string, string>  $replace
     */
    protected function fallbackToLang(string $key, array $replace = [], ?string $locale = null): mixed
    {
        return __($key, $replace, $locale);
    }
}
