<?php

declare(strict_types=1);

/**
 * This file is part of CodeIgniter 4 framework.
 *
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace CodeIgniter\Session\Handlers\Database;

use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Session\Handlers\DatabaseHandler;
use ReturnTypeWillChange;

/**
 * Session handler for Postgre
 *
 * @see \CodeIgniter\Session\Handlers\Database\PostgreHandlerTest
 */
class PostgreHandler extends DatabaseHandler
{
    /**
     * Sets SELECT clause
     */
    protected function setSelect(BaseBuilder $builder)
    {
        $builder->select("encode(data, 'base64') AS data");
    }

    /**
     * Decodes column data
     *
     * @param string $data
     *
     * @return false|string
     */
    protected function decodeData($data)
    {
        return base64_decode(rtrim($data), true);
    }

    /**
     * Prepare data to insert/update
     */
    protected function prepareData(string $data): string
    {
        return '\x' . bin2hex($data);
    }

    /**
     * Cleans up expired sessions.
     *
     * @param int $max_lifetime Sessions that have not updated
     *                          for the last max_lifetime seconds will be removed.
     *
     * @return false|int Returns the number of deleted sessions on success, or false on failure.
     */
    #[ReturnTypeWillChange]
    public function gc($max_lifetime)
    {
        $separator = '\'';
        $interval  = implode($separator, ['', "{$max_lifetime} second", '']);

        return $this->db->table($this->table)->where('timestamp <', "now() - INTERVAL {$interval}", false)->delete() ? 1 : $this->fail();
    }

    /**
     * Lock the session.
     */
    protected function lockSession(string $sessionID): bool
    {
        $arg = "hashtext('{$sessionID}')" . ($this->matchIP ? ", hashtext('{$this->ipAddress}')" : '');
        if ($this->db->simpleQuery("SELECT pg_advisory_lock({$arg})") !== false) {
            $this->lock = $arg;

            return true;
        }

        return $this->fail();
    }

    /**
     * Releases the lock, if any.
     */
    protected function releaseLock(): bool
    {
        if (! $this->lock) {
            return true;
        }

        if ($this->db->simpleQuery("SELECT pg_advisory_unlock({$this->lock})") !== false) {
            $this->lock = false;

            return true;
        }

        return $this->fail();
    }
}
