<?php

namespace Modules\Auth\Entities;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\Permission\Contracts\Permission;
use Spatie\Permission\PermissionRegistrar;
use Spatie\Permission\Traits\HasPermissions;
use Spatie\Permission\Traits\HasRoles;
use App\Services\ActiveRoleService;
use Lab404\Impersonate\Models\Impersonate;
use Modules\Role\Entities\Role;
use Modules\User\Entities\Address;
use Modules\User\Entities\BankDetail;
use Modules\User\Entities\Employment;
use Modules\User\Entities\Heir;

class User extends Authenticatable
{
    /** @use HasFactory<\Modules\Auth\Database\Factories\UserFactory> */
    use HasApiTokens, HasFactory, HasPermissions, HasRoles, HasUuids, Impersonate, LogsActivity, Notifiable, SoftDeletes;

    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'ic_number',
        'position',
        'phone_number',
        'image_url',
        'status',
        'two_factor_secret',
        'two_factor_recovery_codes',
        'two_factor_confirmed_at',
        'gender',
        'marriage_status',
        'member_number',
        'member_type',
        'join_date',
        'birth_date',
        'birth_place',
    ];

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

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'status' => 'string',
            'gender' => 'string',
            'marriage_status' => 'string',
            'member_number' => 'integer',
            'member_type' => 'string',
            'join_date' => 'date',
            'birth_date' => 'date',
            'birth_place' => 'string',
        ];
    }


    /**
     * Role used for permission checks in the current session (Sanctum token).
     */
    public function activeRole(): ?Role
    {
        return ActiveRoleService::getActiveRole($this);
    }

    public function addresses()
    {
        return $this->hasMany(Address::class);
    }

    public function employments()
    {
        return $this->hasMany(Employment::class);
    }

    public function bankDetails()
    {
        return $this->hasMany(BankDetail::class);
    }

    public function heirs()
    {
        return $this->hasMany(Heir::class);
    }

    public function hasPermissionAction($action): bool
    {
        $role = $this->activeRole();

        return $role && $role->permission_actions &&
            in_array($action, $role->permission_actions, true);
    }

    public function getActivityLogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logAll()
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs()
            ->setDescriptionForEvent(fn (string $eventName) => "User {$this->name} was {$eventName}");
    }

    /**
     * Override hasPermissionTo to bypass all permission checks for DEVELOPER role
     */
    public function hasPermissionTo($permission, $guardName = null): bool
    {
        $activeRole = $this->activeRole();

        if ($activeRole?->name === 'DEVELOPER') {
            return true;
        }

        static $isCheckingPermission = false;

        if ($isCheckingPermission) {
            return false;
        }

        $isCheckingPermission = true;

        try {
            $permissionClass = app(PermissionRegistrar::class)->getPermissionClass();

            if (is_string($permission)) {
                $permission = $permissionClass::findByName($permission, $guardName ?? 'api');
            }

            if (is_int($permission)) {
                $permission = $permissionClass::findById($permission, $guardName ?? 'api');
            }

            if (! $permission instanceof Permission) {
                return false;
            }

            if ($this->permissions->contains('id', $permission->id)) {
                return true;
            }

            if (! $activeRole) {
                return false;
            }

            $activeRole->loadMissing('permissions');

            return $activeRole->permissions->contains('id', $permission->id);
        } catch (\Exception $e) {
            return false;
        } finally {
            $isCheckingPermission = false;
        }
    }

    /**
     * Scope to exclude current user unless they have developer role
     */
    public function scopeExcludeCurrentUserUnlessDeveloper($query, $currentUser = null)
    {
        $currentUser = $currentUser ?? auth()->user();

        if ($currentUser && ! $currentUser->hasRole('DEVELOPER')) {
            return $query->where('id', '!=', $currentUser->id);
        }

        return $query;
    }

    /**
     * Scope to exclude users with DEVELOPER role unless current user is DEVELOPER
     */
    public function scopeExcludeDevelopersUnlessDeveloper($query, $currentUser = null)
    {
        $currentUser = $currentUser ?? auth()->user();

        if ($currentUser && ! $currentUser->hasRole('DEVELOPER')) {
            return $query->whereDoesntHave('roles', function ($q) {
                $q->where('name', 'DEVELOPER');
            });
        }

        return $query;
    }

    /**
     * Check if the user can impersonate another user
     */
    public function canImpersonate(): bool
    {
        // Check permission instead of hardcoded roles
        return $this->hasPermissionTo('menyamar pengguna');
    }

    /**
     * Check if the user can be impersonated
     */
    public function canBeImpersonated(): bool
    {
        // DEVELOPER cannot be impersonated by anyone
        if ($this->hasRole('DEVELOPER')) {
            return false;
        }
        
        // Check if the current user has permission to impersonate
        $currentUser = auth()->user();
        if (!$currentUser) {
            return false;
        }
        
        // Users with permission can impersonate other users (except DEVELOPER)
        return $currentUser->hasPermissionTo('menyamar pengguna');
    }

    public function hasVerifiedEmail(): bool
    {
        return $this->email_verified_at !== null;
    }

    /**
     * Check if user can login based on their status
     */
    public function canLogin(): bool
    {
        return $this->status === 'active';
    }

    /**
     * Whether credentials are valid for issuing a session (includes verified pending users).
     */
    public function canAuthenticate(): bool
    {
        if ($this->canLogin()) {
            return true;
        }

        return $this->hasVerifiedEmail() && $this->status === 'pending';
    }

    /**
     * Get the login restriction message based on user status
     */
    public function getLoginRestrictionMessage(): ?string
    {
        switch ($this->status) {
            case 'pending':
                return 'Akaun anda sedang menunggu pengaktifan dari pentadbir sistem. Sila hubungi pentadbir sistem.';
            case 'inactive':
                return 'Akaun anda tidak aktif. Sila hubungi pentadbir sistem.';
            default:
                return 'Akaun anda tidak dapat mengakses sistem. Sila hubungi pentadbir sistem.';
        }
    }
}
