<?php
/**
 *
 * @todo        Die Klasse muss überprüft werden.
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  LivewireComponents
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025 Wassilios Meletiadis <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Livewire\Core\User;


use BplanBase\Globals\Enums\UserAccessLevel;
use BplanBase\Globals\Livewire\Core\Global\BaseForm;
use BplanBase\Globals\Models\Tenant;
use BplanBase\Globals\Models\User;
use BplanBase\Globals\Repositories\Core\TenantRepository;
use BplanBase\Globals\Repositories\Core\TenantUserRepository;
use BplanBase\Globals\Services\TenantUserService;
use Exception;


/**
 *
 * @version     1.2.0 / 2026-02-17
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class Form extends BaseForm
{


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


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


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


    /**
     * Die Liste der Tenants auf die der Bearbeiter Zugriff hat
     *
     * @var     array $_editorTenants
     */
    public $_editorTenants = [];


    /**
     * @var     array $accessLevels
     */
    public $accessLevels;


    /**
     * Der aktuelle AccessLevel des bearbeitenden Users
     *
     * @var     string $editorAccessLevel
     */
    public $editorAccessLevel;


    /**
     * Der Wert des aktuellen AccessLevels des bearbeitenden Users
     *
     * @var     int $editorAccessLevelValue
     */
    public $editorAccessLevelValue;


    /**
     * Info darüber, ob der bearbeitende User selbst "restricted" ist
     *
     * @var     bool $editorRestricted
     */
    public $editorRestricted;


    /**
     * Beinhaltet den ursprünglich aus der Datenbank ausgelesenen Stand zum Tenant-Zugriff
     *
     * Die Liste enthält nicht zwangsläufig immer alle Tenant-Einstellungen des in der Bearbeitung
     * befindlichen Users. Sie wird beschränkt auf die Tenants, auf die auch der Bearbeiter Zugriff
     * hat.
     *
     * @var     array $initialTenantAccess
     */
    public $initialTenantAccess = [];


    /**
     * @var         array $main
     *
     * @version     1.0.0 / 2025-05-25
     */
    public $main = [
        'active'      => true,
        'access_level' => '',
        'name'        => '',
        'email'       => '',
        'restricted'  => true,
    ];


    /**
     * @var    array $onRedirectIdName
     */
    public $onRedirectIdName = [
        'ABORT' => null,
        'CREATE' => 'id',
        'DELETE' => null,
        'UPDATE' => null,
    ];


    /**
     * @var    array $_onRedirectRoutes
     */
    public $onRedirectRoutes = [
        'ABORT' => 'core-users.index',
        'CREATE' => 'core-user-record',
        'DELETE' => 'core-users.index',
        'UPDATE' => 'core-users.index',
    ];


    /**
     * @var    array $tabs
     */
    public $tabs = [
        'base',
        'tenant-users',
    ];


    /**
     * @var     array $tenant
     */
    public $tenant;


    /**
     * Beinhaltet den aktuellen Stand zum Tenant-Zugriff
     *
     * @var     array
     */
    public $tenantAccess = [];


    /**
     * @var     Illuminate\Support\Collection $Tenants
     */
    public $Tenants = [];


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


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


    /**
     *
     * @version     1.0.0 / 2025-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _initAccessLevels(): void
    {
        $accessLevels = UserAccessLevel::getValidAccessLevels($this->editorAccessLevel);

        $this->accessLevels = array_combine($accessLevels, $accessLevels);

        if (count($accessLevels) === 1) {
            $this->main['access_level'] = current($accessLevels);
        }
        if ($this->exists === true) {
            if ($this->accessLevels === null) {
                //  @todo Das funktioniert nicht mehr, weil die Rules jetzt nicht mehr vorinitialisiert werden.
                unset($this->rules['main.access_level']);
            }
        }
    } // _initAccessLevels()


    /**
     * Initialisiert Daten zum Bearbeiter
     *
     * @version     1.1.0 / 2025-07-16
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _initEditorData(): void
    {
        $Editor = auth()->user();

        $this->editorAccessLevel = $Editor->access_level;
        $this->editorAccessLevelValue = UserAccessLevel::getValue($this->editorAccessLevel);
        $this->editorRestricted = $Editor->restricted;

        if ($this->exists === true && $Editor->id === $this->mainID) {
            $this->readOnly = true;
        }


//dump($this->editorAccessLevel.' / '.$this->editorAccessLevelValue,
//    UserAccessLevel::{$this->editorAccessLevel}->value.' / '.UserAccessLevel::getValue($this->editorAccessLevel),
//    UserAccessLevel::{$this->editorAccessLevel}->name.' / '.UserAccessLevel::getName($this->editorAccessLevelValue),
//    UserAccessLevel::from(99),
//);


        /*
        **  Die verfügbaren Tenants des Bearbeiters ermitteln. */
        $this->_editorTenants = TenantRepository::determineUserTenants($Editor);

    } // _initEditorData()


    /**
     * Initialisiert diverse Tenant-Variablen
     *
     * Die Objektvariable $Tenants wird mit der der Liste der Tenants, auf die der User theoretisch
     * Zugriff haben kann, bestückt. Außerdem wird in der Objektvariablen $tenant ein Array mit
     * einem einzelnen Element abgelegt. Dieser Array wird im Formular verwendet um den Tenant des
     * Users in einem deaktivierten Select anzuzeigen.
     *
     * @version     1.2.0 / 2026-02-17
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _initTenantData(): void
    {
        /*
        **  Die ID des aktuellen Tenants des Bearbeiters aus der Registry lesen. */
        $tenantId = Tenant::current()->id;
        /*
        **  Alle Sub-Tenants zum aktuellen Tenant ermitteln und in der nachfolgenden Schleife die
        **  TenantAccess-Daten zusammenstellen und mit Standardwerten versehen. */
        $this->Tenants = collect(TenantRepository::determineSubTenants($tenantId));

        foreach ($this->Tenants as $Tenant) {
            $id = $Tenant->id;
            /*
            **  Tenants, auf die der Bearbeiter keinen Zugriff hat, überspringen. */
            if (!isset($this->_editorTenants[$id])) {
                continue;
            }
            $editorAccessLevel = $this->_editorTenants[$id]['access_level'];
            $validAccessLevels = UserAccessLevel::getValidAccessLevels($editorAccessLevel);

            $this->initialTenantAccess[$id] = [
                'id' => null,
                //'access_level' => UserAccessLevel::User->name, //UserAccessLevel::getName('User'),
                'access_level' => false,
                'accessLevels' => array_combine($validAccessLevels, $validAccessLevels),
                'active' => false,
                'identifier' => $Tenant->identifier,
            ];
        }
        if ($this->exists === true) {
            /*
            **  Gespeicherte TenantAccess-Daten zum aktuellen User ermitteln und die vorbereiteten
            **  Daten ergänzen. */
            $TenantAccess = TenantUserRepository::getUserTenants($this->mainID);

            foreach ($TenantAccess as $Item) {
                $id = $Item->tenant_id;
                /*
                **  Nur die Tenants, die zuvor bereits zusammengestellt wurden, werden ergänzt. */
                if (!isset($this->initialTenantAccess[$id])) {
                    continue;
                }
                /*
                **  Wenn der AccessLevel des Bearbeiters niedriger ist als der AccessLevel des in
                **  Bearbeitung befindlichen Users, dann wird der AccessLevel für das Formular auf
                **  FALSE gesetzt. Das bewirkt, dass im Formular keine Auswahl für den AccessLevel
                **  angezeigt wird und auch kein Update möglich ist. */
                $itemAccessLevelValue = UserAccessLevel::getValue($Item->access_level);

                if ($this->editorRestricted === false) {
                    $editorAccessLevelValue = $this->editorAccessLevelValue;
                } else {
                    $editorAccessLevel = $this->_editorTenants[$id]['access_level'];
                    //$editorAccessLevelValue = UserAccessLevel::getValue($editorAccessLevel);

                    $editorAccessLevelValue = $this->editorAccessLevelValue;
                }
                if ($editorAccessLevelValue >= $itemAccessLevelValue) {
                    /*
                    **  Nur wenn der AccessLevels des Bearbeiters höher oder gleich dem AccessLevel
                    **  des Users in Bearbeitung ist, kann der Bearbeiter den AccessLevel des Users
                    **  verändern. Zur Auswahl stehen dann auch nur die AccessLevel die der
                    **  Bearbeiter selbst  */
                    $accessLevel = $Item->access_level;

                    $validAccessLevels = UserAccessLevel::getValidAccessLevels($editorAccessLevel);

                    if (!empty($validAccessLevels)) {
                        $accessLevels = array_combine($validAccessLevels, $validAccessLevels);
                    }
                } else {
                    /*
                    **  Wenn der AccessLevel des Bearbeiters niedriger ist als der AccessLevels des
                    **  Users in Bearbeitung, dann darf der Bearbeiter keine Änderungen durchführen.
                    **  Die entsprechenden Zeilen werden dann nicht im Formular angezeigt.
                    **
                    **  Alternative Arbeitsweise:
                    **  Die Zeilen werden angezeigt, allerdings ohne die AccessLevel-Auswahl. Das
                    **  setzt voraus, dass vor/bei der Speicherung entweder nochmal die AccessLevel
                    **  von Bearbeiter und bearbeitetem User verglichen werden oder das Vorhandensein
                    **  des Wertes überprüft wird (je nach Art der Implementation). */
                    //$accessLevel = false;
                    //$accessLevels = [];
                    unset($this->initialTenantAccess[$id]);

                    continue;
                }
                $this->initialTenantAccess[$id]['id'] = $Item->id;
                $this->initialTenantAccess[$id]['access_level'] = $accessLevel;
                $this->initialTenantAccess[$id]['accessLevels'] = $accessLevels;
                $this->initialTenantAccess[$id]['active'] = true;
            }
        } else {
            /*
            **  Für neue User-Datensätze die "tenant_id" im Daten-Array hinterlegen. */
            $this->main['tenant_id'] = $tenantId;
        }
        $this->tenantAccess = $this->initialTenantAccess;
        /*
        **  Den Tenant des Users aus der Liste der verfügbaren Tenants ermitteln und den Select-
        **  Array initialisieren. */
        $Tenant = $this->Tenants->where('id', '=', $this->main['tenant_id'])
            ->first();

        $this->tenant = [$this->main['tenant_id'] => $Tenant->identifier];

    } // _initTenantData()


    /**
     * Speichert die TenantAccess-Daten für einen neuen User
     *
     * @param       bool $saveWasCreate
     *
     * @version     1.0.0 / 2025-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _onAfterSave(bool $saveWasCreate): void
    {
        parent::_onAfterSave($saveWasCreate);

        if ($saveWasCreate === true) {
            if ($this->main['restricted'] === true) {
                /*
                ** Bei neuen User-Datensätzen können die Tenant-Zuordnungen erst nach der Speicherung
                ** des User-Datensatzes angelegt werden. */
                $Service = new TenantUserService();

                foreach ($this->tenantAccess as $tenantId => & $item) {
                    if ($item['active'] === false) {
                        continue;
                    }
                    $item['user_id'] = $this->mainID;

                    $TenantAccess = $Service->create([
                        'access_level' => $item['access_level'],
                        'tenant_id' => $tenantId,
                        'user_id' => $this->mainID,
                    ]);
                    $item['id'] = $TenantAccess->id;
                }
            }
        }
    } // _onAfterSave()


    /**
     * Speichert die TenantAccess-Daten für einen bestehenden User
     *
     * @param       bool $saveIsCreate
     *
     * @throws      Exception
     *
     * @version     1.0.0 / 2025-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _onBeforeSave(bool $saveIsCreate): void
    {
        parent::_onBeforeSave($saveIsCreate);

        if ($saveIsCreate === false) {
            $Service = new TenantUserService();

            if ($this->main['restricted'] == true) {
                /*
                **  Bei bestehenden User-Datensätzen werden die Tenant-Zuordnungen vor der Speicherung
                **  des User-Datensatzes gespeichert. */

                foreach ($this->tenantAccess as $tenantId => & $item) {
                    /*
                    **  Handelt es sich um eine bestehende Zuordnung? */
                    if ($item['id'] !== null) {
                        if ($item['active'] === true && $item['access_level'] !== false) {
                            /*
                            **  Wenn sich der Status nicht geändert hat, dann kann lediglich der
                            **  AccessLevel aktualisiert werden. */
                            $Service->update($item['id'], [
                                'access_level' => $item['access_level'],
                            ]);
                        } else {
                            /*
                            **  Eine bestehende Zuordnung, mit jetzt inaktivem Status bewirkt eine
                            **  Löschung der Zuordnung.*/
                            $Service->delete($item['id']);

                            $item['id'] = null;
                        }
                    } else {
                        if ($item['active'] === true) {
                            if ($item['access_level'] === false || $item['access_level'] === '') {
                                throw new Exception(__('validation.required', ['attribute' => __('user-form.label.tenantAccess.tenant-accesslevel', ['identifier' => $item['identifier']])]));
                            }
                            $TenantAccess = $Service->create([
                                'access_level' => $item['access_level'],
                                'tenant_id' => $tenantId,
                                'user_id' => $this->mainID,
                            ]);
                            $item['id'] = $TenantAccess->id;
                        }
                    }
                }
            } else {
                $Service->deleteByUserId($this->mainID);

                foreach ($this->tenantAccess as & $item) {
                    $item['id'] = null;
                    //$item['access_level'] = UserAccessLevel::User->name;
                    $item['access_level'] = UserAccessLevel::getName('User');
                }
            }
        }
    } // _onBeforeSave()


    /**
     * Überschreibt die gleichnamige Methode der Base-Klasse
     *
     * @version     1.2.0 / 2026-02-17
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function mount(): void
    {
        parent::mount();
        /*
        **  Der Tab für die Tenant-Zuordnung wird deaktiviert, wenn der aktuelle User nicht
        **  "restricted" ist. */
        if ($this->main['restricted'] == false) {
            $this->tabs['tenant-users']['disabled'] = true;
        }
        $this->_initEditorData();
        $this->_initTenantData();

        $userAccessLevelValue = UserAccessLevel::getValue(\Registry::get('core.userAccessLevel'));

        if ($this->exists === true) {
            $editUserAccessLevelValue = UserAccessLevel::getValue($this->main['access_level']);
            /*
            **  Die Liste der Auswahlwerte für die Accesslevel wird nur dann initialisiert, wenn der
            **  Level des angemeldeten Users identisch oder höher ist als der des Users in Bearbeitung. */
            if ($editUserAccessLevelValue <= $userAccessLevelValue) {
                $this->_initAccessLevels();
            }
        } else {
            $this->multiSaveButton = false;
            $this->redirectOnStandardSave = true;

            $this->_initAccessLevels();
        }
        $this->onCreateRedirect = 'user-record';

    } // mount()


    /**
     *
     * @param       string $property
     *
     * @version     1.0.0 / 2025-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function updated(string $property): void
    {
        parent::updated($property);

        if ($property === 'main.restricted') {
            if ($this->main['restricted'] === true) {
                /*
                **  Tab für die Tenant-Zuordnung aktivieren. */
                $this->tabs['tenant-users']['disabled'] = false;
                /*
                **  Setzt den aktuellen Stand zum Tentant-Zugriff auf den ursprünlgich aus der
                **  Datenbank ausgelesenen Stand zurück. */
                $this->tenantAccess = $this->initialTenantAccess;

            } else {
                /*
                **  Tab für die Tenant-Zuordnung deaktivieren. */
                $this->tabs['tenant-users']['disabled'] = true;
                /*
                **  Wenn das Restricted-Flag zurückgesetzt wird, dann werden auch die Flags zu den
                **  einzelnen Mandanten deaktiviert. */
                $this->tenantAccess = array_map(function ($item) {
                    //$item['access_level'] = UserAccessLevel::User->name;
                    $item['access_level'] = UserAccessLevel::getName('User');
                    $item['active'] = false;

                    return $item;

                }, $this->tenantAccess);
            }
        } else {
            foreach ($this->tenantAccess as $tenantId => & $tenantAccess) {
                if ($property === 'tenantAccess.'.$tenantId.'.active') {
                    /*
                    **  Wenn der Active-Status deaktiviert wird, dann wird auch
                    **  der AccessLevel zurückgesetzt. */
                    if ($tenantAccess['active'] === false) {
                        $tenantAccess['access_level'] = '';
                    }
                } elseif ($property === 'tenantAccess.'.$tenantId.'.accessLevel') {
                    /*
                    **  Wenn ein AccessLevel ausgewählt wird, dann wird auch der
                    **  Active-Status aktiviert. */
                    if (!empty($tenantAccess['access_level'])) {
                        $tenantAccess['active'] = true;
                    }
                }
            }
        }
    } // updated()


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


} // class Form extends BaseForm {}
