<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\services;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\modules\user\exceptions\SessionException;
use apexl\Io\modules\user\interfaces\CanBeMasqueradedInterface;
use apexl\Io\modules\user\interfaces\MasqueradeServiceInterface;
use apexl\Io\modules\user\interfaces\SessionManagerInterface;

final readonly class SessionMasqueradeService implements MasqueradeServiceInterface
{
    private const string KEY__MASQUERADING = 'masquerading';

    public function __construct(private SessionManagerInterface $sessionManager) {}

    /**
     * @throws SessionException
     */
    public function startMasquerade(CanBeMasqueradedInterface $to): void
    {
        $this->setMasqueradeInfo(
            new MasqueradeDto(
                masqueradedClass: get_class($to),
                id: $to->getId(),
            )
        );
    }

    /**
     * @throws SessionException
     */
    private function setMasqueradeInfo(?MasqueradeDto $info): void
    {
        $this->sessionManager->addDataToCurrentSession([self::KEY__MASQUERADING => $info]);
    }

    /**
     * @throws SessionException
     */
    public function endMasquerade(): void
    {
        $this->setMasqueradeInfo(null);
    }

    public function isMasquerading(): bool
    {
        try {
            return !!$this->getMasqueradedInfo();
        } catch (SessionException) {
            return false;
        }
    }

    /**
     * @throws SessionException
     */
    private function getMasqueradedInfo(): ?MasqueradeDto
    {
        $sessionData = $this->sessionManager->current()?->data;

        return $sessionData[self::KEY__MASQUERADING] ?? null;
    }

    /**
     * @throws RecordNotFoundException
     * @throws SessionException
     */
    public function getMasqueraded(): CanBeMasqueradedInterface
    {
        $info = $this->getMasqueradedInfo();

        if (!$info) {
            throw new RecordNotFoundException('No masqueraded user found.');
        }

        $class = $info->masqueradedClass;

        /** @noinspection PhpIncompatibleReturnTypeInspection */
        return $class::fromPrimaryKey($info->id);
    }
}