<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\AdminBundle\Tenant;

use Ferienpass\CoreBundle\Entity\Tenant;
use Ferienpass\CoreBundle\Repository\TenantRepository;
use Ferienpass\CoreBundle\Tenant\TenantContext;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Lets a global admin work inside another tenant for the rest of their session.
 *
 * Support work is the reason this exists: a municipality reports a problem and
 * the operator needs to see their admin area, not a copy of it. The choice is
 * per-session, applies to the admin area only, and is available to nobody but
 * ROLE_GLOBAL_ADMIN — a super admin stays in the tenant they signed in to.
 */
class TenantSwitcher
{
    public const SESSION_KEY = '_ferienpass_tenant';

    public function __construct(
        private readonly RequestStack $requestStack,
        private readonly Security $security,
        private readonly TenantRepository $tenants,
        private readonly TenantContext $tenantContext,
    ) {
    }

    /**
     * Whether the tenant switcher applies at all: a multi-tenant build, and a
     * user allowed to cross tenant lines.
     */
    public function isAvailable(): bool
    {
        return $this->tenantContext->isEnabled() && $this->security->isGranted('ROLE_GLOBAL_ADMIN');
    }

    /**
     * The tenant the current session has switched to, or null when it is still
     * on the tenant of the host it signed in to.
     */
    public function selected(): ?Tenant
    {
        if (!$this->isAvailable() || !($this->requestStack->getMainRequest()?->hasSession() ?? false)) {
            return null;
        }

        $id = $this->requestStack->getSession()->get(self::SESSION_KEY);

        return \is_int($id) ? $this->tenants->find($id) : null;
    }

    /**
     * Switches the session to $tenant; null returns to the host's own tenant.
     *
     * Only the session is written — the active tenant for the *next* request is
     * resolved from the host and then re-applied from here, so callers redirect
     * rather than continue rendering under a half-switched context.
     */
    public function switchTo(?Tenant $tenant): void
    {
        if (!$this->isAvailable()) {
            throw new \LogicException('Only a global admin on a multi-tenant installation can switch tenants.');
        }

        $session = $this->requestStack->getSession();

        if (null === $tenant) {
            $session->remove(self::SESSION_KEY);

            return;
        }

        $session->set(self::SESSION_KEY, $tenant->getId());
    }
}
