<?php

declare(strict_types=1);

namespace App\Services\Cdn;

use App\Services\ThumbnailService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

final class ArtworkCdnPurgeService
{
    /**
     * @param array<int, string> $objectPaths
     * @param array<string, mixed> $context
     */
    public function purgeArtworkObjectPaths(array $objectPaths, array $context = []): bool
    {
        $urls = array_values(array_unique(array_filter(array_map(
            fn (mixed $path): ?string => is_string($path) && trim($path) !== ''
                ? $this->cdnUrlForObjectPath($path)
                : null,
            $objectPaths,
        ))));

        return $this->purgeUrls($urls, $context);
    }

    /**
     * @param array<int, string> $variants
     * @param array<string, mixed> $context
     */
    public function purgeArtworkHashVariants(string $hash, string $extension = 'webp', array $variants = ['xs', 'sm', 'md', 'lg', 'xl', 'sq'], array $context = []): bool
    {
        $urls = array_values(array_unique(array_filter(array_map(
            fn (string $variant): ?string => ThumbnailService::fromHash($hash, $extension, $variant),
            $variants,
        ))));

        return $this->purgeUrls($urls, $context + ['hash' => $hash]);
    }

    /**
     * @param array<int, string> $urls
     * @param array<string, mixed> $context
     */
    private function purgeUrls(array $urls, array $context = []): bool
    {
        if ($urls === []) {
            return false;
        }

        if ($this->hasCloudflareCredentials()) {
            return $this->purgeViaCloudflare($urls, $context);
        }

        $legacyPurgeUrl = trim((string) config('cdn.purge_url', ''));
        if ($legacyPurgeUrl !== '') {
            return $this->purgeViaLegacyWebhook($legacyPurgeUrl, $urls, $context);
        }

        Log::debug('CDN purge skipped - no Cloudflare or legacy purge configuration is available', $context + [
            'url_count' => count($urls),
        ]);

        return false;
    }

    private function purgeViaCloudflare(array $urls, array $context): bool
    {
        $purgeUrl = sprintf(
            'https://api.cloudflare.com/client/v4/zones/%s/purge_cache',
            trim((string) config('cdn.cloudflare.zone_id')),
        );

        try {
            $response = Http::timeout(10)
                ->acceptJson()
                ->withToken(trim((string) config('cdn.cloudflare.api_token')))
                ->post($purgeUrl, ['files' => $urls]);

            if ($response->successful()) {
                return true;
            }

            Log::warning('Cloudflare artwork CDN purge failed', $context + [
                'status' => $response->status(),
                'body' => $response->body(),
                'url_count' => count($urls),
            ]);
        } catch (\Throwable $e) {
            Log::warning('Cloudflare artwork CDN purge threw an exception', $context + [
                'error' => $e->getMessage(),
                'url_count' => count($urls),
            ]);
        }

        return false;
    }

    private function purgeViaLegacyWebhook(string $purgeUrl, array $urls, array $context): bool
    {
        $paths = array_values(array_unique(array_filter(array_map(function (string $url): ?string {
            $path = parse_url($url, PHP_URL_PATH);

            return is_string($path) && $path !== '' ? $path : null;
        }, $urls))));

        if ($paths === []) {
            return false;
        }

        try {
            $response = Http::timeout(10)->acceptJson()->post($purgeUrl, ['paths' => $paths]);

            if ($response->successful()) {
                return true;
            }

            Log::warning('Legacy artwork CDN purge failed', $context + [
                'status' => $response->status(),
                'body' => $response->body(),
                'path_count' => count($paths),
            ]);
        } catch (\Throwable $e) {
            Log::warning('Legacy artwork CDN purge threw an exception', $context + [
                'error' => $e->getMessage(),
                'path_count' => count($paths),
            ]);
        }

        return false;
    }

    private function hasCloudflareCredentials(): bool
    {
        return trim((string) config('cdn.cloudflare.zone_id', '')) !== ''
            && trim((string) config('cdn.cloudflare.api_token', '')) !== '';
    }

    private function cdnUrlForObjectPath(string $objectPath): string
    {
        return rtrim((string) config('cdn.files_url', 'https://cdn.skinbase.org'), '/') . '/' . ltrim($objectPath, '/');
    }
}