<?php
/**
 * Livewire Component
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  LivewireComponents
 * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
 * @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\Livewire\Core\User\Modals;


use BplanBase\Globals\Enums\UserAccessLevel;
use BplanBase\Globals\Http\Requests\UpdateUserRecordRequest;
use BplanBase\Globals\Http\Requests\UpdateUserRequest;
use BplanBase\Globals\Models\Role;
use BplanBase\Globals\Models\Tenant;
use BplanBase\Globals\Models\User;
use BplanBase\Globals\Models\UserRecord;
use BplanBase\Globals\Repositories\Core\TenantRepository;
use BplanBase\Globals\Repositories\Core\TenantUserRepository;
use BplanBase\Globals\Repositories\IndexCollectionRepository;
use BplanBase\Globals\Services\TenantUserService;
use BplanBase\Globals\Services\UserRecordService;
use BplanBase\Globals\Services\UserService;
use BplanBase\Globals\Traits\InteractsWithBanner;
use Exception;
use Illuminate\Database\Eloquent\Collection;
use Livewire\Attributes\On;
use Livewire\Component;


/**
 * Livewire Component
 *
 * @version     1.2.0 / 2026-06-10
 * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class Edit extends Component
{


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


    use InteractsWithBanner;


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


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


    /**
     * @var     string
     */
    public string $accessLevel = '';


    /**
     * @var     array|null
     */
    public ?array $accessLevels = null;


    /**
     * @var     bool
     */
    public bool $active = false;


    /**
     * Roles available for assignment (active, non-internal roles the editor themselves has).
     *
     * @var     Collection|null
     */
    public ?Collection $AvailableRoles = null;


    /**
     * Whether the editor may assign roles to users.
     *
     * @var     bool
     */
    public bool $canAssignRoles = false;


    /**
     * Whether the editor may set a password directly (requires Developer+ access level)
     *
     * @var     bool
     */
    public bool $canSetPassword = false;


    /**
     * @var     string
     */
    public string $editorAccessLevel = '';


    /**
     * @var     int
     */
    public int $editorAccessLevelValue = 0;


    /**
     * @var     bool
     */
    public bool $editorRestricted = false;


    /**
     * The tenants the editor has access to, keyed by tenant ID
     *
     * @var     array
     */
    public array $editorTenants = [];


    /**
     * @var     string
     */
    public string $email = '';


    /**
     * @var     string
     */
    public string $firstName = '';


    /**
     * @var     array
     */
    public array $initialTenantAccess = [];


    /**
     * @var     bool
     */
    public bool $internal = false;


    /**
     * @var     string
     */
    public string $lastName = '';


    /**
     * @var     string
     */
    public string $name = '';


    /**
     * @var     string
     */
    public string $password = '';


    /**
     * @var     string
     */
    public string $passwordConfirmation = '';


    /**
     * @var     bool
     */
    public bool $restricted = false;


    /**
     * IDs of the roles currently assigned to the edited user.
     *
     * @var     array<int>
     */
    public array $roleIds = [];


    /**
     * @var     array
     */
    public array $tenantAccess = [];


    /**
     * For displaying the user's tenant in a disabled select
     *
     * @var     array|null
     */
    public ?array $tenantSelect = null;


    /**
     * @var     TenantUserService
     */
    protected TenantUserService $_TenantUserService;


    /**
     * @var     User|null
     */
    public ?User $_User = null;


    /**
     * @var     UserRecord|null
     */
    public ?UserRecord $_UserRecord = null;


    /**
     * @var     UserRecordService
     */
    protected UserRecordService $_UserRecordService;


    /**
     * @var     UserService
     */
    protected UserService $_Service;


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


    /**
     * @var     string
     */
    private static string $_logPrefix = 'Core/User/Modals/Edit';


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


    /**
     *
     * @param       UserService       $Service
     * @param       UserRecordService $UserRecordService
     * @param       TenantUserService $TenantUserService
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function boot(UserService $Service, UserRecordService $UserRecordService, TenantUserService $TenantUserService): void
    {
        $this->_Service           = $Service;
        $this->_UserRecordService = $UserRecordService;
        $this->_TenantUserService = $TenantUserService;

    } // boot()


    /**
     * Initializes editor-specific data (access level, tenant list)
     *
     * @version     1.1.0 / 2026-06-08
     * @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       = (bool) $Editor->restricted;
        $this->canSetPassword         = $this->editorAccessLevelValue >= UserAccessLevel::Developer->value;

        $this->editorTenants  = TenantRepository::determineUserTenants($Editor);
        $this->canAssignRoles = $Editor->can('users.assign-roles');

        if ($this->canAssignRoles === true) {
            $EditorRoleIds = $Editor->roles->pluck('id')->toArray();

            /*
            **  SystemManagers and Admins have no Spatie roles themselves but may
            **  assign any role. All others may only delegate roles they hold. */
            $isPrivileged = $this->editorAccessLevelValue >= UserAccessLevel::Admin->value;

            $this->AvailableRoles = $isPrivileged
                ? IndexCollectionRepository::getRoles(withInactive: false, withInternal: false)
                : IndexCollectionRepository::getRoles(
                    ExtendQuery: Role::whereIn('id', $EditorRoleIds),
                    withInactive: false,
                    withInternal: false
                );
        }
    } // _initEditorData()


    /**
     * Initializes tenant access data for the edited user
     *
     * Uses the materialized path to efficiently find all sub-tenants. Builds
     * the tenantAccess array respecting the editor's own access level per tenant.
     *
     * @param       int $userId
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _initTenantData(int $userId): void
    {
        $currentTenantId = Tenant::current()->id;

        $SubTenants = TenantRepository::determineSubTenantsByPath($currentTenantId);

        $this->tenantSelect = [$this->_User->tenant_id => ($SubTenants[$this->_User->tenant_id] ?? null)?->identifier];

        foreach ($SubTenants 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' => false,
                'accessLevels' => array_combine($validAccessLevels, $validAccessLevels),
                'active'       => false,
                'identifier'   => $Tenant->identifier,
            ];
        }
        /*
        **  Gespeicherte TenantAccess-Daten für den bearbeiteten User laden. */
        $TenantAccess = TenantUserRepository::getUserTenants($userId);

        foreach ($TenantAccess as $Item) {
            $id = $Item->tenant_id;

            if (!isset($this->initialTenantAccess[$id])) {
                continue;
            }
            $itemAccessLevelValue   = UserAccessLevel::getValue($Item->access_level);
            $editorAccessLevelValue = $this->editorAccessLevelValue;

            if ($this->editorRestricted === true && isset($this->editorTenants[$id])) {
                $editorAccessLevelValue = UserAccessLevel::getValue($this->editorTenants[$id]['access_level']);
            }
            /*
            **  Wenn der AccessLevel des Bearbeiters niedriger als der des Users ist, dann wird der
            **  Eintrag komplett ausgeblendet. */
            if ($editorAccessLevelValue < $itemAccessLevelValue) {
                unset($this->initialTenantAccess[$id]);

                continue;
            }
            $editorTenantAccessLevel = $this->editorTenants[$id]['access_level'] ?? $this->editorAccessLevel;
            $validAccessLevels       = UserAccessLevel::getValidAccessLevels($editorTenantAccessLevel);

            $this->initialTenantAccess[$id]['id']           = $Item->id;
            $this->initialTenantAccess[$id]['access_level'] = $Item->access_level;
            $this->initialTenantAccess[$id]['accessLevels'] = array_combine($validAccessLevels, $validAccessLevels);
            $this->initialTenantAccess[$id]['active']       = true;
        }
        $this->tenantAccess = $this->initialTenantAccess;

    } // _initTenantData()


    /**
     *
     * @param       array   $user
     * @param       string  $openTab
     *
     * @version     1.1.0 / 2026-06-08
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    #[On('show-edit-user-modal')]
    public function initUser(array $user, string $openTab = 'base'): void
    {
        $userId = $user['id'];

        $this->_User       = User::withoutGlobalScopes()->with('roles')->find($userId);
        $this->_UserRecord = UserRecord::withoutGlobalScopes()->find($userId);

        $this->accessLevel = $this->_User->access_level;
        $this->active      = (bool) $this->_User->active;
        $this->email       = $this->_User->email;
        $this->internal    = (bool) $this->_User->internal;
        $this->name        = $this->_User->name;
        $this->restricted  = (bool) $this->_User->restricted;

        $this->firstName = $this->_UserRecord->first_name ?? '';
        $this->lastName  = $this->_UserRecord->last_name ?? '';

        $this->_initEditorData();

        if ($this->canAssignRoles === true) {
            $this->roleIds = $this->_User->roles->pluck('id')->map(fn ($id) => (string) $id)->toArray();
        }

        $editUserAccessLevelValue = UserAccessLevel::getValue($this->accessLevel);

        if ($editUserAccessLevelValue <= $this->editorAccessLevelValue) {
            $validAccessLevels    = UserAccessLevel::getValidAccessLevels($this->editorAccessLevel);
            $this->accessLevels   = array_combine($validAccessLevels, $validAccessLevels);
        } else {
            $this->accessLevels   = null;
        }
        $this->_initTenantData($userId);

        $this->dispatch('user-modal-initialized', openTab: $openTab);

    } // initUser()


    /**
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function mount(): void
    {
    } // mount()


    /**
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function render()
    {
        return view('globals::livewire.core.user.modals.edit');

    } // render()


    /**
     *
     * @version     1.1.0 / 2026-06-08
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function resetModal(): void
    {
        $this->accessLevel            = '';
        $this->accessLevels           = null;
        $this->active                 = false;
        $this->AvailableRoles         = null;
        $this->canAssignRoles         = false;
        $this->canSetPassword         = false;
        $this->editorAccessLevel      = '';
        $this->editorAccessLevelValue = 0;
        $this->editorRestricted       = false;
        $this->editorTenants          = [];
        $this->email                  = '';
        $this->firstName              = '';
        $this->initialTenantAccess    = [];
        $this->internal               = false;
        $this->lastName               = '';
        $this->name                   = '';
        $this->password               = '';
        $this->passwordConfirmation   = '';
        $this->restricted             = false;
        $this->roleIds                = [];
        $this->tenantAccess           = [];
        $this->tenantSelect           = null;
        $this->_User                  = null;
        $this->_UserRecord            = null;

        $this->resetValidation();

    } // resetModal()


    /**
     *
     * @return      array
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function rules(): array
    {
        if ($this->_User === null || $this->_UserRecord === null) {
            return [];
        }
        $userRules = UpdateUserRequest::getRules($this->_User->id);

        unset($userRules['current_team_id']);
        unset($userRules['email_verified_at']);
        unset($userRules['profile_photo_path']);
        /*
        **  Passwort ist optional — wird nur gesetzt wenn Developer+ und Eingabe erfolgt. */
        $userRules['password'] = ['nullable', 'string', 'max:255'];
        /*
        **  camelCase → snake_case Remapping für Livewire-Properties. */
        $userRules['accessLevel'] = $userRules['access_level'];
        unset($userRules['access_level']);

        $userRecordRules = UpdateUserRecordRequest::getRules($this->_UserRecord->id);

        unset($userRecordRules['email']);
        unset($userRecordRules['id']);

        $userRecordRules['firstName'] = $userRecordRules['first_name'];
        unset($userRecordRules['first_name']);

        $userRecordRules['lastName'] = $userRecordRules['last_name'];
        unset($userRecordRules['last_name']);

        return array_merge($userRules, $userRecordRules);

    } // rules()


    /**
     *
     * @version     1.1.0 / 2026-06-08
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function save(): void
    {
        $this->validate();
        /*
        **  Passwort-Bestätigung manuell prüfen (camelCase Livewire-Properties). */
        if ($this->canSetPassword === true && $this->password !== '' && $this->password !== $this->passwordConfirmation) {
            $this->addError('passwordConfirmation', __('validation.confirmed', ['attribute' => __('globals::users.label.password')]));

            return;
        }

        try {
            /*
            **  TenantUser-Datensätze vor dem User-Update verwalten. */
            if ($this->restricted === true) {
                foreach ($this->tenantAccess as $tenantId => &$item) {
                    if ($item['id'] !== null) {
                        if ($item['active'] === true && $item['access_level'] !== false) {
                            $this->_TenantUserService->update($item['id'], [
                                'access_level' => $item['access_level'],
                            ]);
                        } else {
                            $this->_TenantUserService->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' => __('globals::users.label.tenant-access.access-level', ['identifier' => $item['identifier']])]));
                            }
                            $TenantAccess = $this->_TenantUserService->create([
                                'access_level' => $item['access_level'],
                                'tenant_id'    => $tenantId,
                                'user_id'      => $this->_User->id,
                            ]);
                            $item['id'] = $TenantAccess->id;
                        }
                    }
                }
            } else {
                /*
                **  Alle Tenant-Verknüpfungen des Users löschen. */
                $ExistingAccess = TenantUserRepository::getUserTenants($this->_User->id);

                foreach ($ExistingAccess as $ExistingItem) {
                    $this->_TenantUserService->delete($ExistingItem->id);
                }
                foreach ($this->tenantAccess as &$item) {
                    $item['id']           = null;
                    $item['access_level'] = UserAccessLevel::User->name;
                }
            }
            /*
            **  User-Stammdaten speichern. */
            $updateData = [
                'access_level' => $this->accessLevel,
                'active'       => $this->active,
                'internal'     => $this->internal,
                'name'         => $this->name,
                'restricted'   => $this->restricted,
            ];

            if ($this->canSetPassword === true && $this->password !== '') {
                $updateData['password'] = $this->password;
            }
            $this->_Service->update($this->_User, $updateData);

            if ($this->canAssignRoles === true) {
                app(\Spatie\Permission\PermissionRegistrar::class)
                    ->setPermissionsTeamId(Tenant::current()->id);
                
                $this->_User->syncRoles(Role::whereIn('id', $this->roleIds)->get());
            }
            /*
            **  UserRecord-Daten speichern. */
            $this->_UserRecordService->update($this->_UserRecord, [
                'first_name' => $this->firstName,
                'last_name'  => $this->lastName,
            ]);

            $LogUser = auth()->user();

            \AppLog::info('User ['.$this->_User->name.'] and UserRecord ['.$this->_UserRecord->id.'] updated by ['.$LogUser->name.' ('.$LogUser->email.')]', self::$_logPrefix);

            $this->dispatch('user-saved');

        } catch (\Throwable $th) {
            \AppLog::error($th, self::$_logPrefix);

            $this->dangerBanner(__('globals::users.error.update-error'));
        }

    } // save()


    /**
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Emilio Cannarozzo <emilio.cannarozzo@bplan-solutions.de>
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function sendPasswordReset(): void
    {
        try {
            $this->_Service->resetPassword($this->_User);

            $LogUser = auth()->user();

            \AppLog::info('Password reset sent for user ['.$this->_User->name.'] by ['.$LogUser->name.' ('.$LogUser->email.')]', self::$_logPrefix);

            $this->dispatch('user-password-reset-sent');

        } catch (\Throwable $th) {
            \AppLog::error($th, self::$_logPrefix);

            $this->dangerBanner(__('globals::users.error.password-reset-error'));
        }

    } // sendPasswordReset()


    /**
     * Handles changes to the restricted toggle
     *
     * @version     1.0.1 / 2026-06-10
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function updatedRestricted(): void
    {
        if ($this->restricted === true) {
            $this->tenantAccess = $this->initialTenantAccess;
        } else {
            $this->tenantAccess = array_map(function ($item) {
                $item['access_level'] = UserAccessLevel::User->value;
                $item['active']       = false;

                return $item;
            }, $this->tenantAccess);
        }

    } // updatedRestricted()


    /**
     * Handles changes to per-tenant active and access_level fields
     *
     * @param       mixed   $value
     * @param       string  $key
     *
     * @version     1.0.0 / 2026-05-25
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function updatedTenantAccess(mixed $value, string $key): void
    {
        [$tenantId, $field] = explode('.', $key, 2);

        if ($field === 'active') {
            if ($this->tenantAccess[$tenantId]['active'] === false) {
                $this->tenantAccess[$tenantId]['access_level'] = '';
            }
        } elseif ($field === 'access_level') {
            if (!empty($this->tenantAccess[$tenantId]['access_level'])) {
                $this->tenantAccess[$tenantId]['active'] = true;
            }
        }

    } // updatedTenantAccess()


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


} // class Edit extends Component {}
