<?php

namespace App\Actions\Jetstream;

use App\Models\User;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\DB;
use Laravel\Jetstream\Contracts\DeletesUsers;
use Throwable;

class DeleteUser implements DeletesUsers
{
    /**
     * Delete the given user.
     *
     * @param  mixed  $user
     * @param  string  $balanceHandlingOption
     * @param  int|null  $donationAccountId
     * @param  bool  $isAutoDeleted
     * @param  string|null  $deletedByUsername
     * @return void
     */
    public function delete($user, $balanceHandlingOption = 'delete', $donationAccountId = null, $isAutoDeleted = false, $deletedByUsername = null)
    {

        try {
            // Use a transaction for deleting the user
            // START
            DB::transaction(function () use ($user, $balanceHandlingOption, $donationAccountId, $isAutoDeleted, $deletedByUsername): void {

                // Check for negative balances before proceeding with deletion
                $userAccounts = $user->accounts()->active()->notRemoved()->get();
                foreach ($userAccounts as $account) {
                    \Cache::forget("account_balance_{$account->id}");
                    if ($account->balance < 0) {
                        \Log::error('Profile deletion blocked: negative balance detected', [
                            'user_id' => $user->id,
                            'account_id' => $account->id,
                            'account_name' => $account->name,
                            'balance' => $account->balance
                        ]);
                        throw new \Exception('Cannot delete profile with negative balance. Please settle all debts before deleting your profile.');
                    }
                }

                // Store balance handling preferences in cache for later use
                // This will be used by permanentlyDelete() after grace period
                // Fallback: if cache is lost, currency will be destroyed (transferred to debit account)
                $balanceHandlingData = [
                    'option' => $balanceHandlingOption,
                    'donation_account_id' => $donationAccountId,
                    'stored_at' => now()->toDateTimeString(),
                ];

                // Store in cache with TTL = grace period + 7 days buffer
                $gracePeriodDays = timebank_config('delete_profile.grace_period_days', 30);
                $cacheKey = 'balance_handling_' . get_class($user) . '_' . $user->id;
                \Cache::put($cacheKey, $balanceHandlingData, now()->addDays($gracePeriodDays + 7));

                // Set human-readable comment (always in English for database storage)
                if ($isAutoDeleted) {
                    // Auto-deletion due to inactivity
                    $daysInactive = timebank_config('profile_inactive.days_not_logged_in') + timebank_config('delete_profile.days_after_inactive.run_delete');
                    $user->comment = 'Profile automatically deleted after ' . $daysInactive . ' days of inactivity.';
                } elseif ($deletedByUsername) {
                    // Admin/manager deletion
                    $user->comment = 'Profile deleted by ' . $deletedByUsername;
                } else {
                    // Self-deletion by profile owner
                    $user->comment = 'Profile deleted by self-deletion';
                }

                // Mark profile as deleted (soft delete with grace period)
                // Balances will be handled after grace period by scheduled command
                // Accounts remain active during grace period to allow restoration
                $user->deleted_at = now();
                $user->save();

                // Delete tokens to force logout
                if ($user instanceof \App\Models\User) {
                    $user->tokens->each->delete();
                }

            });
            // STOP
            // End of transaction

            return ['status' => 'success'];



        } catch (Throwable $e) {

            return ['status' => 'error', 'message' => $e->getMessage()];
        }
    }

    /**
     * Donate user's account balances to an organization.
     *
     * @param  mixed  $user
     * @param  int  $donationAccountId
     * @return void
     */
    protected function donateBalancesToOrganization($user, $donationAccountId)
    {
        \Log::info('Starting balance donation', [
            'user_id' => $user->id,
            'donation_account_id' => $donationAccountId
        ]);

        // Get the donation target account
        $toAccount = \App\Models\Account::find($donationAccountId);

        if (!$toAccount) {
            \Log::error('Donation account not found', ['donation_account_id' => $donationAccountId]);
            throw new \Exception('Donation account not found.');
        }

        \Log::info('Donation target account found', [
            'account_id' => $toAccount->id,
            'account_name' => $toAccount->name,
            'accountable_type' => $toAccount->accountable_type
        ]);

        // Verify the target account is an organization
        if ($toAccount->accountable_type !== 'App\\Models\\Organization') {
            \Log::error('Target account is not an organization', [
                'accountable_type' => $toAccount->accountable_type
            ]);
            throw new \Exception('The selected account is not an organization account.');
        }

        // Get all active accounts belonging to the user with positive balances
        $userAccounts = $user->accounts()
            ->active()
            ->notRemoved()
            ->get();

        // Clear balance cache for all accounts to ensure we get current values
        foreach ($userAccounts as $account) {
            \Cache::forget("account_balance_{$account->id}");
        }

        \Log::info('User accounts found', [
            'count' => $userAccounts->count(),
            'accounts' => $userAccounts->map(function($acc) {
                return [
                    'id' => $acc->id,
                    'name' => $acc->name,
                    'balance' => $acc->balance
                ];
            })
        ]);

        $totalTransferred = 0;
        $transactionsCreated = 0;

        foreach ($userAccounts as $fromAccount) {
            // Calculate the current balance
            $balance = $fromAccount->balance;

            \Log::info('Processing account', [
                'account_id' => $fromAccount->id,
                'balance' => $balance
            ]);

            // Only create transaction if there's a positive balance
            if ($balance > 0) {
                try {
                    // Create a donation transaction
                    $transaction = \App\Models\Transaction::create([
                        'from_account_id' => $fromAccount->id,
                        'to_account_id' => $toAccount->id,
                        'transaction_type_id' => 3, // Donation type
                        'amount' => $balance,
                        'description' => 'Balance donation from deleted profile ' . $user->name,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);

                    $totalTransferred += $balance;
                    $transactionsCreated++;

                    \Log::info('Transaction created successfully', [
                        'transaction_id' => $transaction->id,
                        'amount' => $balance
                    ]);
                } catch (\Exception $e) {
                    \Log::error('Failed to create transaction', [
                        'from_account_id' => $fromAccount->id,
                        'to_account_id' => $toAccount->id,
                        'amount' => $balance,
                        'error' => $e->getMessage()
                    ]);
                    throw new \Exception('Failed to create donation transaction: ' . $e->getMessage());
                }
            }
        }

        \Log::info('Balance donation completed', [
            'transactions_created' => $transactionsCreated,
            'total_transferred' => $totalTransferred
        ]);

        // Mark associated accounts inactive
        $user->accounts()->update(['inactive_at' => now(), 'deleted_at' => now()]);
    }

    /**
     * Transfer user's account balances to a bank the profile was a client of.
     *
     * @param  mixed  $user
     * @return void
     */
    protected function transferBalancesToBankClient($user)
    {
        // Get all active accounts belonging to the user with positive balances
        $userAccounts = $user->accounts()
            ->active()
            ->notRemoved()
            ->get();

        // Find banks the user was a client of
        // This would need to be implemented based on your bank-client relationship structure
        // For now, this is a placeholder for the implementation
        foreach ($userAccounts as $fromAccount) {
            $balance = $fromAccount->balance;
            if ($balance > 0) {
                // Transfer to bank account logic would go here
                // You would need to determine which bank account to use
            }
        }
    }

    /**
     * Transfer user's account balances to a specific account ID.
     *
     * @param  mixed  $user
     * @param  int  $accountId
     * @return void
     */
    protected function transferBalancesToSpecificAccount($user, $accountId)
    {
        // Get the target account
        $toAccount = \App\Models\Account::find($accountId);

        if (!$toAccount) {
            throw new \Exception('Target account not found.');
        }

        // Get all active accounts belonging to the user with positive balances
        $userAccounts = $user->accounts()
            ->active()
            ->notRemoved()
            ->get();

        foreach ($userAccounts as $fromAccount) {
            $balance = $fromAccount->balance;

            if ($balance > 0) {
                // Create a donation transaction to the specified account
                \App\Models\Transaction::create([
                    'from_account_id' => $fromAccount->id,
                    'to_account_id' => $toAccount->id,
                    'transaction_type_id' => 3, // Donation type
                    'amount' => $balance,
                    'description' => 'Balance transfer from deleted profile ' . $user->name,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        }

        // Mark associated accounts inactive
        $user->accounts()->update(['inactive_at' => now(), 'deleted_at' => now()]);
    }

    /**
     * Transfer user's account balances to debit account (remove currency from circulation).
     *
     * @param  mixed  $user
     * @return void
     */
    protected function transferBalancesToDebitAccount($user)
    {
        // Get all active accounts belonging to the user with positive balances
        $userAccounts = $user->accounts()
            ->active()
            ->notRemoved()
            ->get();

        // Find the debit account (typically a system account for currency removal)
        $debitAccount = \App\Models\Account::where('name', 'debit')
            ->whereHasMorph('accountable', [\App\Models\Bank::class])
            ->first();

        if (!$debitAccount) {
            throw new \Exception('Debit account not found for currency removal.');
        }

        foreach ($userAccounts as $fromAccount) {
            $balance = $fromAccount->balance;

            if ($balance > 0) {
                // Create a currency removal transaction
                \App\Models\Transaction::create([
                    'from_account_id' => $fromAccount->id,
                    'to_account_id' => $debitAccount->id,
                    'transaction_type_id' => 5, // Currency removal type
                    'amount' => $balance,
                    'description' => 'Currency removal from deleted profile ' . $user->name,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        }

        // Mark associated accounts inactive
        $user->accounts()->update(['inactive_at' => now(), 'deleted_at' => now()]);
    }

    /**
     * Permanently delete a profile by handling balances and anonymizing all data.
     * Called by scheduled command after grace period expires.
     *
     * @param  mixed  $user
     * @return array
     */
    public function permanentlyDelete($user)
    {
        try {
            DB::transaction(function () use ($user): void {
                $profileType = get_class($user);
                $profileTypeName = class_basename($profileType);

                // Retrieve balance handling preferences from cache
                $cacheKey = 'balance_handling_' . get_class($user) . '_' . $user->id;
                $balanceHandlingData = \Cache::get($cacheKey);

                // Fallback: try to parse from comment field if it's JSON (old format compatibility)
                if (!$balanceHandlingData && $user->comment && str_starts_with($user->comment, '{')) {
                    $balanceHandlingData = json_decode($user->comment, true);
                }

                // Handle balances before anonymization
                if ($balanceHandlingData && isset($balanceHandlingData['option'])) {
                    $option = $balanceHandlingData['option'];
                    $donationAccountId = $balanceHandlingData['donation_account_id'] ?? null;

                    // Execute balance handling based on stored option
                    if ($option === 'donate' && $donationAccountId) {
                        $this->donateBalancesToOrganization($user, $donationAccountId);
                    } elseif ($option === 'delete') {
                        // User chose to delete balance - destroy currency
                        $this->transferBalancesToDebitAccount($user);
                    }
                } else {
                    // FALLBACK: Cache lost or no data stored
                    // Destroy currency (transfer to debit account) as safe default
                    \Log::warning('Balance handling cache lost for profile deletion', [
                        'user_id' => $user->id,
                        'user_name' => $user->name,
                        'fallback' => 'destroying_currency'
                    ]);
                    $this->transferBalancesToDebitAccount($user);
                }

                // Handle WireChat kept messages to prevent orphaned data
                // This is ALWAYS done when profile is permanently deleted (not optional)
                if (timebank_config('wirechat.profile_deletion.release_kept_messages', true)) {
                    $releasedCount = \DB::table('wirechat_messages')
                        ->where('sendable_id', $user->id)
                        ->where('sendable_type', get_class($user))
                        ->whereNotNull('kept_at')
                        ->update([
                            'kept_at' => null,
                            'updated_at' => now()
                        ]);

                    if ($releasedCount > 0) {
                        \Log::info('WireChat kept messages released for deleted profile', [
                            'profile_id' => $user->id,
                            'profile_type' => get_class($user),
                            'profile_name' => $user->name,
                            'messages_released' => $releasedCount
                        ]);
                    }
                }

                // Detach all relationships that do not need any historic record
                // User-specific relationships
                if ($user instanceof \App\Models\User) {
                    if (method_exists($user, 'locations')) {
                        $user->locations()->delete();
                    }
                    if (method_exists($user, 'languages')) {
                        $user->languages()->detach();
                    }
                    if (method_exists($user, 'socials')) {
                        $user->socials()->detach();
                    }
                    if (method_exists($user, 'organizations')) {
                        $user->organizations()->detach();
                    }
                    if (method_exists($user, 'bankClients')) {
                        $user->bankClients()->detach();
                    }
                    if (method_exists($user, 'banksManaged')) {
                        $user->banksManaged()->detach();
                    }
                    if (method_exists($user, 'admins')) {
                        $user->admins()->detach();
                    }
                }

                // Organization/Bank/Admin specific relationships
                if ($user instanceof \App\Models\Organization) {
                    if (method_exists($user, 'users')) {
                        $user->users()->detach();
                    }
                }
                if ($user instanceof \App\Models\Bank) {
                    if (method_exists($user, 'managers')) {
                        $user->managers()->detach();
                    }
                }
                if ($user instanceof \App\Models\Admin) {
                    if (method_exists($user, 'users')) {
                        $user->users()->detach();
                    }
                }
                // Common relationships for all profile types
                if (method_exists($user, 'locations')) {
                    $user->locations()->delete();
                }

                // Anonymize profile
                $anonymousId = $this->generateAnonymousId($profileType);

                $user->name = 'Removed ' . strtolower($profileTypeName) . ' ' . $anonymousId;
                $user->full_name = 'Removed ' . strtolower($profileTypeName) . ' ' . $anonymousId;
                $user->email = 'removed-' . $anonymousId . '@remove.ed';
                $user->email_verified_at = null;
                $user->password = "";

                if (property_exists($user, 'two_factor_secret')) {
                    $user->two_factor_secret = null;
                }
                if (property_exists($user, 'two_factor_recovery_codes')) {
                    $user->two_factor_recovery_codes = null;
                }
                if (property_exists($user, 'two_factor_confirmed_at')) {
                    $user->two_factor_confirmed_at = null;
                }

                $user->deleteProfilePhoto();
                $user->profile_photo_path = 'app-images/profile-user-removed.svg';
                $user->about = null;
                $user->about_short = null;
                $user->motivation = null;

                if (property_exists($user, 'date_of_birth')) {
                    $user->date_of_birth = null;
                }

                $user->website = null;
                $user->phone = null;
                $user->phone_public = 0;

                if (property_exists($user, 'remember_token')) {
                    $user->remember_token = null;
                }
                if (property_exists($user, 'current_team_id')) {
                    $user->current_team_id = null;
                }
                if (property_exists($user, 'cyclos_id')) {
                    $user->cyclos_id = null;
                }
                if (property_exists($user, 'cyclos_salt')) {
                    $user->cyclos_salt = null;
                }
                if (property_exists($user, 'cyclos_skills')) {
                    $user->cyclos_skills = null;
                }

                $user->limit_min = 0;
                $user->limit_max = 0;
                $user->comment = null;
                $user->lang_preference = null;

                if (property_exists($user, 'principles_terms_accepted')) {
                    $user->principles_terms_accepted = null;
                }

                $user->last_login_ip = null;
                $user->save();

                // Unreact all Laravel-love reactions
                if (!($user instanceof \App\Models\Admin)) {
                    $reacterFacade = $user->getloveReacter();
                    $reactions = $reacterFacade->getReactions()->load(['reactant', 'type']);
                    foreach ($reactions as $reaction) {
                        if ($reaction->reactant && $reaction->type) {
                            $reacterFacade->unReactTo($reaction->reactant, $reaction->type);
                        }
                    }

                    $reactantFacade = $user->getloveReactant();
                    $receivedReactions = $reactantFacade->getReactions()->load(['reacter', 'type']);
                    foreach ($receivedReactions as $reaction) {
                        if ($reaction->reacter && $reaction->type) {
                            $reaction->reacter->unReactTo($reaction->reactant, $reaction->type);
                        }
                    }
                }

                // Remove all taggable skills
                if (!($user instanceof \App\Models\Admin)) {
                    $user->detag();
                }

                // Clear the balance handling cache
                $cacheKey = 'balance_handling_' . get_class($user) . '_' . $user->id;
                \Cache::forget($cacheKey);
            });

            return ['status' => 'success'];
        } catch (Throwable $e) {
            return ['status' => 'error', 'message' => $e->getMessage()];
        }
    }

    /**
     * Generate a short, anonymous, unique identifier for deleted profiles.
     *
     * @param  string  $profileType  The profile model class name
     * @return string  8-character alphanumeric ID
     */
    protected function generateAnonymousId($profileType)
    {
        $attempts = 0;
        $maxAttempts = 100;

        do {
            // Generate 8-character random alphanumeric string (lowercase for consistency)
            $anonymousId = strtolower(substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 8));

            // Check if this ID is already used in name or email fields
            $nameExists = $profileType::where('name', 'like', '%' . $anonymousId . '%')->exists();
            $emailExists = $profileType::where('email', 'like', '%' . $anonymousId . '%')->exists();

            $attempts++;

            if ($attempts >= $maxAttempts) {
                // Fallback to timestamp-based ID if we can't find a unique random one
                $anonymousId = strtolower(substr(md5(microtime()), 0, 8));
                break;
            }

        } while ($nameExists || $emailExists);

        return $anonymousId;
    }
}
