<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\services;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\interfaces\GlobalDataInterface;
use apexl\Io\modules\user\Dtos\SessionDto;
use apexl\Io\modules\user\entities\sessionEntity;
use apexl\Io\modules\user\exceptions\SessionException;
use apexl\Io\modules\user\interfaces\SessionManagerInterface;
use DateTimeImmutable;
use Exception;

final readonly class DatabaseSessionManager implements SessionManagerInterface
{
    public function __construct(private GlobalDataInterface $globalData) {}

    /**
     * @throws RecordNotFoundException
     * @throws Exception
     */
    public function endSession(): void
    {
        $session = $this->currentSessionEntity();
        $session->active = false;
        $session->ended = new DateTimeImmutable();
        $session->store();
    }

    /**
     * @throws RecordNotFoundException
     */
    private function currentSessionEntity(): ?sessionEntity
    {
        /** @var ?SessionDto $session */
        $session = $this->globalData->getData('session');
        if (!$session) {
            return null;
        }

        return $this->sessionEntityForSessionId($session->sessionId);
    }

    /**
     * @throws RecordNotFoundException
     */
    private function sessionEntityForSessionId(string $sessionId): sessionEntity
    {
        return sessionEntity::fromPrimaryKey($sessionId);
    }

    /**
     * @throws Exception
     */
    public function create(int $userId, string $sessionId): SessionDto
    {
        $session = new sessionEntity();
        $session->uid = $userId;
        $session->sessionId = $sessionId;
        $session->created = time();
        $session->active = true;
        $session->ended = 0;
        $session->store();

        return $session->toDto();
    }

    /**
     * @throws RecordNotFoundException
     */
    public function forSessionId(string $sessionId): SessionDto
    {
        $entity = $this->sessionEntityForSessionId($sessionId);

        if ($entity->active) {
            return $entity->toDto();
        }

        throw new RecordNotFoundException(sprintf('Session %s is found but not active', $sessionId));
    }

    /**
     * @throws RecordNotFoundException
     * @throws Exception
     */
    public function addDataToCurrentSession(array $data): void
    {
        $current = $this->currentSessionEntity();

        if (!$current) {
            throw new SessionException('No current session');
        }

        $this->addDataToSessionObject($current, $data);
    }

    /**
     * @throws Exception
     */
    private function addDataToSessionObject(sessionEntity $session, array $data): void
    {
        $session->session_data = [
            ...$session->session_data ?? [],
            ...$data,
        ];


        $session->store();
        $this->globalData->addData('session', $session->toDto());
    }

    public function current(): ?SessionDto
    {
        return $this->globalData->getData('session');
    }
}