<?php

namespace App\Http\Livewire\Profiles;

use App\Http\Livewire\Traits\RequiresAdminAuthorization;
use App\Models\Admin;
use App\Models\Bank;
use App\Models\Organization;
use App\Models\User;
use App\Traits\AccountInfoTrait;
use App\Traits\DateTimeTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Component;
use Livewire\WithPagination;
use Namu\WireChat\Events\NotifyParticipant;
use Spatie\Permission\Models\Role;
use WireUi\Traits\WireUiActions;

class Manage extends Component
{
    use WithPagination;
    use WireUiActions;
    use DateTimeTrait;
    use AccountInfoTrait;
    use RequiresAdminAuthorization;

    public string $search = '';
    public ?string $typeFilter = null;
    public ?string $activeFilter = null;
    public ?string $emailVerifiedFilter = null;
    public bool $showModal = false;
    public $createTranslation;
    public int $profileId;
    public bool $buttonDisabled = true;
    public string $confirmString;
    public int $categoryId;
    public int $profile;

    public bool $modalEditAccounts = false;
    public bool $modalEditProfile = false;
    public bool $modalAttachProfile = false;
    public bool $modalDeleteProfile = false;
    public bool $modalRestoreProfile = false;

    public $selectedProfile;
    public int $selectedProfileId;    // Needed for the modals

    public $deleteProfileData = null;
    public $restoreProfileData = null;
    public string $balanceHandlingOption = 'delete';
    public $donationAccountId = null;
    public string $adminPassword = '';
    public bool $donationExceedsLimit = false;
    public $donationLimitError = null;

    public $initProfile = [];
    public $editProfile = [];
    public bool $editProfileChangedName = false;
    public $editProfileMessages = [];
    public bool $editProfileChanged = false;
    public bool $reActivate = false;

    public $initAttachProfile = [];
    public $editAttachProfile = [];
    public $newTypesAvailable = [];
    public bool $editAttachProfileChanged = false;


    public $initAccounts = [];
    public $editAccounts = [];
    public $editAccountsMessages = [];
    public bool $editAccountsChanged = false;

    public string $sortField = 'created_at'; // Default sort field
    public string $sortDirection = 'desc'; // Default sort direction
    public $perPage = 10; // default 10 results per page
    public $page;

    protected $listeners = [
        'profileCreated' => '$refresh', // $refresh is a magic method that forces a re-render
        'selectedProfile',
        'toAccountId' => 'setDonationAccountId'
    ];

    public function mount()
    {
        // Admin Authorization - Prevent IDOR attacks and cross-guard access
        $activeProfileType = session('activeProfileType');
        $activeProfileId = session('activeProfileId');

        if (!$activeProfileType || !$activeProfileId) {
            abort(403, __('No active profile selected'));
        }

        $profile = $activeProfileType::find($activeProfileId);

        if (!$profile) {
            abort(403, __('Active profile not found'));
        }

        // Validate profile ownership using ProfileAuthorizationHelper (prevents cross-guard attacks)
        \App\Helpers\ProfileAuthorizationHelper::authorize($profile);

        // Verify admin or central bank permissions
        if ($profile instanceof \App\Models\Admin) {
            // Admin access OK
        } elseif ($profile instanceof \App\Models\Bank) {
            // Only central bank (level 0) can access profile management
            if ($profile->level !== 0) {
                abort(403, __('Central bank access required for profile management'));
            }
        } else {
            abort(403, __('Admin or central bank access required'));
        }

        // Log admin access for security monitoring
        \Log::info('Profiles management access', [
            'component' => 'Profiles\\Manage',
            'profile_id' => $profile->id,
            'profile_type' => get_class($profile),
            'authenticated_guard' => \Auth::getDefaultDriver(),
            'ip_address' => request()->ip(),
        ]);
    }

    public function setDonationAccountId($accountId)
    {
        $this->donationAccountId = $accountId;
        $this->checkDonationLimits();
    }

    /**
     * Check if the donation would exceed the receiving account's limits.
     *
     * @return void
     */
    protected function checkDonationLimits()
    {
        // Reset error state
        $this->donationExceedsLimit = false;
        $this->donationLimitError = null;

        // If no donation account selected or no balance to donate, skip check
        if (!$this->donationAccountId || !isset($this->deleteProfileData['totalBalance']) || $this->deleteProfileData['totalBalance'] <= 0) {
            return;
        }

        // Get the donation account
        $donationAccount = \App\Models\Account::find($this->donationAccountId);
        if (!$donationAccount) {
            return;
        }

        // Clear cache for fresh balance
        \Cache::forget("account_balance_{$donationAccount->id}");

        // Get current balance of the receiving account
        $currentBalance = $donationAccount->balance;

        // Calculate the maximum receivable amount
        // limitMaxTo = limit_max - limit_min (similar to Pay.php logic)
        $limitMaxTo = $donationAccount->limit_max - $donationAccount->limit_min;

        // Calculate available budget for receiving
        $transferBudgetTo = $limitMaxTo - $currentBalance;

        // Check if donation amount exceeds the receiving account's budget
        if ($this->deleteProfileData['totalBalance'] > $transferBudgetTo) {
            $this->donationExceedsLimit = true;

            // Check if the receiving account holder's balance is public
            $holderType = $donationAccount->accountable_type;
            $balancePublic = timebank_config('account_info.' . strtolower(class_basename($holderType)) . '.balance_public', false);

            if ($balancePublic) {
                $this->donationLimitError = __('The selected organization account can only receive up to :amount more. Please select a different organization or delete the balance instead.', [
                    'amount' => tbFormat($transferBudgetTo)
                ]);
            } else {
                $this->donationLimitError = __('The selected organization account cannot receive this donation amount due to account limits. Please select a different organization or delete the balance instead.');
            }
        }
    }

    /**
     * Called when balanceHandlingOption is updated.
     *
     * @return void
     */
    public function updatedBalanceHandlingOption()
    {
        $this->checkDonationLimits();
    }

    protected function rules()
    {
        $modelKey = $this->editProfile['model'] ?? 'user'; // Default or handle if not set

        $baseRules = [
            'editProfile.inactive_at' => ['nullable', 'date'],
            'editProfile.name'        => timebank_config("rules.profile_{$modelKey}.name"),
            'editProfile.full_name'   => timebank_config("rules.profile_{$modelKey}.full_name"),
            'editProfile.email'       => timebank_config("rules.profile_{$modelKey}.email"),
            'editProfile.about_short' => timebank_config("rules.profile_{$modelKey}.about_short"),
            'editProfile.about'       => timebank_config("rules.profile_{$modelKey}.about"),
            'editProfile.motivation'  => timebank_config("rules.profile_{$modelKey}.motivation"),
            'editProfile.phone'       => timebank_config('rules.phone'),
            'editProfile.website'     => timebank_config("rules.profile_{$modelKey}.website"),
            'editProfile.comment'     => timebank_config('rules.comment'),

            'editAttachProfile.comment' => timebank_config('rules.comment'),
            'editAttachProfile.profile' => ['required', 'array'],
            'editAttachProfile.profile.id' => ['required', 'integer'],
            'editAttachProfile.profile.model' => ['required', 'string'],
            'editAttachProfile.profiles.*.role' => ['nullable', 'string', 'in:organization-manager,organization-coordinator,bank-manager,bank-coordinator'],
            'editAttachProfile.newProfile.role' => ['nullable', 'string', 'in:organization-manager,organization-coordinator,bank-manager,bank-coordinator,administrator'],

            'confirmString' => ['required',
                function (string $attribute, $value, $fail) {
                    $expected = strtolower(__('messages.confirm_input_string'));
                    if (strtolower($value) !== $expected) {
                        $fail(__('The confirmation keyword is incorrect.'));
                    }
                },
            ],
        ];

        // Add level validation for Bank models only
        $modelClass = $this->editProfile['modelClass'] ?? null;
        if ($modelClass === \App\Models\Bank::class) {
            $baseRules['editProfile.level'] = ['required', 'integer', 'in:1,2'];
        }

        // Ensure all loaded rules are arrays and handle nulls from config
        foreach ($baseRules as $key => $ruleSet) {
            if (is_string($ruleSet)) {
                $baseRules[$key] = explode('|', $ruleSet);
            } elseif (is_null($ruleSet)) {
                $baseRules[$key] = []; // Default to empty array if config key is missing or null
            }
        }
        return $baseRules;
    }

    protected $queryString = [
        'search' => ['except' => ''],
        'typeFilter' => ['except' => ''],
        'activeFilter' => ['except' => ''],
        'emailVerifiedFilter' => ['except' => ''],
        'sortField' => ['except' => 'created_at'],  // Fixed to match actual default
        'sortDirection' => ['except' => 'desc'],
        'perPage' => ['except' => 10],
        'page' => ['except' => 1]
    ];


    public function openEditAccountsModal($profileId, $modelName)
    {
        $this->confirmString = '';

        $profile = $modelName::with('accounts')->find($profileId);

        if (!$profile) {
            $modelBasename = class_basename($modelName);
            throw new \Exception("{$modelBasename} profile with ID {$profileId} not found.");
        }

        $this->editAccounts = $profile->makeVisible([
            'id',
            'name',
            'full_name',
            'email',
            'email_verified_at',
            'limit_min',
            'limit_max',
            'lang_preference',
            'last_login_at',
            'last_login_ip',
            'inactive_at',
            'deleted_at',
            'updated_at',
            'created_at',
            ])->toArray();

        $this->editAccounts['model'] = strtolower(class_basename($modelName));
        $this->editAccounts['location'] = $profile->getLocationFirst()['name'];
        $this->editAccounts['accounts'] = $this->getAccountsInfo($modelName, $profileId)->toArray(); // Use the AccountInfoTrait

        $this->editAccounts['totals'] = $this->getAccountsTotals($modelName, $profileId); // Use the AccountInfoTrait, include totals for past 12 months
        $this->editAccounts['totalsPastYear'] = $this->getAccountsTotals($modelName, $profileId, timebank_config('account_info.account_totals.countTransfersSince')); // Use the AccountInfoTrait, include totals for past 12 months
        $this->initAccounts = $this->editAccounts; // Store the initial profile data for comparison later
        $this->modalEditAccounts = true;
    }





    public function openEditProfileModal($profileId, $modelName)
    {
        $this->confirmString = '';

        $profile = $modelName::find($profileId);

        if (!$profile) {
            $modelBasename = class_basename($modelName);
            throw new \Exception("{$modelBasename} profile with ID {$profileId} not found.");
        }

        // $this->confirmString = strtolower(__('messages.confirm_input_string'));
        $visibleFields = [
            'id',
            'name',
            'full_name',
            'email',
            'email_verified_at',
            'profile_photo_path',
            'about',
            'about_short',
            'motivation',
            'website',
            'phone',
            'phone_public',
            'limit_min',
            'limit_max',
            'comment',
            'lang_preference',
            'last_login_at',
            'last_login_ip',
            'inactive_at',
            'deleted_at',
            'updated_at',
            'created_at',
        ];

        // Add 'level' field only for Bank models
        if ($modelName === \App\Models\Bank::class) {
            $visibleFields[] = 'level';
        }

        $this->editProfile = $profile->makeVisible($visibleFields)->only($visibleFields);

        $this->editProfile['model'] = strtolower(class_basename($modelName));
        $this->editProfile['modelClass'] = $modelName; // Store full class name for type checking
        $this->editProfile['location'] = $profile->getLocationFirst()['name'];

        // Cast level to string for WireUI select compatibility
        if (isset($this->editProfile['level'])) {
            $this->editProfile['level'] = (string) $this->editProfile['level'];
        }
        if ($this->editProfile['inactive_at']) {
            $this->reActivate = false; // If the profile is inactive, set reActivate to true
        } else {
            $this->reActivate = true; // Otherwise, set it to false
        }
        $this->initProfile = $this->editProfile; // Store the initial profile data for comparison later
        $this->modalEditProfile = true;
    }


    public function openAttachProfilesModal($profileId, $modelName)
    {
        $this->editAttachProfile['newProfile'] = null;
        $this->initAttachProfile['newProfile'] = null;
        $this->confirmString = '';

        $profile = $modelName::find($profileId);

        if (!$profile) {
            $modelBasename = class_basename($modelName);
            throw new \Exception("{$modelBasename} profile with ID {$profileId} not found.");
        }

        // $this->confirmString = strtolower(__('messages.confirm_input_string'));
        $this->editAttachProfile = $profile->makeVisible([
            'id',
            'full_name',
            'comment',
            'lang_preference',
            'last_login_at',
            'last_login_ip',
            'inactive_at',
            'deleted_at',
            'updated_at',
            'created_at',
            ])
            ->toArray();

        $this->editAttachProfile['model'] = strtolower(class_basename($modelName));
        $this->editAttachProfile['location'] = $profile->getLocationFirst()['name'];

        $profiles = [];

        // Map and merge all attached profiles into a single array

        if (class_basename($modelName) === 'User') {

            $this->newTypesAvailable = [Organization::class, Bank::class, Admin::class];

            $profileWithRelations = $modelName::with(['organizations', 'banksManaged', 'admins'])->find($profileId);
            foreach ($profileWithRelations->organizations as $org) {
                $arr = $org->toArray();
                $arr['typeName'] = __('organization');
                $arr['username'] = $arr['name'] ?? '';
                $arr['inactiveAt'] = $arr['inactive_at'] ?? null;
                $arr['type'] = 'Organization';
                $arr['role'] = $this->resolveLinkedUserRole($profileWithRelations, 'Organization', $org->id);
                $profiles[] = $arr;
            }
            foreach ($profileWithRelations->banksManaged as $bank) {
                $arr = $bank->toArray();
                $arr['typeName'] = __('bank');
                $arr['username'] = $arr['name'] ?? '';
                $arr['inactiveAt'] = $arr['inactive_at'] ?? null;
                $arr['type'] = 'Bank';
                $arr['role'] = $this->resolveLinkedUserRole($profileWithRelations, 'Bank', $bank->id);
                $profiles[] = $arr;
            }
            foreach ($profileWithRelations->admins as $admin) {
                $arr = $admin->toArray();
                $arr['typeName'] = __('admin');
                $arr['username'] = $arr['name'] ?? '';
                $arr['inactiveAt'] = $arr['inactive_at'] ?? null;
                $arr['type'] = 'Admin';
                $arr['role'] = 'administrator';
                $profiles[] = $arr;
            }
        } elseif (class_basename($modelName) === 'Organization' || class_basename($modelName) === 'Admin') {

            $this->newTypesAvailable = [User::class];

            $profileWithRelations = $modelName::with(['users'])->find($profileId);
            $parentType = class_basename($modelName);
            foreach ($profileWithRelations->users as $user) {
                $arr = $user->toArray();
                $arr['typeName'] = __('user');
                $arr['username'] = $arr['name'] ?? '';
                $arr['inactiveAt'] = $arr['inactive_at'] ?? null;
                $arr['type'] = 'User';
                $arr['role'] = $this->resolveLinkedUserRole($user, $parentType, $profileId);
                $profiles[] = $arr;
            }
        } elseif (class_basename($modelName) === 'Bank') {

            $this->newTypesAvailable = [User::class];

            $profileWithRelations = $modelName::with(['managers'])->find($profileId);
            foreach ($profileWithRelations->managers as $manager) {
                $arr = $manager->toArray();
                $arr['username'] = $arr['name'] ?? '';
                $arr['inactiveAt'] = $arr['inactive_at'] ?? null;
                $arr['type'] = 'User';
                $arr['role'] = $this->resolveLinkedUserRole($manager, 'Bank', $profileId);
                $profiles[] = $arr;
            }
        }

        $this->editAttachProfile['profiles'] = $profiles;
        $this->initAttachProfile['profiles'] = $profiles;
        $this->initAttachProfile = $this->editAttachProfile;

        $this->modalAttachProfile = true;
    }

    /**
     * Resolve the current role of a linked user for a given parent profile type/id.
     * Returns 'organization-manager', 'organization-coordinator', 'bank-manager', 'bank-coordinator', or the manager default.
     */
    protected function resolveLinkedUserRole($user, string $parentType, int $parentId): string
    {
        $coordinatorMap = [
            'Organization' => 'organization-coordinator',
            'Bank'         => 'bank-coordinator',
        ];
        $managerMap = [
            'Organization' => 'organization-manager',
            'Bank'         => 'bank-manager',
        ];

        $coordinatorRole = isset($coordinatorMap[$parentType])
            ? "{$parentType}\\{$parentId}\\{$coordinatorMap[$parentType]}"
            : null;

        if ($coordinatorRole && $user->hasRole($coordinatorRole)) {
            return $coordinatorMap[$parentType];
        }

        return $managerMap[$parentType] ?? 'organization-manager';
    }

    public function selectedProfile($profile)
    {
        if ($profile) {
            $profile['role'] = null;
            $this->editAttachProfile['newProfile'] = $profile;
        } else {
            $this->editAttachProfile['newProfile'] = null;
        }

        $this->syncAttachProfileChangedState();
    }



    public function updatedReActivate()
    {
        // If reActivate is set, we assume the user wants to reactivate the profile
        if ($this->reActivate) {
            $this->editProfile['inactive_at'] = null; // Clear the inactive date
        } else {
            $this->editProfile['inactive_at'] = $this->initProfile['inactive_at']; // Restore the inactive date
        }

        $this->syncEditProfileChangedState();

    }




    public function updatedConfirmString($value)
    {
        // always assume “bad” until proven good
        $this->buttonDisabled = true;
        // explicitly grab the confirmString rules from rules()
        $ruleSet = ['confirmString' => $this->rules()['confirmString']];
        try {
            // validateOnly needs the keyed map
            $this->validateOnly('confirmString', $ruleSet);
            // if no exception, it’s good
            $this->buttonDisabled = false;
            // clear out any existing validation error for that field
            $this->resetErrorBag('confirmString');
        } catch (\Illuminate\Validation\ValidationException $e) {
            // leave it disabled
            $this->buttonDisabled = true;
        }
    }


    public function updatedEditProfile($newValue, $fieldKey) // $fieldKey is 'name', 'email' etc.
    {
        $fieldPath = 'editProfile.' . $fieldKey;

        // Handle specific field side-effects (e.g., messages)
        if ($fieldKey === 'email') {
            if ($newValue !== ($this->initProfile['email'] ?? null)) {
                $this->editProfileMessages['email'] = __('The email address will be marked as unverified.');
            } else {
                unset($this->editProfileMessages['email']);
            }
        }

        // Real-time validation for the updated field
        $rules = $this->getProcessedRulesForField($fieldPath, true);

        if (! empty($rules)) {
            // ← wrap the rules in a map keyed by the field
            $this->validateOnly($fieldPath, [
                $fieldPath => $rules
            ]);
        }

        $this->syncEditProfileChangedState();
    }

    /**
     * Recalculate the overall "changed" state.
     * This method iterates over the list of fields that can be modified.
     */
    protected function syncEditProfileChangedState(): void
    {
        $keysToCheck = [
            'inactive_at',
            'name',
            'full_name',
            'email',
            'about_short',
            'about',
            'motivation',
            'website',
            'phone',
            'comment',
            'level', // Include level for Bank models
        ];

        $this->editProfileChanged = false; // assume no changes initially

        foreach ($keysToCheck as $key) {
            // If a key does not exist in one of the arrays, consider them different.
            $current = $this->editProfile[$key] ?? null;
            $initial = $this->initProfile[$key] ?? null;
            if ($current !== $initial) {
                $this->editProfileChanged = true;
                break;
            }
        }

        // Also consider pending email suppression toggle as a change
        if (!$this->editProfileChanged && isset($this->editProfile['email_suppression_pending'])) {
            $this->editProfileChanged = true;
        }
    }


    public function updatedEditAttachProfile($newValue, $fieldKey) // $fieldKey is 'name', 'email' etc.
    {
        $fieldPath = 'editAttachProfile.' . $fieldKey;
        $rules = $this->getProcessedRulesForField($fieldPath, true);

        if (! empty($rules)) {
            $this->validateOnly($fieldPath, [
                $fieldPath => $rules
            ]);
        }

        $this->syncAttachProfileChangedState();
    }



    /**
     * Recalculate the overall "changed" state.
     * This method iterates over the list of fields that can be modified.
     */
    protected function syncAttachProfileChangedState(): void
    {
        $keysToCheck = ['comment', 'newProfile', 'profiles'];
        $this->editAttachProfileChanged = false;

        foreach ($keysToCheck as $key) {
            $current = $this->editAttachProfile[$key] ?? null;
            $initial = $this->initAttachProfile[$key] ?? null;
            if (is_array($current) || is_array($initial)) {
                if (json_encode($current) !== json_encode($initial)) {
                    $this->editAttachProfileChanged = true;
                    break;
                }
            } else {
                if ($current !== $initial) {
                    $this->editAttachProfileChanged = true;
                    break;
                }
            }
        }
    }

    /**
     * Processes and returns the validation rules for a given field path.
     * Removes the unique rule when the field has not been updated.
     *
     * @param string $fieldPath The dot-notated path to the field (e.g., 'editProfile.email').
     * @param bool $isUpdateContext Whether the rules are being processed in an update context.
     * @return array The processed validation rules for the field.
     */
    protected function getProcessedRulesForField(string $fieldPath, bool $isUpdateContext = false): array
    {
        $allBaseRules = $this->rules();
        if (!isset($allBaseRules[$fieldPath])) {
            return [];
        }

        $fieldRulesArray = $allBaseRules[$fieldPath];

        if ($isUpdateContext && isset($this->editProfile['id']) && isset($this->editProfile['model'])) {
            $profileId = $this->editProfile['id'];
            $modelKey = $this->editProfile['model'];
            $modelClassString = 'App\\Models\\' . Str::studly($modelKey);

            if (!class_exists($modelClassString)) {
                return $fieldRulesArray;
            }
            $table = (new $modelClassString())->getTable();
            $actualFieldName = Str::after($fieldPath, 'editProfile.');

            $processedRules = [];
            $uniqueRuleForCurrentTableAdded = false;

            foreach ($fieldRulesArray as $rule) {
                if (is_string($rule) && Str::startsWith(trim($rule), 'unique:')) {
                    // If it's the unique rule for the current table and field, mark it to be replaced
                    if (preg_match("/^unique:{$table},{$actualFieldName}(,|$)/", trim($rule))) {
                        // We will add the ->ignore() version later, so skip this string version
                        $uniqueRuleForCurrentTableAdded = true; // Mark that we've handled this specific one
                        continue;
                    } else {
                        // Keep unique rules for OTHER tables (cross-table uniqueness checks)
                        $processedRules[] = $rule;
                    }
                } else {
                    $processedRules[] = $rule; // Keep non-unique rules
                }
            }

            // Only add unique rule if there was a unique rule in the original rules
            if ($uniqueRuleForCurrentTableAdded) {
                $processedRules[] = Rule::unique($table, $actualFieldName)->ignore($profileId);
            }

            return array_values(array_unique($processedRules, SORT_REGULAR)); // Remove duplicates
        }
        return $fieldRulesArray;
    }


    /**
     * Update a profile
     *
     * @param  mixed $translationId
     * @return void
     */
    public function updateProfile()
    {
        // CRITICAL: Authorize admin access for updating profile
        $this->authorizeAdminAccess();

        $this->buttonDisabled = true; // Disable the button until validation is done
        $profileId = $this->editProfile['id'] ?? null;
        $modelKey = $this->editProfile['model'] ?? null;

        if (!$profileId || !$modelKey) {
            $this->notification()->error(__('Error!'), __('Profile data is incomplete.'));
            return;
        }

        if (!$this->editProfileChanged) {
            $this->notification()->info(__('No changes'), __('No changes were saved to the profile.'));
            $this->modalEditProfile = false;
            return;
        }

        $rulesForProfileFields = [];
        $allBaseRules = $this->rules(); // Get all defined base rules, including for 'confirmString'

        // Identify changed fields in editProfile and prepare their rules for validation
        foreach ($this->editProfile as $field => $currentValue) {
            if (!array_key_exists($field, $this->initProfile) || $currentValue !== $this->initProfile[$field]) {
                $fieldPath = 'editProfile.' . $field;
                if (isset($allBaseRules[$fieldPath])) {
                    $processedRules = $this->getProcessedRulesForField($fieldPath, true);
                    if (!empty($processedRules)) {
                        $rulesForProfileFields[$fieldPath] = $processedRules;
                    }
                }
            }
        }

        // Prepare all rules to be validated in one go.
        $allRulesToValidate = $rulesForProfileFields;
        if (isset($allBaseRules['confirmString'])) {
            $allRulesToValidate['confirmString'] = $allBaseRules['confirmString'];
        }
        try {
            if (empty($allRulesToValidate)) {
                $this->notification()->info(__('No Changes'), __('No changes requiring validation were made.'));
                $this->modalEditProfile = false;
                return;
            }

            $this->validate($allRulesToValidate);

            // If validation passes (including confirmString), proceed with DB transaction
            DB::transaction(function () use ($modelKey, $rulesForProfileFields) { // Pass only profile field rules for DB update logic
                $modelClass = 'App\\Models\\' . Str::studly($modelKey);
                $profile = $modelClass::find($this->editProfile['id']);

                if ($profile) {
                    $changedFields = [];

                    // Iterate over the keys for fields that were actually part of $this->editProfile and had rules
                    foreach (array_keys($rulesForProfileFields) as $validatedFieldPath) {
                        $actualFieldKey = Str::after($validatedFieldPath, 'editProfile.');
                        if (array_key_exists($actualFieldKey, $this->editProfile)) {
                            if ($actualFieldKey === 'email' && $profile->email !== $this->editProfile['email']) {
                                $profile->email_verified_at = null;
                            }
                            $profile->{$actualFieldKey} = $this->editProfile[$actualFieldKey];
                            $changedFields[] = $actualFieldKey;
                        }
                    }
                    $profile->save();

                    // Send email notification to profile owner about the changes
                    Log::info('ProfileEdited: Checking if email should be sent', [
                        'profile_id' => $profile->id,
                        'profile_type' => get_class($profile),
                        'changed_fields' => $changedFields,
                    ]);

                    $messageSetting = \App\Models\MessageSetting::where('message_settingable_id', $profile->id)
                        ->where('message_settingable_type', get_class($profile))
                        ->first();

                    $sendEmail = $messageSetting ? $messageSetting->system_message : true;

                    Log::info('ProfileEdited: Message setting check', [
                        'has_message_setting' => $messageSetting ? 'yes' : 'no',
                        'system_message' => $messageSetting ? $messageSetting->system_message : 'default:true',
                        'will_send_email' => $sendEmail ? 'yes' : 'no',
                    ]);

                    if ($sendEmail && !empty($changedFields)) {
                        \App\Jobs\SendProfileEditedByAdminMail::dispatch($profile, $changedFields);
                        Log::info('ProfileEdited: Dispatched email notification', [
                            'recipient_email' => $profile->email ?? 'NO EMAIL',
                            'changed_fields' => $changedFields,
                        ]);
                    } else {
                        Log::info('ProfileEdited: Skipped email notification', [
                            'reason' => $sendEmail ? 'no_fields_changed' : 'system_message_disabled',
                        ]);
                    }

                    // Apply pending email suppression toggle if set
                    if (isset($this->editProfile['email_suppression_pending'])) {
                        $emailToToggle = $profile->email;
                        if ($emailToToggle) {
                            if ($this->editProfile['email_suppression_pending']) {
                                \App\Models\MailingBounce::suppressEmail($emailToToggle, 'Manually suppressed by admin');
                            } else {
                                \App\Models\MailingBounce::where('email', $emailToToggle)->update(['is_suppressed' => false]);
                            }
                        }
                    }

                    $this->resetForm();
                    $this->notification()->success(__('Saved'), __('The profile has been saved successfully!'));
                    $this->modalEditProfile = false;
                } else {
                    $this->notification()->error(__('Error!'), __('Profile not found.'));
                }
            });
        } catch (\Illuminate\Validation\ValidationException $e) {
            $this->notification()->warning(__('Validation Error'), __('Please correct the errors in the form.'));
        } catch (\Exception $e) {
            $this->notification()->error(__('Error!'), __('Oops, we have an error: the profile was not saved!') . ' ' . $e->getMessage());
        }
    }



    public function toggleEmailSuppression(): void
    {
        $this->authorizeAdminAccess();

        $current = $this->editProfile['email_suppression_pending'] ?? null;

        if ($current === null) {
            // First toggle: flip the live DB state
            $email = $this->editProfile['email'] ?? null;
            $liveState = $email ? \App\Models\MailingBounce::isSuppressed($email) : false;
            $this->editProfile['email_suppression_pending'] = !$liveState;
        } else {
            // Subsequent toggles: flip the pending state
            $this->editProfile['email_suppression_pending'] = !$current;
        }

        $this->syncEditProfileChangedState();
    }

    public function openDeleteProfileModal($profileId, $modelName)
    {
        $this->adminPassword = '';
        $this->balanceHandlingOption = 'delete';
        $this->donationAccountId = null;

        $profile = $modelName::find($profileId);

        if (!$profile) {
            $this->notification()->error(__('Error!'), __('Profile not found.'));
            return;
        }

        $this->selectedProfile = $profile;
        $this->selectedProfileId = $profileId;

        // Load account information
        $accounts = collect();
        $totalBalance = 0;
        $hasNegativeBalance = false;

        if (method_exists($profile, 'accounts')) {
            $userAccounts = $profile->accounts()->active()->notRemoved()->get();

            foreach ($userAccounts as $account) {
                \Cache::forget("account_balance_{$account->id}");
                $balance = $account->balance;

                $accounts->push([
                    'id' => $account->id,
                    'name' => __('messages.' . $account->name . '_account'),
                    'balance' => $balance,
                    'balanceFormatted' => tbFormat($balance),
                ]);

                $totalBalance += $balance;

                if ($balance < 0) {
                    $hasNegativeBalance = true;
                }
            }
        }

        // Check if central bank
        $isCentralBank = ($profile instanceof \App\Models\Bank && isset($profile->level) && $profile->level == 0);

        // Check if final admin
        $isFinalAdmin = false;
        if ($profile instanceof \App\Models\Admin) {
            $activeAdminCount = \App\Models\Admin::whereNull('deleted_at')->count();
            if ($activeAdminCount <= 1) {
                $isFinalAdmin = true;
            }
        }

        $this->deleteProfileData = [
            'name' => $profile->name,
            'full_name' => $profile->full_name ?? '',
            'email' => $profile->email,
            'type' => class_basename($profile),
            'accounts' => $accounts->toArray(),
            'totalBalance' => $totalBalance,
            'hasNegativeBalance' => $hasNegativeBalance,
            'isCentralBank' => $isCentralBank,
            'isFinalAdmin' => $isFinalAdmin,
            'showBalanceOptions' => timebank_config('delete_profile.account_balances.donate_balances_to_organization_account_specified', false),
        ];

        $this->modalDeleteProfile = true;
    }

    public function openRestoreProfileModal($profileId, $modelName)
    {
        $this->adminPassword = '';

        // Models don't use SoftDeletes trait, so we can query directly
        $profile = $modelName::find($profileId);

        if (!$profile) {
            $this->notification()->error(__('Error!'), __('Profile not found.'));
            return;
        }

        if (!$profile->deleted_at) {
            $this->notification()->error(__('Error!'), __('Profile is not deleted.'));
            return;
        }

        // Check if profile can be restored
        if (!$this->isProfileRestorable($profile)) {
            $this->notification()->error(__('Error!'), __('Profile permanently deleted - cannot be restored'));
            return;
        }

        $this->selectedProfile = $profile;
        $this->selectedProfileId = $profileId;

        // Calculate grace period information
        $gracePeriodDays = timebank_config('delete_profile.grace_period_days', 30);
        $deletedAt = \Carbon\Carbon::parse($profile->deleted_at);
        $gracePeriodExpiry = $deletedAt->copy()->addDays($gracePeriodDays);
        $now = \Carbon\Carbon::now();

        // Calculate time remaining
        $secondsRemaining = $now->diffInSeconds($gracePeriodExpiry, false);

        if ($secondsRemaining <= 0) {
            $timeRemaining = __('Expired');
        } elseif ($secondsRemaining >= 86400 * 7) {
            $weeks = round($secondsRemaining / (86400 * 7));
            $timeRemaining = trans_choice('weeks_remaining', $weeks, ['count' => $weeks]);
        } elseif ($secondsRemaining >= 86400) {
            $days = round($secondsRemaining / 86400);
            $timeRemaining = trans_choice('days_remaining', $days, ['count' => $days]);
        } elseif ($secondsRemaining >= 3600) {
            $hours = round($secondsRemaining / 3600);
            $timeRemaining = trans_choice('hours_remaining', $hours, ['count' => $hours]);
        } else {
            $minutes = max(1, round($secondsRemaining / 60));
            $timeRemaining = trans_choice('minutes_remaining', $minutes, ['count' => $minutes]);
        }

        $this->restoreProfileData = [
            'name' => $profile->name,
            'full_name' => $profile->full_name ?? '',
            'email' => $profile->email,
            'type' => class_basename($profile),
            'deletedAt' => $deletedAt->translatedFormat('j F Y, H:i'),
            'gracePeriodExpiry' => $gracePeriodExpiry->translatedFormat('j F Y, H:i'),
            'timeRemaining' => $timeRemaining,
        ];

        $this->modalRestoreProfile = true;
    }


    /**
     * Attach a profile
     *
     * @param  mixed $translationId
     * @return void
     */
    public function attachProfile()
    {
        // CRITICAL: Authorize admin access for attaching profile
        $this->authorizeAdminAccess();

        $this->buttonDisabled = true;
        $profileId = $this->editAttachProfile['id'] ?? null;
        $modelKey = $this->editAttachProfile['model'] ?? null;

        if (!$profileId || !$modelKey) {
            $this->notification()->error(__('Error!'), __('Profile data is incomplete.'));
            return;
        }

        if (!$this->editAttachProfileChanged) {
            $this->notification()->info(__('No changes'), __('No changes were saved to the profile.'));
            $this->modalAttachProfile = false;
            return;
        }

        // --- ALWAYS add newProfile to profiles array before validation/sync ---
        if (!empty($this->editAttachProfile['newProfile'])) {
            $selectedProfile = $this->editAttachProfile['newProfile'];
            $selectedProfileId = $selectedProfile['id'] ?? null;
            $selectedProfileModel = class_basename($selectedProfile['type'] ?? '');

            if (!$profileId || !$modelKey || !$selectedProfileId || !$selectedProfileModel) {
                $this->notification()->error(__('Error!'), __('Profile data is incomplete!'));
                return;
            }

            // Validate role selection before proceeding
            $roleValue = $selectedProfile['role'] ?? null;
            $roleRequired = !in_array($selectedProfileModel, ['User', 'Admin']);
            if ($roleRequired && empty($roleValue)) {
                $this->addError('editAttachProfile.newProfile.role', __('A role is required for the selected profile.'));
                $this->buttonDisabled = false;
                return;
            }

            $alreadyExists = collect($this->editAttachProfile['profiles'] ?? [])
                ->contains(function ($profile) use ($selectedProfileId, $selectedProfileModel) {
                    return $profile['id'] == $selectedProfileId && $profile['type'] == $selectedProfileModel;
                });

            if (!$alreadyExists) {
                $this->editAttachProfile['profiles'][] = [
                    'id'   => $selectedProfileId,
                    'type' => $selectedProfileModel,
                    'role' => $roleValue,
                ];
            }

            $this->editAttachProfile['newProfile'] = null;
        }

        $rulesForProfileFields = [];
        $allBaseRules = $this->rules();

        foreach ($this->editAttachProfile as $field => $currentValue) {
            if (!array_key_exists($field, $this->initAttachProfile) || $currentValue !== $this->initAttachProfile[$field]) {
                $fieldPath = 'editAttachProfile.' . $field;
                if (isset($allBaseRules[$fieldPath])) {
                    $processedRules = $this->getProcessedRulesForField($fieldPath, true);
                    if (!empty($processedRules)) {
                        $rulesForProfileFields[$fieldPath] = $processedRules;
                    }
                }
            }
        }

        $allRulesToValidate = $rulesForProfileFields;
        if (isset($allBaseRules['confirmString'])) {
            $allRulesToValidate['confirmString'] = $allBaseRules['confirmString'];
        }

        try {
            if (empty($allRulesToValidate)) {
                $this->notification()->info(__('No Changes'), __('No changes requiring validation were made.'));
                $this->modalAttachProfile = false;
                return;
            }

            $this->validate($allRulesToValidate);

            DB::transaction(function () use ($modelKey, $rulesForProfileFields) {
                $modelClass = 'App\\Models\\' . Str::studly($modelKey);
                $profile = $modelClass::find($this->editAttachProfile['id']);

                if ($profile) {
                    foreach (array_keys($rulesForProfileFields) as $validatedFieldPath) {
                        $actualFieldKey = Str::after($validatedFieldPath, 'editAttachProfile.');
                        if (array_key_exists($actualFieldKey, $this->editAttachProfile)) {
                            $profile->{$actualFieldKey} = $this->editAttachProfile[$actualFieldKey];
                        }
                    }

                    $comment = $this->editAttachProfile['comment'] ?? null;
                    $profile->comment = $comment;
                    $profile->save();

                    $relationMap = [
                        'user' => [
                            'Organization' => 'organizations',
                            'Bank'         => 'banksManaged',
                            'Admin'        => 'admins',
                        ],
                        'organization' => [
                            'User' => 'users',
                        ],
                        'bank' => [
                            'User' => 'managers',
                        ],
                        'admin' => [
                            'User' => 'users',
                        ],
                    ];

                    $roleMap = [
                        'Organization' => ['organization-manager'],
                        'Bank' => ['bank-manager'],
                        'Admin' => ['admin'],
                    ];

                    // Allowed roles per profile type (for validation).
                    // Admin is intentionally excluded: admins always receive the 'admin' role
                    // regardless of any client-submitted role value (safe by design).
                    $allowedRolesMap = [
                        'Organization' => ['organization-manager', 'organization-coordinator'],
                        'Bank'         => ['bank-manager', 'bank-coordinator'],
                    ];

                    $currentProfiles = collect($this->editAttachProfile['profiles'] ?? [])
                    ->reject(fn($profile) => !empty($profile['removed']))
                    ->values()
                    ->all();
                    $initialProfiles = $this->initAttachProfile['profiles'] ?? [];
                    $modelRelations = $relationMap[$modelKey] ?? [];

                    $roles = [];

                    foreach ($modelRelations as $type => $relation) {
                        $currentIds = $this->getIdsByType($currentProfiles, $type);

                        $profile->{$relation}()->sync($currentIds); // Sync attached profiles

                        if ($profile instanceof User) {
                            // For each attached profile, create and assign a profile-specific role
                            foreach ($currentIds as $attachedId) {
                                if (isset($roleMap[$type])) {
                                    // Read the role selected in the modal for this linked profile entry
                                    $profileEntry = collect($currentProfiles)
                                        ->first(fn($p) => $p['id'] == $attachedId && $p['type'] === $type);
                                    $selectedRole = $profileEntry['role'] ?? null;

                                    if (isset($allowedRolesMap[$type]) && in_array($selectedRole, $allowedRolesMap[$type])) {
                                        $baseRole = $selectedRole;
                                    } else {
                                        $baseRole = $roleMap[$type][0]; // default: manager
                                    }

                                    // Remove any previous coordinator/manager role before assigning new one
                                    if (isset($allowedRolesMap[$type])) {
                                        foreach ($allowedRolesMap[$type] as $oldBase) {
                                            $oldRoleName = "{$type}\\{$attachedId}\\{$oldBase}";
                                            if ($profile->hasRole($oldRoleName)) {
                                                $profile->removeRole($oldRoleName);
                                            }
                                        }
                                    }

                                    $roleName = "{$type}\\{$attachedId}\\{$baseRole}";
                                    $role = Role::findOrCreate($roleName, 'web');
                                    $permissionName = 'manage ' . strtolower($type) . 's';
                                    $role->givePermissionTo($permissionName);
                                    $roles[] = $roleName;
                                    if ($baseRole === 'admin') {
                                        $role->syncPermissions(\Spatie\Permission\Models\Permission::all());
                                    }
                                }
                            }
                        } else {
                            // $profile is Organization, Bank, or Admin
                            $parentType = class_basename($profile); // e.g. 'Organization'
                            $parentId = $profile->id;

                            foreach ($currentIds as $attachedUserId) {
                                $user = \App\Models\User::find($attachedUserId);
                                if ($user) {
                                    // Determine selected role from profile entry, fall back to manager default
                                    $profileEntry = collect($currentProfiles)
                                        ->first(fn($p) => $p['id'] == $attachedUserId && $p['type'] === 'User');
                                    $selectedRole = $profileEntry['role'] ?? null;

                                    if (isset($allowedRolesMap[$parentType]) && in_array($selectedRole, $allowedRolesMap[$parentType])) {
                                        $baseRole = $selectedRole;
                                    } else {
                                        $baseRole = $roleMap[$parentType][0] ?? 'organization-manager';
                                    }

                                    // Remove any previous coordinator/manager role for this user+profile before assigning new one
                                    if (isset($allowedRolesMap[$parentType])) {
                                        foreach ($allowedRolesMap[$parentType] as $oldBase) {
                                            $oldRoleName = "{$parentType}\\{$parentId}\\{$oldBase}";
                                            if ($user->hasRole($oldRoleName)) {
                                                $user->removeRole($oldRoleName);
                                            }
                                        }
                                    }

                                    $roleName = "{$parentType}\\{$parentId}\\{$baseRole}";
                                    $role = Role::findOrCreate($roleName, 'web');
                                    $permissionName = 'manage ' . strtolower($parentType) . 's';
                                    $role->givePermissionTo($permissionName);
                                    if ($baseRole === 'admin') {
                                        $role->syncPermissions(\Spatie\Permission\Models\Permission::all());
                                    }
                                    // Assign the role to the user
                                    $user->assignRole($roleName);
                                }
                            }
                        }
                    }

                    // --- Also assign / revoke / clean-up roles when $profile is a User --- //
                    // Remove duplicate roles
                    $roles = array_unique($roles);

                    // Remember roles that were previously assigned to this profile
                    $previousRoles = $profile->roles->pluck('name')->toArray();

                    if ($profile instanceof User) {
                        $user = $profile;
                        $willHavePermission = false;

                        // Test: Will the user have 'manage profiles' after sync?
                        // 1. Get all permissions from the roles that will be assigned
                        $newRoles = Role::whereIn('name', $roles)->get();
                        foreach ($newRoles as $role) {
                            if ($role->hasPermissionTo('manage profiles', 'web')) {
                                $willHavePermission = true;
                                break;
                            }
                        }

                        // 2. If user currently has permission but will lose it, abort
                        if ($user->hasPermissionTo('manage profiles', 'web') && !$willHavePermission && $profile->id === Auth::guard('web')->user()->id) {
                            throw new \Exception(__('This action would remove your own "manage profiles" permission. Login with another account that can manage profiles and try again.'));
                        }

                        // Now sync roles
                        $profile->syncRoles($roles);
                    }

                    // 1. Find detached profiles (present in initialProfiles but not in currentProfiles)
                    $detachedProfiles = collect($initialProfiles)
                        ->reject(function ($prev) use ($currentProfiles) {
                            return collect($currentProfiles)->contains(function ($curr) use ($prev) {
                                return $curr['id'] == $prev['id'] && $curr['type'] == $prev['type'];
                            });
                        })
                        ->values()
                        ->all();

                    // 2. Remove the corresponding role from each detached profile
                        $parentType = class_basename($profile); // e.g. 'Organization'
                        $parentId = $profile->id;

                        // All possible base roles per type (manager + coordinator)
                        $allRolesMap = [
                            'Organization' => ['organization-manager', 'organization-coordinator'],
                            'Bank'         => ['bank-manager', 'bank-coordinator'],
                            'Admin'        => ['admin'],
                        ];

                        foreach ($detachedProfiles as $detached) {
                            // Only process if detached profile is a User
                            if (($detached['type'] ?? '') === 'User') {
                                $user = \App\Models\User::find($detached['id']);
                                if ($user && isset($allRolesMap[$parentType])) {
                                    foreach ($allRolesMap[$parentType] as $baseRole) {
                                        $roleName = "{$parentType}\\{$parentId}\\{$baseRole}";
                                        $user->removeRole($roleName);
                                        $role = Role::where('name', $roleName)->first();
                                        if ($role && $role->users()->count() === 0) {
                                            $role->delete();
                                        }
                                    }
                                }
                            }
                        }

                    // 3. Find roles that were removed and delete any stray roles that do not have a related profile
                    $removedRoles = array_diff($previousRoles, $roles);
                    foreach ($removedRoles as $roleName) {
                        $role = Role::where('name', $roleName)->first();
                        if ($role && $role->users()->count() === 0) {
                            $role->delete();
                        }
                    }

                    // Send email notifications for profile link changes
                    Log::info('ProfileLinkChanged: Starting email notification process', [
                        'profile_id' => $profile->id,
                        'profile_type' => get_class($profile),
                        'profile_name' => $profile->name,
                    ]);

                    // 1. Find newly attached profile(s)
                    $newlyAttached = [];
                    foreach ($modelRelations as $type => $relation) {
                        $currentIds = $this->getIdsByType($currentProfiles, $type);
                        $initialIds = $this->getIdsByType($initialProfiles, $type);

                        // Find IDs that are in current but not in initial (i.e., newly attached)
                        $addedIds = array_diff($currentIds, $initialIds);

                        foreach ($addedIds as $addedId) {
                            $newlyAttached[] = [
                                'id' => $addedId,
                                'type' => $type,
                            ];
                        }
                    }

                    Log::info('ProfileLinkChanged: Found newly attached profiles', [
                        'count' => count($newlyAttached),
                        'attached' => $newlyAttached,
                    ]);

                    // 2. Send emails for newly attached profiles
                    foreach ($newlyAttached as $attached) {
                        $attachedModelClass = 'App\\Models\\' . $attached['type'];
                        $attachedProfile = $attachedModelClass::find($attached['id']);

                        if ($attachedProfile) {
                            Log::info('ProfileLinkChanged: Processing attached profile', [
                                'attached_profile_id' => $attachedProfile->id,
                                'attached_profile_type' => get_class($attachedProfile),
                                'attached_profile_name' => $attachedProfile->name,
                                'attached_profile_email' => $attachedProfile->email ?? 'NO EMAIL',
                            ]);

                            // Send email to the attached profile
                            $messageSetting = \App\Models\MessageSetting::where('message_settingable_id', $attachedProfile->id)
                                ->where('message_settingable_type', get_class($attachedProfile))
                                ->first();

                            $sendEmail = $messageSetting ? $messageSetting->system_message : true;

                            Log::info('ProfileLinkChanged: Attached profile message setting check', [
                                'has_message_setting' => $messageSetting ? 'yes' : 'no',
                                'system_message' => $messageSetting ? $messageSetting->system_message : 'default:true',
                                'will_send_email' => $sendEmail ? 'yes' : 'no',
                            ]);

                            if ($sendEmail) {
                                \App\Jobs\SendProfileLinkChangedMail::dispatch($attachedProfile, $profile, 'attached');
                                Log::info('ProfileLinkChanged: Dispatched email to attached profile', [
                                    'recipient_email' => $attachedProfile->email,
                                    'linked_profile_name' => $profile->name,
                                ]);
                            } else {
                                Log::info('ProfileLinkChanged: Skipped email to attached profile (system_message disabled)');
                            }

                            // Send email to the main profile
                            $profileMessageSetting = \App\Models\MessageSetting::where('message_settingable_id', $profile->id)
                                ->where('message_settingable_type', get_class($profile))
                                ->first();

                            $sendProfileEmail = $profileMessageSetting ? $profileMessageSetting->system_message : true;

                            Log::info('ProfileLinkChanged: Main profile message setting check', [
                                'has_message_setting' => $profileMessageSetting ? 'yes' : 'no',
                                'system_message' => $profileMessageSetting ? $profileMessageSetting->system_message : 'default:true',
                                'will_send_email' => $sendProfileEmail ? 'yes' : 'no',
                            ]);

                            if ($sendProfileEmail) {
                                \App\Jobs\SendProfileLinkChangedMail::dispatch($profile, $attachedProfile, 'attached');
                                Log::info('ProfileLinkChanged: Dispatched email to main profile', [
                                    'recipient_email' => $profile->email ?? 'NO EMAIL',
                                    'linked_profile_name' => $attachedProfile->name,
                                ]);
                            } else {
                                Log::info('ProfileLinkChanged: Skipped email to main profile (system_message disabled)');
                            }
                        } else {
                            Log::warning('ProfileLinkChanged: Attached profile not found', [
                                'attached_id' => $attached['id'],
                                'attached_type' => $attached['type'],
                            ]);
                        }
                    }

                    // 3. Send emails for detached profiles
                    Log::info('ProfileLinkChanged: Found detached profiles', [
                        'count' => count($detachedProfiles),
                        'detached' => $detachedProfiles,
                    ]);

                    foreach ($detachedProfiles as $detached) {
                        $detachedModelClass = 'App\\Models\\' . $detached['type'];
                        $detachedProfile = $detachedModelClass::find($detached['id']);

                        if ($detachedProfile) {
                            Log::info('ProfileLinkChanged: Processing detached profile', [
                                'detached_profile_id' => $detachedProfile->id,
                                'detached_profile_type' => get_class($detachedProfile),
                                'detached_profile_name' => $detachedProfile->name,
                                'detached_profile_email' => $detachedProfile->email ?? 'NO EMAIL',
                            ]);

                            // Send email to the detached profile
                            $messageSetting = \App\Models\MessageSetting::where('message_settingable_id', $detachedProfile->id)
                                ->where('message_settingable_type', get_class($detachedProfile))
                                ->first();

                            $sendEmail = $messageSetting ? $messageSetting->system_message : true;

                            Log::info('ProfileLinkChanged: Detached profile message setting check', [
                                'has_message_setting' => $messageSetting ? 'yes' : 'no',
                                'system_message' => $messageSetting ? $messageSetting->system_message : 'default:true',
                                'will_send_email' => $sendEmail ? 'yes' : 'no',
                            ]);

                            if ($sendEmail) {
                                \App\Jobs\SendProfileLinkChangedMail::dispatch($detachedProfile, $profile, 'detached');
                                Log::info('ProfileLinkChanged: Dispatched email to detached profile', [
                                    'recipient_email' => $detachedProfile->email,
                                    'linked_profile_name' => $profile->name,
                                ]);
                            } else {
                                Log::info('ProfileLinkChanged: Skipped email to detached profile (system_message disabled)');
                            }

                            // Send email to the main profile
                            $profileMessageSetting = \App\Models\MessageSetting::where('message_settingable_id', $profile->id)
                                ->where('message_settingable_type', get_class($profile))
                                ->first();

                            $sendProfileEmail = $profileMessageSetting ? $profileMessageSetting->system_message : true;

                            Log::info('ProfileLinkChanged: Main profile message setting check (detached)', [
                                'has_message_setting' => $profileMessageSetting ? 'yes' : 'no',
                                'system_message' => $profileMessageSetting ? $profileMessageSetting->system_message : 'default:true',
                                'will_send_email' => $sendProfileEmail ? 'yes' : 'no',
                            ]);

                            if ($sendProfileEmail) {
                                \App\Jobs\SendProfileLinkChangedMail::dispatch($profile, $detachedProfile, 'detached');
                                Log::info('ProfileLinkChanged: Dispatched email to main profile (detached)', [
                                    'recipient_email' => $profile->email ?? 'NO EMAIL',
                                    'unlinked_profile_name' => $detachedProfile->name,
                                ]);
                            } else {
                                Log::info('ProfileLinkChanged: Skipped email to main profile (system_message disabled)');
                            }
                        } else {
                            Log::warning('ProfileLinkChanged: Detached profile not found', [
                                'detached_id' => $detached['id'],
                                'detached_type' => $detached['type'],
                            ]);
                        }
                    }

                    $this->resetForm();
                    $this->notification()->success(__('Saved'), __('The profile has been saved successfully!'));

                    // Uncomment to send wirechat message to the new Profile when it is added to the $profile
                    // This block is commented because currently no other models than User could chat with chat.

                    // // 1. Find newly attached profile(s)
                    // $newlyAttached = [];
                    // foreach ($modelRelations as $type => $relation) {
                    //     $currentIds = $this->getIdsByType($currentProfiles, $type);
                    //     $initialIds = $this->getIdsByType($initialProfiles, $type);

                    //     // Find IDs that are in current but not in initial (i.e., newly attached)
                    //     $addedIds = array_diff($currentIds, $initialIds);

                    //     foreach ($addedIds as $addedId) {
                    //         $newlyAttached[] = [
                    //             'id' => $addedId,
                    //             'type' => $type,
                    //         ];
                    //     }
                    // }

                    // // 2. Get the model instance for the first newly attached profile (if any)
                    // if (!empty($newlyAttached)) {
                    //     $recipientInfo = $newlyAttached[0];
                    //     $recipientModelClass = 'App\\Models\\' . $recipientInfo['type'];
                    //     $recipient = $recipientModelClass::find($recipientInfo['id']);

                    //     if ($recipient) {
                    //         $sender = $profile;
                    //         $messageLocale = $recipient->lang_preference ?? $sender->lang_preference;
                    //         if (!Lang::has('messages.manage_profiles.attached_profile_chat_message', $messageLocale)) {
                    //             $messageLocale = config('app.fallback_locale');
                    //         }

                    //         $chatMessage = __('messages.manage_profiles.attached_profile_chat_message', [], $messageLocale);

                    //         // Send Wirechat message
                    //         $message = $sender->sendMessageTo($recipient, $chatMessage);

                    //         // Broadcast the NotifyParticipant event to wirechat messenger
                    //         broadcast(new NotifyParticipant($recipient, $message));
                    //     }
                    // }


                    $this->modalAttachProfile = false;
                } else {
                    $this->notification()->error(__('Error!'), __('Profile not found.'));
                }
            });
        } catch (\Illuminate\Validation\ValidationException $e) {
            $this->notification()->warning(__('Validation Error'), __('Please correct the errors in the form.'));
        } catch (\Exception $e) {
            $this->notification()->error(__('Error!'), __('Oops, we have an error: the profile was not saved!') . ' ' . $e->getMessage());
        }
    }

    // Helper function for attachProfile method
    private function getIdsByType($profiles, $type)
    {
        return collect($profiles)
            ->where('type', $type)
            ->pluck('id')
            ->map(fn($id) => (int)$id)
            ->unique()
            ->values()
            ->toArray();
    }

    public function removeAttachedProfile($id, $type)
    {
        $this->editAttachProfile['profiles'] = array_map(function ($profile) use ($id, $type) {
            if (
                isset($profile['id'], $profile['type']) &&
                $profile['id'] == $id &&
                strtolower($profile['type']) == strtolower($type)
            ) {
                $profile['removed'] = true; // Mark as removed
            }
            return $profile;
        }, $this->editAttachProfile['profiles']);

        $this->syncAttachProfileChangedState();
    }



    /**
     * Delete the tag
     *
     * @param  mixed $translationId
     * @return void
     */
    public function deleteProfile()
    {
        // CRITICAL: Authorize admin access for deleting profile
        $this->authorizeAdminAccess();

        $this->validate([
            'adminPassword' => ['required', 'string'],
        ]);

        // Check if donation would exceed limits
        if ($this->balanceHandlingOption === 'donate' && $this->donationExceedsLimit) {
            $this->addError('donationAccountId', $this->donationLimitError ?? __('The selected organization account cannot receive this donation amount.'));
            return;
        }

        try {
            $profile = $this->selectedProfile;

            if (!$profile) {
                throw new \Exception('Profile not found.');
            }

            // Verify admin password
            $admin = getActiveProfile();
            if (!$admin instanceof \App\Models\Admin) {
                throw new \Exception('Only administrators can delete profiles.');
            }

            if (!\Hash::check($this->adminPassword, $admin->password)) {
                $this->addError('adminPassword', __('This password does not match our records.'));
                return;
            }

            // Get the profile's updated_at timestamp for the email
            $profileTable = $profile->getTable();
            $time = DB::table($profileTable)
                    ->where('id', $profile->id)
                    ->pluck('updated_at')
                    ->first();
            $time = \Carbon\Carbon::parse($time);

            // Get the admin username who is deleting the profile
            $deletedByUsername = \Auth::guard('web')->user()->name ?? null;

            // Use the DeleteUser action
            $deleter = app(\Laravel\Jetstream\Contracts\DeletesUsers::class);
            $result = $deleter->delete(
                $profile->fresh(),
                $this->balanceHandlingOption,
                $this->donationAccountId,
                false, // isAutoDeleted = false
                $deletedByUsername // admin username
            );

            if ($result['status'] === 'success') {
                // Prepare email data
                $result['time'] = $time->translatedFormat('j F Y, H:i');
                $result['deletedUser'] = $profile;
                $result['mail'] = $profile->email;
                $result['balanceHandlingOption'] = $this->balanceHandlingOption;
                $result['totalBalance'] = $this->deleteProfileData['totalBalance'] ?? 0;
                $result['donationAccountId'] = $this->donationAccountId;

                // Get donation account details if donated
                if ($this->balanceHandlingOption === 'donate' && $this->donationAccountId) {
                    $donationAccount = \App\Models\Account::find($this->donationAccountId);
                    if ($donationAccount && $donationAccount->accountable) {
                        $result['donationAccountName'] = $donationAccount->name;
                        $result['donationOrganizationName'] = $donationAccount->accountable->name;
                    }
                }

                // Send confirmation email
                Log::notice('Profile deleted by admin: ' . $profile->name);
                \Mail::to($profile->email)->queue(new \App\Mail\UserDeletedMail($result));

                $this->notification()->success(
                    __('Deleted'),
                    __('Profile was deleted successfully!')
                );

                $this->resetForm();
                $this->resetPage();
                $this->dispatch('scroll-to-top');
            } else {
                $this->notification()->error(
                    __('Error!'),
                    __('There was an error deleting the profile: ') . $result['message']
                );
            }

        } catch (\Exception $e) {
            $this->notification()->error(
                __('Error!'),
                __('Could not delete the profile: ') . $e->getMessage()
            );
            Log::error('Admin profile deletion failed', [
                'profile_id' => $this->selectedProfileId,
                'error' => $e->getMessage()
            ]);
        }

        $this->modalDeleteProfile = false;
    }

    /**
     * Restore a deleted profile
     *
     * @return void
     */
    public function restoreProfile()
    {
        // CRITICAL: Authorize admin access for restoring profile
        $this->authorizeAdminAccess();

        $this->validate([
            'adminPassword' => ['required', 'string'],
        ]);

        try {
            $profile = $this->selectedProfile;

            if (!$profile) {
                throw new \Exception('Profile not found.');
            }

            // Verify admin password
            $admin = getActiveProfile();
            if (!$admin instanceof \App\Models\Admin) {
                throw new \Exception('Only administrators can restore profiles.');
            }

            if (!\Hash::check($this->adminPassword, $admin->password)) {
                $this->addError('adminPassword', __('This password does not match our records.'));
                return;
            }

            // Use the RestoreProfile action
            $restorer = new \App\Actions\Jetstream\RestoreProfile();
            $result = $restorer->restore($profile->fresh());

            if ($result['status'] === 'success') {
                $this->notification()->success(
                    __('Restored'),
                    __('Profile was restored successfully!')
                );

                Log::notice('Profile restored by admin: ' . $profile->name, [
                    'profile_id' => $profile->id,
                    'profile_type' => get_class($profile),
                    'admin_id' => $admin->id,
                    'admin_name' => $admin->name,
                ]);

                $this->resetForm();
                $this->resetPage();
                $this->dispatch('scroll-to-top');
            } else {
                $this->notification()->error(
                    __('Error!'),
                    __('There was an error restoring the profile: ') . $result['message']
                );
            }

        } catch (\Exception $e) {
            $this->notification()->error(
                __('Error!'),
                __('Could not restore the profile: ') . $e->getMessage()
            );
            Log::error('Admin profile restoration failed', [
                'profile_id' => $this->selectedProfileId,
                'error' => $e->getMessage()
            ]);
        }

        $this->modalRestoreProfile = false;
    }

    public function deleteSelected()
    {
        // CRITICAL: Authorize admin access for bulk deleting profiles
        $this->authorizeAdminAccess();

        $this->validateOnly('confirmString');
        try {
            $tags = User::whereIn('id', $this->bulkSelected);
            if ($tags) {
                $tags->delete();
                $this->notification()->success(
                    $title = __('Deleted'),
                    $description = __('Selected tags were deleted successfully!')
                );
            } else {
                $this->notification()->error(
                    $title = __('Error!'),
                    $description = __('The selected tags were not found.')
                );
            }
        } catch (\Exception $e) {
            $this->notification()->error(
                $title = __('Error!'),
                $description = __('Oops, could not delete the selected tags') . '!' . $e->getMessage()
            );
        }
        $this->resetPage();
        $this->dispatch('scroll-to-top');
        $this->confirmString = '';
    }



    public function resetForm()
    {
        $this->reset([
            'selectedProfileId',
            'selectedProfile',
            'modalEditProfile',
            'modalAttachProfile',
            'modalEditAccounts',
            'modalDeleteProfile',
            'modalRestoreProfile',
            'initProfile',
            'initAttachProfile',
            'initAccounts',
            'editProfile',
            'editAttachProfile',
            'editAccounts',
            'editProfileChanged',
            'editAttachProfileChanged',
            'editAccountsChanged',
            'editProfileMessages',
            'editAccountsMessages',
            'confirmString',
            'buttonDisabled',
            'reActivate',
            'deleteProfileData',
            'restoreProfileData',
            'adminPassword',
            'balanceHandlingOption',
            'donationAccountId',
            'donationExceedsLimit',
            'donationLimitError',
        ]);

        $this->resetErrorBag();
    }


    public function sortBy(string $field): void
    {
        if ($this->sortField === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortDirection = 'asc';
        }

        $this->sortField = $field;
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }



    public function searchProfiles()
    {
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }


    public function handleSearchEnter()
    {
        if (!$this->showModal) {
            $this->searchProfiles();
        }
    }

    public function updatingSearch()
    {
        $this->resetPage();
    }


    public function resetSearch()
    {
        $this->search = '';
        $this->searchProfiles();
        $this->dispatch('scroll-to-top');
    }


    public function updatedPage()
    {
        $this->dispatch('scroll-to-top');
    }


    public function updatingPerPage()
    {
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }

    public function updatedTypeFilter()
    {
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }

    public function updatedActiveFilter()
    {
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }

    public function updatedEmailVerifiedFilter()
    {
        $this->resetPage();
        $this->dispatch('scroll-to-top');
    }

    /**
     * Get available profile type options for the filter dropdown.
     *
     * @return array
     */
    public function getTypeOptionsProperty(): array
    {
        return [
            ['id' => 'user', 'name' => __('User')],
            ['id' => 'organization', 'name' => __('Organization')],
            ['id' => 'bank', 'name' => __('Bank')],
            ['id' => 'admin', 'name' => __('Admin')],
        ];
    }

    /**
     * Get available active status options for the filter dropdown.
     *
     * @return array
     */
    public function getActiveOptionsProperty(): array
    {
        return [
            ['id' => 'active', 'name' => __('Active')],
            ['id' => 'inactive', 'name' => __('Inactive')],
            ['id' => 'deleted', 'name' => __('Deleted')],
        ];
    }

    /**
     * Get available email verification options for the filter dropdown.
     *
     * @return array
     */
    public function getEmailVerifiedOptionsProperty(): array
    {
        return [
            ['id' => 'verified', 'name' => __('Verified')],
            ['id' => 'unverified', 'name' => __('Not verified')],
            ['id' => 'blocked', 'name' => __('Blocked')],
        ];
    }

    /**
     * Check if a deleted profile can be restored.
     *
     * @param  mixed  $profile
     * @return bool
     */
    protected function isProfileRestorable($profile)
    {
        // If profile is not deleted, it can't be restored
        if (!$profile->deleted_at) {
            return false;
        }

        // Check if profile has been permanently deleted (anonymized email)
        if (str_starts_with($profile->email, 'removed-') && str_ends_with($profile->email, '@remove.ed')) {
            return false;
        }

        // Check if password is empty (permanently deleted)
        if (empty($profile->password)) {
            return false;
        }

        // Check if grace period has expired
        $gracePeriodDays = timebank_config('delete_profile.grace_period_days', 30);
        $gracePeriodExpiry = \Carbon\Carbon::parse($profile->deleted_at)->addDays($gracePeriodDays);

        if (now()->isAfter($gracePeriodExpiry)) {
            return false;
        }

        return true;
    }


    public function render()
    {
        // --- 1. Define Base Queries ---
        $usersQuery = User::query()
            ->select('id', 'name', 'full_name', 'email', 'password', 'profile_photo_path', 'comment', 'inactive_at', 'email_verified_at', 'last_login_at', 'deleted_at', 'created_at', 'updated_at');

        $organizationsQuery = Organization::query()
            ->select('id', 'name', 'full_name', 'email', 'password', 'profile_photo_path', 'comment', 'inactive_at', 'email_verified_at', 'last_login_at', 'deleted_at', 'created_at', 'updated_at');

        $banksQuery = Bank::query()
            ->select('id', 'name', 'full_name', 'email', 'password', 'profile_photo_path', 'comment', 'inactive_at', 'email_verified_at', 'last_login_at', 'deleted_at', 'created_at', 'updated_at');

        $adminsQuery = Admin::query()
            ->select('id', 'name', 'full_name', 'email', 'password', 'profile_photo_path', 'comment', 'inactive_at', 'email_verified_at', 'last_login_at', 'deleted_at', 'created_at', 'updated_at');

        // --- 2. Apply Search ---
        if ($this->search) {
            $searchTerm = '%' . $this->search . '%';
            $usersQuery->where(function (Builder $query) use ($searchTerm) {
                $query->where('name', 'like', $searchTerm)
                    ->orWhere('full_name', 'like', $searchTerm)
                    ->orWhere('email', 'like', $searchTerm)
                    ->orWhere('comment', 'like', $searchTerm)
                    ->orWhere('id', 'like', $searchTerm);
            });
            $organizationsQuery->where(function (Builder $query) use ($searchTerm) {
                $query->where('name', 'like', $searchTerm)
                    ->orWhere('full_name', 'like', $searchTerm)
                    ->orWhere('email', 'like', $searchTerm)
                    ->orWhere('comment', 'like', $searchTerm)
                    ->orWhere('id', 'like', $searchTerm);
            });
            $banksQuery->where(function (Builder $query) use ($searchTerm) {
                $query->where('name', 'like', $searchTerm)
                    ->orWhere('full_name', 'like', $searchTerm)
                    ->orWhere('email', 'like', $searchTerm)
                    ->orWhere('comment', 'like', $searchTerm)
                    ->orWhere('id', 'like', $searchTerm);
            });
            $adminsQuery->where(function (Builder $query) use ($searchTerm) {
                $query->where('name', 'like', $searchTerm)
                    ->orWhere('full_name', 'like', $searchTerm)
                    ->orWhere('email', 'like', $searchTerm)
                    ->orWhere('comment', 'like', $searchTerm)
                    ->orWhere('id', 'like', $searchTerm);
            });
        }

        // --- 3. Fetch Data (conditionally based on type filter) ---
        $users = collect();
        $organizations = collect();
        $banks = collect();
        $admins = collect();

        if (!$this->typeFilter || $this->typeFilter === 'user') {
            $users = $usersQuery->get();
        }
        if (!$this->typeFilter || $this->typeFilter === 'organization') {
            $organizations = $organizationsQuery->get();
        }
        if (!$this->typeFilter || $this->typeFilter === 'bank') {
            $banks = $banksQuery->get();
        }
        if (!$this->typeFilter || $this->typeFilter === 'admin') {
            $admins = $adminsQuery->get();
        }

        // --- 4. Combine Collections & Add Type ---
        // Pre-fetch all suppressed emails in one query to avoid N+1
        $allEmails = collect([$users, $organizations, $banks, $admins])
            ->flatten()
            ->pluck('email')
            ->filter()
            ->unique()
            ->values();
        $suppressedEmails = \App\Models\MailingBounce::whereIn('email', $allEmails)
            ->where('is_suppressed', true)
            ->pluck('email')
            ->flip(); // flip for O(1) lookup

        $combined = new Collection();
        foreach ($users as $user) {
            $user->type = __('User');
            $user->model = 'App\Models\User';
            $user->inactive = $this->dateStatus($user->inactive_at);
            $user->email_suppressed = isset($suppressedEmails[$user->email]);
            $user->email_verif = $user->email_suppressed ? __('Blocked') : $this->dateStatus($user->email_verified_at);
            $user->deleted = $this->dateStatus($user->deleted_at);
            $user->is_restorable = $this->isProfileRestorable($user);
            $combined->push($user);
        }
        foreach ($organizations as $org) {
            $org->type = __('Organization');
            $org->model = 'App\Models\Organization';
            $org->inactive = $this->dateStatus($org->inactive_at);
            $org->email_suppressed = isset($suppressedEmails[$org->email]);
            $org->email_verif = $org->email_suppressed ? __('Blocked') : $this->dateStatus($org->email_verified_at);
            $org->deleted = $this->dateStatus($org->deleted_at);
            $org->is_restorable = $this->isProfileRestorable($org);
            $combined->push($org);
        }
        foreach ($banks as $bank) {
            $bank->type = __('Bank');
            $bank->model = 'App\Models\Bank';
            $bank->inactive = $this->dateStatus($bank->inactive_at);
            $bank->email_suppressed = isset($suppressedEmails[$bank->email]);
            $bank->email_verif = $bank->email_suppressed ? __('Blocked') : $this->dateStatus($bank->email_verified_at);
            $bank->deleted = $this->dateStatus($bank->deleted_at);
            $bank->is_restorable = $this->isProfileRestorable($bank);
            $combined->push($bank);
        }
        foreach ($admins as $admin) {
            $admin->type = __('Admin');
            $admin->model = 'App\Models\Admin';
            $admin->inactive = $this->dateStatus($admin->inactive_at);
            $admin->email_suppressed = isset($suppressedEmails[$admin->email]);
            $admin->email_verif = $admin->email_suppressed ? __('Blocked') : $this->dateStatus($admin->email_verified_at);
            $admin->deleted = $this->dateStatus($admin->deleted_at);
            $admin->is_restorable = $this->isProfileRestorable($admin);
            $combined->push($admin);
        }

        // --- 5. Apply Active and Email Verified Filters ---
        if ($this->activeFilter) {
            $combined = $combined->filter(function ($profile) {
                if ($this->activeFilter === 'active') {
                    return !$profile->inactive_at && !$profile->deleted_at;
                } elseif ($this->activeFilter === 'inactive') {
                    return $profile->inactive_at && !$profile->deleted_at;
                } elseif ($this->activeFilter === 'deleted') {
                    return $profile->deleted_at !== null;
                }
                return true;
            });
        }

        if ($this->emailVerifiedFilter) {
            $combined = $combined->filter(function ($profile) {
                if ($this->emailVerifiedFilter === 'verified') {
                    return $profile->email_verified_at !== null && !$profile->email_suppressed;
                } elseif ($this->emailVerifiedFilter === 'unverified') {
                    return $profile->email_verified_at === null && !$profile->email_suppressed;
                } elseif ($this->emailVerifiedFilter === 'blocked') {
                    return $profile->email_suppressed;
                }
                return true;
            });
        }

        // --- 6. Sort Combined Collection ---
        $sortField = $this->sortField;
        $sortDirection = $this->sortDirection === 'asc' ? false : true;

        if (in_array($sortField, ['name', 'inactive', 'email_verif', 'created_at', 'updated_at', 'type', 'id', 'last_login_at', 'comment'])) {
            $combined = $combined->sortBy($sortField, SORT_REGULAR, $sortDirection);
        } else {
            $combined = $combined->sortBy('created_at', SORT_REGULAR, $sortDirection);
        }

        // --- 7. Use proper Livewire pagination ---
        // Get current page using the correct method
        $currentPage = $this->getPage();
        $perPage = $this->perPage;
        $currentPageItems = $combined->slice(($currentPage - 1) * $perPage, $perPage)->values();

        $profilesPaginator = new LengthAwarePaginator(
            $currentPageItems,
            $combined->count(),
            $perPage,
            $currentPage,
            [
                'path' => request()->url(),
                'pageName' => 'page',
            ]
        );

        return view('livewire.profiles.manage', [
            'profiles' => $profilesPaginator,
        ]);
    }
}
