<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Auth\Passwords\CanResetPassword as CanResetPasswordTrait;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable implements CanResetPassword
{
    use HasApiTokens, HasFactory, Notifiable, CanResetPasswordTrait;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'username',
        'role',
        'refer_code',
        'email',
        'phone',
        'password',
        'referred_by',
        'fund_wallet',
        'mining_earning',
        'referral_earning',
        'net_balance',
        'total_invested',
        'profile_photo',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'fund_wallet' => 'decimal:2',
        'mining_earning' => 'decimal:2',
        'referral_earning' => 'decimal:2',
        'net_balance' => 'decimal:2',
        'total_invested' => 'decimal:2',
    ];

    /**
     * Generate a unique referral code from user name and ID
     * Format: RealName + LastTwoDigitsOfID
     * Example: "Ramiz Nazar" with ID 123 -> "RamizNazar23"
     *
     * @param string $name
     * @param int $userId
     * @return string
     */
    public static function generateReferralCode(string $name, int $userId): string
    {
        // Get the user's real name (remove extra spaces, keep as is)
        $realName = preg_replace('/\s+/', '', trim($name));
        
        // If name is empty, use default
        if (empty($realName)) {
            $realName = 'USER';
        }
        
        // Get last two digits of user ID
        $lastTwoDigits = str_pad((string) ($userId % 100), 2, '0', STR_PAD_LEFT);

        $referralCode = $realName . $lastTwoDigits;

        // Ensure uniqueness (in case of duplicate names with same last 2 digits)
        $attempts = 0;
        $originalCode = $referralCode;
        while (static::where('refer_code', $referralCode)->exists() && $attempts < 10) {
            // If duplicate, append a random digit
            $referralCode = $originalCode . rand(0, 9);
            $attempts++;
        }

        return $referralCode;
    }

    /**
     * Get the user who referred this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function referrer()
    {
        return $this->belongsTo(User::class, 'referred_by', 'refer_code');
    }

    /**
     * Get users directly referred by this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function directReferrals()
    {
        return $this->hasMany(User::class, 'referred_by', 'refer_code');
    }

    /**
     * Calculate the referral level of a user relative to the current user
     * 
     * @param int $userId The ID of the user to check
     * @param int $maxLevel Maximum level to search (default: 5)
     * @return int|null The level (1-5) or null if not in referral chain
     */
    public function getReferralLevel($userId, $maxLevel = 5)
    {
        if ($userId == $this->id) {
            return 0; // Same user
        }

        $visited = [];
        $queue = [[$this->id, 0]]; // [userId, level]

        while (!empty($queue)) {
            [$currentUserId, $level] = array_shift($queue);

            if ($currentUserId == $userId) {
                return $level;
            }

            if ($level >= $maxLevel || isset($visited[$currentUserId])) {
                continue;
            }

            $visited[$currentUserId] = true;

            // Get direct referrals of current user
            $currentUser = static::find($currentUserId);
            if ($currentUser) {
                $directReferrals = static::where('referred_by', $currentUser->refer_code)->get();
                foreach ($directReferrals as $referral) {
                    if (!isset($visited[$referral->id])) {
                        $queue[] = [$referral->id, $level + 1];
                    }
                }
            }
        }

        return null; // User not found in referral chain
    }

    /**
     * Get all referrals recursively up to N levels deep
     * 
     * @param int $maxLevel Maximum level to traverse (default: 5)
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getAllReferralsRecursive($maxLevel = 5)
    {
        $allReferrals = collect();
        $visited = [];
        $queue = [[$this->id, 0]]; // [userId, level]

        while (!empty($queue)) {
            [$currentUserId, $level] = array_shift($queue);

            if ($level >= $maxLevel || isset($visited[$currentUserId])) {
                continue;
            }

            $visited[$currentUserId] = true;

            // Get direct referrals of current user
            $currentUser = static::find($currentUserId);
            if ($currentUser) {
                $directReferrals = static::where('referred_by', $currentUser->refer_code)->get();
                foreach ($directReferrals as $referral) {
                    if (!isset($visited[$referral->id])) {
                        $referral->referral_level = $level + 1; // Add level as attribute
                        $allReferrals->push($referral);
                        $queue[] = [$referral->id, $level + 1];
                    }
                }
            }
        }

        return $allReferrals;
    }

    /**
     * Get all deposits made by this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function deposits()
    {
        return $this->hasMany(Deposit::class);
    }

    /**
     * Get all investments made by this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function investments()
    {
        return $this->hasMany(Investment::class);
    }

    /**
     * Get all withdrawals made by this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function withdrawals()
    {
        return $this->hasMany(Withdrawal::class);
    }

    /**
     * Get all notifications for this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function notifications()
    {
        return $this->hasMany(Notification::class);
    }

    /**
     * Get all unread notifications for this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function unreadNotifications()
    {
        return $this->hasMany(Notification::class)->where('is_read', false);
    }

    /**
     * Get all reward levels achieved by this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function rewardLevels()
    {
        return $this->hasMany(UserRewardLevel::class);
    }

    /**
     * Get all transactions for this user
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function transactions()
    {
        return $this->hasMany(Transaction::class);
    }

    /**
     * Calculate total referral investment (sum of investments from direct referrals)
     *
     * @return float
     */
    public function getTotalReferralInvestment(): float
    {
        return \App\Services\RewardLevelService::calculateTotalReferralInvestment($this);
    }

    /**
     * Get all reward levels achieved by this user
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getAchievedRewardLevels()
    {
        return $this->rewardLevels()
            ->with('rewardLevel')
            ->get()
            ->map(function ($userRewardLevel) {
                return $userRewardLevel->rewardLevel;
            });
    }

    /**
     * Get the current (highest) reward level achieved by this user
     *
     * @return RewardLevel|null
     */
    public function getCurrentRewardLevel(): ?RewardLevel
    {
        $achievedLevel = $this->rewardLevels()
            ->with('rewardLevel')
            ->join('reward_levels', 'user_reward_levels.reward_level_id', '=', 'reward_levels.id')
            ->orderBy('reward_levels.sort_order', 'desc')
            ->orderBy('reward_levels.level', 'desc')
            ->first();

        return $achievedLevel ? $achievedLevel->rewardLevel : null;
    }

    /**
     * Recalculate and update the net balance
     * Net Balance = Mining Earning + Referral Earning (does NOT include Fund Wallet)
     *
     * @return void
     */
    public function updateNetBalance()
    {
        $this->net_balance = ($this->mining_earning ?? 0) + ($this->referral_earning ?? 0);
        $this->save();
    }

    /**
     * Get the email address where password reset links are sent.
     *
     * @return string
     */
    public function getEmailForPasswordReset()
    {
        return $this->email;
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new \App\Notifications\ResetPasswordNotification($token));
    }

}
