<?php

namespace Zeedhi\Framework\Cache\Type;

use Psr\SimpleCache\CacheInterface;

/**
 * Implementação de cache baseada em MongoDB
 * Compatível com PSR-16 (SimpleCache).
 */
class MongoImpl implements CacheInterface {

    /** @var \MongoClient|\MongoDB\Client */
    protected $mongo;

    /** @var string */
    protected $collection;

    public function __construct($mongo, string $collection = 'cache') {
        $this->mongo = $mongo;
        $this->collection = $collection;
    }

    protected function getCollectionName(): string {
        return $this->collection;
    }

    public function fetch(string $id) {
        $result = $this->mongo->findOne($this->getCollectionName(), ['_id' => $id]);
        return $result['value'] ?? null;
    }

    public function contains(string $id): bool {
        $result = $this->mongo->findOne($this->getCollectionName(), ['_id' => $id]);
        return $result !== null;
    }

    public function save(string $id, $data, int $lifeTime = 0): bool {
        $expireAt = $lifeTime > 0 ? time() + $lifeTime : null;

        $this->mongo->update(
            $this->getCollectionName(),
            ['_id' => $id],
            [
                '$set' => [
                    'value'  => $data,
                    'expire' => $expireAt
                ]
            ],
            true
        );

        return true;
    }

    public function delete(string $id): bool {
        $this->mongo->remove($this->getCollectionName(), ['_id' => $id]);
        return true;
    }

    public function flushAll(): bool {
        $this->mongo->remove($this->getCollectionName(), []);
        return true;
    }

    /* =========================
       Métodos exigidos pelo PSR-16
       ========================= */

    public function get(string $key, mixed $default = null): mixed {
        try {
            $value = $this->fetch($key);
            if ($value === null) {
                return $default;
            }
            return $value;
        } catch (\Throwable $e) {
            return $default;
        }
    }

    public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool {
        $lifeTime = 0;
        if ($ttl instanceof \DateInterval) {
            $lifeTime = (new \DateTimeImmutable())->add($ttl)->getTimestamp() - time();
        } elseif (is_int($ttl)) {
            $lifeTime = $ttl;
        }
        return $this->save($key, $value, $lifeTime);
    }

    public function deleteMultiple(iterable $keys): bool {
        foreach ($keys as $key) {
            $this->delete($key);
        }
        return true;
    }

    public function clear(): bool {
        return $this->flushAll();
    }

    public function getMultiple(iterable $keys, mixed $default = null): iterable {
        $results = [];
        foreach ($keys as $key) {
            $results[$key] = $this->get($key, $default);
        }
        return $results;
    }

    public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool {
        foreach ($values as $key => $value) {
            $this->set($key, $value, $ttl);
        }
        return true;
    }

    public function has(string $key): bool {
        return $this->contains($key);
    }
}
