<?php

declare(strict_types=1);

namespace Cerbero\LaravelEnum\Capsules;

use Closure;
use DateInterval;
use DateTimeInterface;
use Illuminate\Cache\Repository;
use Illuminate\Contracts\Cache\Lock;
use Illuminate\Support\Facades\Cache;
use RuntimeException;

/**
 * The key dealing with the Laravel cache.
 *
 * @template TCacheValue
 */
final class CacheKey
{
    /**
     * Instantiate the class.
     */
    public function __construct(private readonly string $key) {}

    /**
     * Determine whether the key exists in the cache.
     */
    public function exists(): bool
    {
        return Cache::has($this->key);
    }

    /**
     * Determine whether the key doesn't exist in the cache.
     */
    public function missing(): bool
    {
        return Cache::missing($this->key);
    }

    /**
     * Determine whether the key is present and not null.
     */
    public function hasValue(): bool
    {
        return Cache::has($this->key);
    }

    /**
     * Retrieve the value of the key from the cache.
     */
    public function get(mixed $default = null): mixed
    {
        return Cache::get($this->key, $default);
    }

    /**
     * Retrieve the value of the key from the cache and delete it.
     */
    public function pull(mixed $default = null): mixed
    {
        return Cache::pull($this->key, $default);
    }

    /**
     * Store the value of the key in the cache.
     */
    public function put(mixed $value, DateTimeInterface|DateInterval|int|null $ttl = null): bool
    {
        return Cache::put($this->key, $value, $ttl);
    }

    /**
     * Store the value of the key in the cache.
     */
    public function set(mixed $value, DateTimeInterface|DateInterval|int|null $ttl = null): bool
    {
        /** @phpstan-ignore argument.type */
        return Cache::set($this->key, $value, $ttl);
    }

    /**
     * Store the value of the key in the cache if the key does not exist.
     */
    public function add(mixed $value, DateTimeInterface|DateInterval|int|null $ttl = null): bool
    {
        return Cache::add($this->key, $value, $ttl);
    }

    /**
     * Increment the value of the key in the cache.
     */
    public function increment(float|int $value = 1): float|int|bool
    {
        /** @var float|int|bool */
        return Cache::increment($this->key, $value);
    }

    /**
     * Decrement the value of the key in the cache.
     */
    public function decrement(float|int $value = 1): float|int|bool
    {
        /** @var float|int|bool */
        return Cache::decrement($this->key, $value);
    }

    /**
     * Store the value of the key in the cache indefinitely.
     */
    public function forever(mixed $value): bool
    {
        return Cache::forever($this->key, $value);
    }

    /**
     * Retrieve or store the value of the key.
     *
     * @param Closure(): TCacheValue $callback
     */
    public function remember(Closure|DateTimeInterface|DateInterval|int|null $ttl, Closure $callback): mixed
    {
        return Cache::remember($this->key, $ttl, $callback);
    }

    /**
     * Retrieve or store indefinitely the value of the key.
     *
     * @param Closure(): TCacheValue $callback
     */
    public function rememberForever(Closure $callback): mixed
    {
        return Cache::rememberForever($this->key, $callback);
    }

    /**
     * Retrieve or store indefinitely the value of the key.
     */
    public function sear(Closure $callback): mixed
    {
        return Cache::sear($this->key, $callback);
    }

    /**
     * Retrieve the value of the key, refreshing it in the background if it is stale.
     *
     * @param array{ 0: \DateTimeInterface|\DateInterval|int, 1: \DateTimeInterface|\DateInterval|int } $ttl
     * @param callable(): TCacheValue $callback
     * @param array{ seconds?: int, owner?: string }|null $lock
     * @return TCacheValue
     */
    public function flexible(array $ttl, callable $callback, ?array $lock = null, bool $alwaysDefer = false): mixed
    {
        /** @phpstan-ignore-next-line function.alreadyNarrowedType */
        if (method_exists(Repository::class, __FUNCTION__)) {
            /** @phpstan-ignore-next-line arguments.count */
            return Cache::flexible($this->key, $ttl, $callback, $lock, $alwaysDefer);
        }

        throw new RuntimeException('Laravel 11 or later is required for flexible caching'); // @codeCoverageIgnore
    }

    /**
     * Remove the key from the cache.
     */
    public function forget(): bool
    {
        return Cache::forget($this->key);
    }

    /**
     * Delete the key from the cache.
     */
    public function delete(): bool
    {
        return Cache::delete($this->key);
    }

    /**
     * Retrieve the lock instance.
     */
    public function lock(int $seconds = 0, ?string $owner = null): Lock
    {
        return Cache::lock($this->key, $seconds, $owner);
    }

    /**
     * Restore the lock instance using the owner identifier.
     */
    public function restoreLock(string $owner): Lock
    {
        return Cache::restoreLock($this->key, $owner);
    }
}
