<?php

/**
 * @package Dotclear
 * @subpackage Core
 *
 * @copyright Olivier Meunier & Association Dotclear
 * @copyright AGPL-3.0
 */
declare(strict_types=1);

namespace Dotclear\Core;

use Dotclear\Database\Cursor;
use Dotclear\Database\MetaRecord;
use Dotclear\Database\Statement\DeleteStatement;
use Dotclear\Database\Statement\SelectStatement;
use Dotclear\Database\Statement\UpdateStatement;
use Dotclear\Interface\Core\VersionInterface;

/**
 * @brief   Modules (and core) version handler.
 *
 * Handle id,version pairs through database.
 *
 * @since   2.28, modules version features have been grouped in this class
 * @since   2.36, constructor arguments has been replaced by Core instance
 */
class Version implements VersionInterface
{
    /**
     * The version stack.
     *
     * @var     array<string,string>    $stack;
     */
    private array $stack = [];

    /**
     * Full table name (including db prefix).
     */
    protected string $table;

    /**
     * Constructs a new instance.
     *
     * @param   Core    $core   The core container
     */
    public function __construct(
        protected Core $core
    ) {
        $this->table = $this->core->db()->con()->prefix() . self::VERSION_TABLE_NAME;
    }

    public function openVersionCursor(): Cursor
    {
        return $this->core->db()->con()->openCursor($this->table);
    }

    public function getVersion(string $module = 'core'): string
    {
        return $this->getVersions()[$module] ?? '';
    }

    public function getVersions(): array
    {
        if ($this->stack === []) {
            $rs = (new SelectStatement())
                ->columns([
                    'module',
                    'version',
                ])
                ->from($this->table)
                ->select();

            if ($rs instanceof MetaRecord) {
                while ($rs->fetch()) {
                    $this->stack[$rs->strField('module')] = $rs->strField('version');
                }
            }
        }

        return $this->stack;
    }

    public function setVersion(string $module, string $version): void
    {
        $cur = $this->openVersionCursor();
        $cur->setField('module', $module);
        $cur->setField('version', $version);

        if ($this->getVersion($module) === '') {
            $cur->insert();
        } else {
            $sql = new UpdateStatement();
            $sql->where('module = ' . $sql->quote($module));
            $sql->update($cur);
        }

        $this->stack[$module] = $version;
    }

    public function unsetVersion(string $module): void
    {
        $sql = new DeleteStatement();
        $sql
            ->from($this->table)
            ->where('module = ' . $sql->quote($module));

        $sql->delete();

        unset($this->stack[$module]);
    }

    public function compareVersion(string $module, string $version): int
    {
        return version_compare($version, $this->getVersion($module));
    }

    public function newerVersion(string $module, string $version): bool
    {
        return $this->compareVersion($module, $version) === 1;
    }
}
