<?php
/**
 * Middleware Class
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Middlewares
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025, 2026 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Http\Middleware;


use BplanBase\Globals\Exceptions\BadRequestException;
use BplanBase\Globals\Models\User;
use BplanBase\Globals\Repositories\Core\TenantRepository;
use BplanBase\Globals\Repositories\UserRepository;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;


/**
 * Middleware Class
 *
 * @version     1.1.0 / 2026-02-17
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class InitApiEnv
{


/* +++ TRAITS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ CLASS CONSTANTS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT MEMBERS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     string $_configTenantUid
     */
    private $_configTenantUid;


    /**
     * @var     Request $_Request
     */
    private $_Request;


    /**
     * Die ID des Tenants zur UID aus dem Request-Header
     *
     * @var     string $_tenantId
     */
    protected $_tenantId;


    /**
     * Die UID des Tenants aus dem Request-Header
     *
     * @var     string $_tenantUid
     */
    protected $_tenantUid;


    /**
     * Die ID des Users, der über das Auth-Token aus dem Header ermittelt wurde
     *
     * @var     string $_userId
     */
    protected $_userId;


/* +++ CLASS MEMBERS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT METHODS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *
     * @return 	    void
     *
     * @version     1.0.0 / 2025-
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _init()
    {
        $this->_Request = request();

        $this->_configTenantUid = $this->_tenantUid = config('globals.global-tenant');

        $this->_processHeaders();

        $routePrefix = $request->route()?->getPrefix();

        if (str_starts_with($routePrefix, 'api/data')) {
            $this->_processDataRequest($request);

        } elseif (str_starts_with($routePrefix, 'api/terminal')) {
            $this->_processTerminalRequest($request);
        }
    } // _init()


    /**
     * Initialisiert die Tenant-IDs und legt Parameter in der Registry ab
     *
     * Es wird die ID des Tenants zur Tenant-UID aus dem Request-Header ermittelt. Außerdem werden
     * die IDs aller Tenants ermittelt, auf die der User der aktuellen Anfrage Zugriff hat.
     *
     * @todo        Im Fehlerfall muss eine passende Exception geworfen werden, damit eine korrekte
     *              Response ausgeliefert wird.
     *
     * @param       string $tenantUid
     *
     * @param       string $userId
     *
     * @return      void
     *
     * @version     2.1.0 / 2026-02-17
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _initTenants(string $tenantUid, string $userId): void
    {
        /*
        **  Workaround für die Entwicklung.
        **  Wenn im Postman pro Verzeichnis abweichende Tenant-Header definiert werden, dann wird
        **  eine kommaseparierte Liste gesendet. Dieser Fall wird hier berücksichtigt, indem die
        **  Liste zerlegt und die letzte UID aus der Liste verwendet wird. */
        if (strpos($tenantUid, ',') !== false) {
            $uids = explode(',', $tenantUid);
            $tenantUid = trim(end($uids));
        }
        if ($tenantUid === null) {
            if (empty($this->_configTenantUid)) {
                /*
                **  Exception werfen, wenn keine Tenant-UID über den Header mitgegeben wurde und
                **  auch keine feste UID in der Konfiguration verzeichnet ist. */
                throw with(new BadRequestException(__('api-messages.exception.tenant-uid-missing')))
                    ->setSource(header: 'Api-Tenant-UID');
            }
            /*
            **  Bei Systemen mit einer festen Tenant-UID in der Konfiguration ist es akzeptabel,
            **  wenn keine UID über den Header mitgegeben wird. In dem Fall wird einfach die UID
            **  aus der Konfiguration verwendet. */
            $tenantUid = $this->_configTenantUid;

        } else {
            if (!empty($this->_configTenantUid) && $this->_configTenantUid !== $tenantUid) {
                /*
                **  Exception werfen, wenn eine Tenant-UID über den Header mitgegeben wurde und
                **  auch eine feste UID in der Konfiguration verzeichnet ist und diese voneinander
                **  abweichen. */
                throw with(new BadRequestException(__('api-messages.exception.tenant-uuid-illegal', [
                    'uid' => $tenantUid,

                ])))->setSource(header: 'Api-Tenant-UID');
            }
        }
/*
    @todo   Der folgende Methodenaufruf kann NULL liefern, wenn eine ungültige oder auch gelöschte
            TenantUID in der Config oder auch im Header angegeben ist.
            Diesen Fall abfangen.
*/
        $Tenant = TenantRepository::getByUuid($tenantUid);

        $masterTenantId = $Tenant->master_id;
        $this->_tenantId = $Tenant->id;

        if ($this->_configTenantUid === null) {
            $tenants = TenantRepository::determineUserTenants($this->_userId);
            $tenantIds = array_keys($tenants);

        } else {
            /*
            **  Wenn eine Tenant-UID in der Konfiguration steht, dann ist das die einzige gültige
            **  UID. */
            $tenants = [
                $this->_tenantId => $Tenant,
            ];
            $tenantIDs = [
                $this->_tenantId,
            ];
        }
        \Registry::put('core.masterTenantId', $masterTenantId);
        \Registry::put('core.tenantId', $this->_tenantId);
        \Registry::put('core.tenantIds', $tenantIds);
        \Registry::put('core.tenants', $tenants);

        if (in_array($this->_tenantId, $tenantIds) === false) {
            dd('Invalid combination: User-ID '.$this->_userId.', User-Tenant '.$this->_tenantId.', Tenants '.implode('/', $tenantIds));
        }
    } // _initTenants()


    /**
     * Initialisiert den User und legt Parameter in der Registry ab
     *
     * Es wird die ID des Users zur User-Uid aus dem Request-Header ermittelt.
     *
     * @todo        Im Fehlerfall muss eine passende Exception geworfen werden, damit eine korrekte
     *              Response ausgeliefert werden kann.
     *
     * @param       string $userUid
     *
     * @return      void
     *
     * @version     1.2.0 / 2026-02-17
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _initUser(string $userUid): void
    {
/*
    @todo   Diese Funktionalität muss noch fertiggestellt werden.
            Aktuell wird einfach nur die UID des Users aus dem Header verwendet.

    @todo   Der folgende Methodenaufruf kann NULL liefern, wenn eine ungültige oder auch gelöschte
            UserUID im Header angegeben ist.
            Diesen Fall abfangen.
*/
        $User = UserRepository::getByUuid($userUid);

        $this->_userId = $User->id;

        // \Registry::put('userId', $this->_userId);
        \Registry::put('core.userAccessLevel', $User->accessLevel);

    } // _initUser()


    /**
     *
     * @param [type] $request
     * @param Closure $next
     * @return void
     */
    protected function _processDataRequest($request)
    {
        $user = auth('sanctum')->user(); // oder andere Guard

        if (!$user) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        // Optional: Berechtigungen, Tenants usw. laden
        app()->instance(User::class, $user);
        // z.B. TenantContextService::initializeForUser($user);

    } // _processDataRequest()


    /**
     *
     * @return      $this
     *
     * @version     1.10.0 / 2026-02-17
     * @author      Wassilios Meletiadis Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _processHeaders(): self
    {
        $this->_tenantUid = $request->header('API-Tenant-UID');

        $this->_clientId = $this->_Request->header('Api-Client-ID', config('globals.api.default-client'));
        $transactionKey = $this->_Request->header('Api-Transaction-Key', Str::uuid());

        \Registry::put('transactionKey', $transactionKey);

        $headerTenantUid = $this->_Request->header('Api-Tenant-UID');
        $headerUserUid = $this->_Request->header('Api-User-UID');

        $this->_initUser($headerUserUid);
        $this->_initTenants($headerTenantUid, $this->_userId);

        return $this;

    } // _processHeaders()


    /**
     *
     * @param [type] $request
     * @param Closure $next
     * @return void
     */
    protected function _processTerminalRequest($request)
    {
        /*
            Arbeitsweise:
            Wenn es einen Config-Tenant gibt, dann ist der Header irrelevant. Es wird immer dieser Tenant verwendet.
        */

        if ($this->_tenantUid !== null && ($Tenant = Tenant::where('uuid', '=', $this->_tenantUid)->first())) {
            $user = $tenant->dummyUser(); // z. B. $tenant->systemUser()

        } else {
            $user = User::where('email', 'dummy-terminal@example.com')->first(); // Globaler Fallback
        }

        app()->instance(User::class, $user); // Kontext für weitere Services

    } // _processTerminalRequest()


    /**
     * Handle an incoming request.
     *
     * @param       Request $Request
     *
     * @param       Closure $Next
     *
     * @return      Response
     *
     * @version     1.0.0 / 2025-04-15
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function handle(Request $Request, Closure $Next): Response
    {
        if (request()->is('api/*')) {
            // $this->_init();
        }
        return $Next($Request);

    } // handle()


/* +++ CLASS METHODS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


} // class InitApiEnv {}
