<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class User extends Authenticatable
{
    use HasFactory, Notifiable, HasRoles;

    /**
     * The guard name for Spatie Permission.
     */
    protected $guard_name = 'web';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'username',
        'password',
        'phone',
        'avatar',
        'theme',
        'is_active',
        'two_factor_enabled',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'two_factor_confirmed_at',
    ];

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

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'is_active' => 'boolean',
        'two_factor_enabled' => 'boolean',
        'two_factor_confirmed_at' => 'datetime',
    ];

    /**
     * Get the user's profile.
     */
    public function profile(): HasOne
    {
        return $this->hasOne(UserProfile::class);
    }

    /**
     * Get the associated partner (if user is a partner).
     */
    public function partner(): HasOne
    {
        return $this->hasOne(RPartner::class, 'user_id');
    }

    /**
     * Get the user's OTP verifications.
     */
    public function otpVerifications(): HasMany
    {
        return $this->hasMany(OtpVerification::class);
    }

    /**
     * Get the user's login attempts.
     */
    public function loginAttempts(): HasMany
    {
        return $this->hasMany(LoginAttempt::class);
    }

    /**
     * Get the user's social accounts.
     */
    public function socialAccounts(): HasMany
    {
        return $this->hasMany(SocialAccount::class);
    }

    /**
     * Get the user's sessions.
     */
    public function sessions(): HasMany
    {
        return $this->hasMany(UserSession::class);
    }

    /**
     * Get training sessions this user is assigned to as a trainer.
     */
    public function trainedSessions(): BelongsToMany
    {
        return $this->belongsToMany(
            \App\Models\TrainingSession::class,
            'training_session_trainers',
            'trainer_id',
            'training_session_id'
        )->withPivot(['role', 'topic', 'confirmation_status'])->withTimestamps();
    }

    /**
     * Get the user's discussions.
     */
    public function discussions(): HasMany
    {
        return $this->hasMany(\App\Models\Discussion::class);
    }

    /**
     * Get mentorship applications where this user applied as a mentor.
     */
    public function mentorApplications(): HasMany
    {
        return $this->hasMany(\App\Models\Mentorship\Application::class, 'user_id')
            ->where('role_preference', 'mentor');
    }

    /**
     * Get mentorship applications where this user applied as a mentee.
     */
    public function menteeApplications(): HasMany
    {
        return $this->hasMany(\App\Models\Mentorship\Application::class, 'user_id')
            ->where('role_preference', 'mentee');
    }

    /**
     * Get mentorship assignments where this user is the mentor.
     */
    public function mentorAssignments(): HasMany
    {
        return $this->hasMany(\App\Models\Mentorship\Assignment::class, 'mentor_id');
    }

    /**
     * Get mentorship assignments where this user is the mentee.
     */
    public function menteeAssignments(): HasMany
    {
        return $this->hasMany(\App\Models\Mentorship\Assignment::class, 'mentee_id');
    }

    /**
     * Scope a query to only include active users.
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope a query to only include inactive users.
     */
    public function scopeInactive($query)
    {
        return $query->where('is_active', false);
    }

    /**
     * Get the user's preferred theme based on role.
     */
    public function getThemeAttribute($value)
    {
        if ($value) {
            return $value;
        }

        // Fallback to role theme
        $role = $this->roles->first();
        return $role?->theme ?? 'default';
    }

    /**
     * Get the dashboard route based on user's role.
     */
    public function getRoleDashboardRoute(): string
    {
        $roleRoutes = [
            'Super Manager' => 'dashboard.super-manager',
            'Manager' => 'dashboard.manager',
            'Members' => 'dashboard.members',
            'Trainers' => 'dashboard.trainers',
            'Trainees' => 'self-paced.learn.my-courses',
            'Mentor' => 'dashboard.mentor',
            'Mentee' => 'dashboard.mentee',
            'Partners' => 'dashboard.partners',
            'Contributor' => 'dashboard.contributor',
            'Health Marketor' => 'dashboard.health-marketor',
        ];

        $role = $this->roles->first();
        return $roleRoutes[$role?->name] ?? 'dashboard';
    }

    /**
     * Get the absolute dashboard URL for this user's role.
     *
     * Prefers the named route when it is registered (works with or without
     * route cache). Falls back to a hard-coded path so a stale cache never
     * causes a RouteNotFoundException on login/redirect.
     */
    public function getDashboardUrl(): string
    {
        $routeName = $this->getRoleDashboardRoute();

        if (\Illuminate\Support\Facades\Route::has($routeName)) {
            return route($routeName);
        }

        // Hard-coded fallback paths — mirrors the route definitions in web.php.
        $paths = [
            'dashboard.super-manager'   => '/dashboard/super-manager',
            'dashboard.manager'         => '/dashboard/manager',
            'dashboard.members'         => '/dashboard/members',
            'dashboard.trainers'        => '/dashboard/trainers',
            'dashboard.trainees'        => '/dashboard/trainees',
            'dashboard.mentor'          => '/dashboard/mentor',
            'dashboard.mentee'          => '/dashboard/mentee',
            'dashboard.partners'        => '/dashboard/partners',
            'dashboard.contributor'     => '/dashboard/contributor',
            'dashboard.health-marketor' => '/dashboard/health-marketor',
        ];

        return url($paths[$routeName] ?? '/dashboard');
    }

    /**
     * Get the Self-Paced Learning Portal entry-point route for the user's role.
     *
     * - Super Manager, Manager, Trainers  → Portal Dashboard (management UI)
     * - Trainees                          → Public landing / my courses
     * - All other roles                   → null (no portal access)
     *
     * Requirements: 14.1, 14.2
     */
    public function getSelfPacedPortalRoute(): ?string
    {
        $role = $this->roles->first()?->name;

        return match ($role) {
            'Super Manager', 'Manager', 'Trainers' => 'self-paced.dashboard.index',
            'Trainees'                              => 'self-paced.public.index',
            default                                 => null,
        };
    }

    /**
     * Check whether this user has access to the Self-Paced Learning Portal.
     *
     * Requirements: 14.1, 14.2, 14.3
     */
    public function hasSelfPacedPortalAccess(): bool
    {
        return $this->getSelfPacedPortalRoute() !== null;
    }

    /**
     * Check if user requires OTP verification.
     */
    public function requiresOtp(): bool
    {
        return (bool) SystemSetting::get('otp_enabled', false);
    }

    /**
     * Check if user has 2FA enabled.
     */
    public function requires2FA(): bool
    {
        return $this->two_factor_enabled && $this->two_factor_confirmed_at !== null;
    }

    /**
     * Check if user has verified email.
     */
    public function hasVerifiedEmail(): bool
    {
        return $this->email_verified_at !== null;
    }

    /**
     * Get the user's avatar URL.
     */
    public function getAvatarUrlAttribute(): string
    {
        if ($this->avatar) {
            return asset('storage/' . $this->avatar);
        }

        // Generate Gravatar URL
        $hash = md5(strtolower(trim($this->email)));
        return "https://www.gravatar.com/avatar/{$hash}?s=200&d=mp";
    }

    /**
     * Get the user's initials for avatar fallback.
     */
    public function getInitialsAttribute(): string
    {
        $names = explode(' ', $this->name);
        $initials = '';

        foreach ($names as $name) {
            $initials .= strtoupper(substr($name, 0, 1));
        }

        return substr($initials, 0, 2);
    }

    /**
     * Generate a unique username derived from an email address.
     * Uses the local part of the email, strips non-alphanumeric chars,
     * and appends a numeric suffix if the base is already taken.
     */
    public static function generateUsername(string $email): string
    {
        $base = strtolower(preg_replace('/[^a-zA-Z0-9_]/', '', explode('@', $email)[0]));
        $base = $base ?: 'user';

        $username = $base;
        $counter  = 1;

        while (static::where('username', $username)->exists()) {
            $username = $base . $counter;
            $counter++;
        }

        return $username;
    }

    /**
     * Check if user can be impersonated.
     */
    public function canBeImpersonated(): bool
    {
        return !$this->hasRole('Super Manager');
    }
}
