<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\WopiBundle\Service\Wopi;

use ChampsLibres\WopiLib\Contract\Entity\Document;
use ChampsLibres\WopiLib\Contract\Service\DocumentLockManagerInterface;
use Chill\MainBundle\Redis\ChillRedis;
use Psr\Http\Message\RequestInterface;

class ChillDocumentLockManager implements DocumentLockManagerInterface
{
    private const LOCK_DURATION = 60 * 30;

    /**
     * Number of seconds to keep the lock after the delete lock operation.
     */
    private const LOCK_GRACEFUL_DURATION_TIME = 3;

    public function __construct(
        private readonly ChillRedis $redis,
        private readonly int $ttlAfterDeleteSeconds = self::LOCK_GRACEFUL_DURATION_TIME,
    ) {}

    public function deleteLock(Document $document, RequestInterface $request): bool
    {
        if (0 === $this->redis->exists($this->getCacheId($document))) {
            return true;
        }

        // some queries (ex.: putFile) may be executed on the same time than the unlock, so
        // we add a delay before unlocking the file, instead of deleting it immediatly
        return $this->redis->expire($this->getCacheId($document), $this->ttlAfterDeleteSeconds);
    }

    public function getLock(Document $document, RequestInterface $request): string
    {
        if (false !== $value = $this->redis->get($this->getCacheId($document))) {
            return $value;
        }

        throw new \RuntimeException('wopi key does not exists');
    }

    public function hasLock(Document $document, RequestInterface $request): bool
    {
        $r = $this->redis->exists($this->getCacheId($document));

        if (is_bool($r)) {
            return $r;
        }
        if (is_int($r)) {
            return $r > 0;
        }

        throw new \RuntimeException('data type not supported');
    }

    public function setLock(Document $document, string $lockId, RequestInterface $request): bool
    {
        $key = $this->getCacheId($document);
        $this->redis->setex($key, self::LOCK_DURATION, $lockId);

        return true;
    }

    private function getCacheId(Document $document): string
    {
        return sprintf('wopi_lib_lock_%s', $document->getWopiDocId());
    }
}
