<?php

namespace Fifa\ConnectId\Sdk\Storage;

use DateTime;
use Fifa\ConnectId\Sdk\Registration\CorrelationIdBasedItemContainerInterface;
use Fifa\ConnectId\Sdk\Registration\CorrelationIdBasedItemProviderInterface;
use Fifa\ConnectId\Sdk\Registration\MessageReceived;
use Psr\Log\LoggerInterface;
use SQLite3;
use SQLite3Result;

class MessagesSqliteStorage implements CorrelationIdBasedItemContainerInterface, CorrelationIdBasedItemProviderInterface, CleanableInterface
{
    const FILE_NAME = 'messages.db';

    /**
     * @var MessagesSqliteStorage
     */
    private static $instance = null;

    /**
     * @var SQLite3
     */
    private $db;

    /**
     * @var LoggerInterface
     */
    private $logger;

    private function __construct(LoggerInterface $logger)
    {
        $this->db = new SQLite3(dirname(__FILE__) . '/' . self::FILE_NAME);
        $this->db->busyTimeout(5000);
        $this->logger = $logger;
    }

    /**
     * Returns an instance of SqliteStorage class.
     * In case when it doesn't exist yet, it's created
     *
     * @return MessagesSqliteStorage
     */
    public static function getInstance(LoggerInterface $logger)
    {
        if (is_null(self::$instance)) {
            self::$instance = new MessagesSqliteStorage($logger);
        }

        return self::$instance;
    }

    /**
     * @inheritDoc
     * @return MessageReceived|null
     */
    public function getByCorrelationId(string $correlationId): ?MessageReceived
    {
        $query = sprintf("SELECT xmlData FROM messages WHERE correlationId='%s'", SQLite3::escapeString($correlationId));
        $result = $this->db->querySingle($query, true);

        if (!is_null($result) && sizeof($result) > 0) {
            return new MessageReceived($result['xmlData']);
        }

        return null;
    }

    /**
     * @inheritDoc
     * @param MessageReceived $item
     * @return false|SQLite3Result
     */
    public function put(string $correlationId, MessageReceived $item)
    {
        $storedMessageReceived = $this->getByCorrelationId($correlationId);

        if (!is_null($storedMessageReceived)) {
            $warningMessage = "MessageReceived for correlation id `" . $correlationId . "` is already cached. Message insertion is skipped.";
            $this->logger->warning($warningMessage);
            return null;
        }

        $query = sprintf(
            "INSERT INTO messages (correlationId, receiveTime, xmlData) VALUES ('%s', '%s', '%s')",
            SQLite3::escapeString($correlationId),
            time(),
            SQLite3::escapeString($item->getXmlData())
        );

        return $this->db->query($query);
    }

    /**
     * @inheritDoc
     * @return false|SQLite3Result
     */
    public function deleteOlderThan(DateTime $dateTime)
    {
        $query = sprintf("DELETE FROM messages WHERE receiveTime < %s", SQLite3::escapeString($dateTime->getTimestamp()));
        return $this->db->query($query);
    }

    /**
     * String representation of the object
     *
     * @return string
     */
    public function __toString()
    {
        return 'FileName: ' . self::FILE_NAME;
    }
}