<?php

declare(strict_types=1);

namespace App\Services\Sitemaps;

use Closure;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;

final class SitemapCacheService
{
    public const INDEX_DOCUMENT = 'index';

    /**
     * @return array{content: string, source: string}|null
     */
    public function get(string $name): ?array
    {
        if ($this->preferPreGenerated()) {
            $preGenerated = $this->getPreGenerated($name);
            if ($preGenerated !== null) {
                return $preGenerated;
            }
        }

        $cached = Cache::get($this->cacheKey($name));
        if (is_string($cached) && $cached !== '') {
            return ['content' => $cached, 'source' => 'cache'];
        }

        if (! $this->preferPreGenerated()) {
            return $this->getPreGenerated($name);
        }

        return null;
    }

    /**
     * @return array{content: string, source: string}
     */
    public function remember(string $name, Closure $builder, bool $force = false, bool $persist = true): array
    {
        if (! $force) {
            $existing = $this->get($name);

            if ($existing !== null) {
                return $existing;
            }
        }

        $content = (string) $builder();

        if ($persist) {
            $this->store($name, $content);
        }

        return ['content' => $content, 'source' => 'built'];
    }

    public function store(string $name, string $content): void
    {
        Cache::put(
            $this->cacheKey($name),
            $content,
            now()->addSeconds(max(60, (int) config('sitemaps.cache_ttl_seconds', 900))),
        );

        if ($this->preGeneratedEnabled()) {
            Storage::disk($this->disk())->put($this->documentPath($name), $content);
        }
    }

    public function clear(array $names): int
    {
        $cleared = 0;

        foreach (array_values(array_unique($names)) as $name) {
            Cache::forget($this->cacheKey($name));

            if ($this->preGeneratedEnabled()) {
                Storage::disk($this->disk())->delete($this->documentPath($name));
            }

            $cleared++;
        }

        return $cleared;
    }

    public function documentPath(string $name): string
    {
        $prefix = trim((string) config('sitemaps.pre_generated.path', 'generated-sitemaps'), '/');
        $segments = $name === self::INDEX_DOCUMENT
            ? [$prefix, 'sitemaps', 'sitemap.xml']
            : [$prefix, 'sitemaps', $name . '.xml'];

        return implode('/', array_values(array_filter($segments, static fn (string $segment): bool => $segment !== '')));
    }

    private function cacheKey(string $name): string
    {
        return 'sitemaps:v2:' . $name;
    }

    /**
     * @return array{content: string, source: string}|null
     */
    private function getPreGenerated(string $name): ?array
    {
        if (! $this->preGeneratedEnabled()) {
            return null;
        }

        $disk = Storage::disk($this->disk());
        $path = $this->documentPath($name);

        if (! $disk->exists($path)) {
            return null;
        }

        $content = $disk->get($path);

        if (! is_string($content) || $content === '') {
            return null;
        }

        return ['content' => $content, 'source' => 'pre-generated'];
    }

    private function disk(): string
    {
        return (string) config('sitemaps.pre_generated.disk', 'local');
    }

    private function preGeneratedEnabled(): bool
    {
        return (bool) config('sitemaps.pre_generated.enabled', true);
    }

    private function preferPreGenerated(): bool
    {
        return $this->preGeneratedEnabled() && (bool) config('sitemaps.pre_generated.prefer', false);
    }
}