<?php

namespace App\Helpers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

/**
 * Tamamlanan job'ların admin Queue UI için kısa geçmişi.
 *
 * PRC-876: **driver-aware**. Redis backend'i kullanılıyorsa native Redis LIST
 * (atomik, düşük overhead); değilse (paylaşımlı hosting, file/database) Cache
 * array fallback (capped, Cache::lock ile). Böylece hem yüksek-hacim Redis
 * kurulumları etkilenmez hem shared-hosting'de geçmiş görünür.
 */
class CompletedJobLogger
{
    public const REDIS_KEY = 'completed_jobs_list';

    /** Cache fallback anahtarı (file/database sürücü) */
    private const CACHE_KEY = 'completed_jobs_list';

    private const MAX_JOBS = 100;

    private const TTL_SECONDS = 604800; // 7 gün

    private static function useRedis(): bool
    {
        return config('cache.default') === 'redis' || config('queue.default') === 'redis';
    }

    public static function log(string $jobClass, array $data = []): void
    {
        $jobEntry = [
            'job_id' => uniqid('completed_', true),
            'job_class' => $jobClass,
            'short_class' => class_basename($jobClass),
            'job_type' => self::detectJobType($jobClass),
            'queue' => 'completed',
            'details' => $data,
            'attempts' => 1,
            'created_at' => now()->format('d.m.Y H:i:s'),
            'created_at_timestamp' => now()->timestamp,
            'completed_at_human' => now()->diffForHumans(),
        ];

        try {
            if (self::useRedis()) {
                Redis::lpush(self::REDIS_KEY, json_encode($jobEntry));
                Redis::ltrim(self::REDIS_KEY, 0, self::MAX_JOBS - 1);
                if (Redis::ttl(self::REDIS_KEY) === -1) {
                    Redis::expire(self::REDIS_KEY, self::TTL_SECONDS);
                }

                return;
            }

            // Cache fallback (file/database) — capped array, lock ile atomik.
            self::cacheMutate(function (array $list) use ($jobEntry): array {
                array_unshift($list, $jobEntry);

                return array_slice($list, 0, self::MAX_JOBS);
            });
        } catch (\Throwable $e) {
            Log::warning('Failed to log completed job', ['job_class' => $jobClass, 'error' => $e->getMessage()]);
        }
    }

    public static function getAll(): array
    {
        try {
            if (self::useRedis()) {
                return array_map(fn ($j) => json_decode($j, true), Redis::lrange(self::REDIS_KEY, 0, -1));
            }

            return (array) Cache::get(self::CACHE_KEY, []);
        } catch (\Throwable $e) {
            Log::warning('Failed to get completed jobs', ['error' => $e->getMessage()]);

            return [];
        }
    }

    public static function count(): int
    {
        try {
            return self::useRedis()
                ? (int) Redis::llen(self::REDIS_KEY)
                : count((array) Cache::get(self::CACHE_KEY, []));
        } catch (\Throwable $e) {
            return 0;
        }
    }

    public static function clear(): void
    {
        try {
            if (self::useRedis()) {
                Redis::del(self::REDIS_KEY);
            } else {
                Cache::forget(self::CACHE_KEY);
            }
        } catch (\Throwable $e) {
            Log::warning('Failed to clear completed jobs', ['error' => $e->getMessage()]);
        }
    }

    public static function paginate(int $page = 1, int $perPage = 20): array
    {
        try {
            $total = self::count();
            $start = ($page - 1) * $perPage;

            if (self::useRedis()) {
                $jobs = Redis::lrange(self::REDIS_KEY, $start, $start + $perPage - 1);
                $data = array_map(fn ($j) => json_decode($j, true), $jobs);
            } else {
                $data = array_slice((array) Cache::get(self::CACHE_KEY, []), $start, $perPage);
            }

            return [
                'data' => $data,
                'total' => $total,
                'per_page' => $perPage,
                'current_page' => $page,
                'last_page' => (int) ceil($total / $perPage),
            ];
        } catch (\Throwable $e) {
            Log::warning('Failed to paginate completed jobs', ['error' => $e->getMessage()]);

            return ['data' => [], 'total' => 0, 'per_page' => $perPage, 'current_page' => $page, 'last_page' => 0];
        }
    }

    /** Cache array'i lock altında güvenli değiştir (fallback yolu). */
    private static function cacheMutate(callable $mutator): void
    {
        $apply = function () use ($mutator): void {
            $list = (array) Cache::get(self::CACHE_KEY, []);
            Cache::put(self::CACHE_KEY, $mutator($list), self::TTL_SECONDS);
        };
        try {
            Cache::lock(self::CACHE_KEY.':lock', 10)->block(5, $apply);
        } catch (\Throwable $e) {
            $apply();
        }
    }

    private static function detectJobType(string $jobClass): string
    {
        $shortClass = class_basename($jobClass);
        if (str_contains($shortClass, 'Email') || str_contains($shortClass, 'SendEmail')) {
            return 'email';
        }
        if (str_contains($shortClass, 'Product') && str_contains($shortClass, 'Provision')) {
            return 'product_provisioning';
        }
        if (str_contains($shortClass, 'Domain') && str_contains($shortClass, 'Provision')) {
            return 'domain_provisioning';
        }

        return 'other';
    }
}
