<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Models\Concerns\Auditable;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    /** @use HasFactory<UserFactory> */
    use Auditable, HasFactory, Notifiable, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'username',
        'email',
        'password',
        'is_active',
        'must_change_password',
        'failed_login_attempts',
        'locked_until',
        'last_login_at',
        'last_seen_at',
    ];

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

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'is_active' => 'boolean',
            'must_change_password' => 'boolean',
            'locked_until' => 'datetime',
            'last_login_at' => 'datetime',
            'last_seen_at' => 'datetime',
        ];
    }

    public function assignments(): HasMany
    {
        return $this->hasMany(UserAssignment::class);
    }

    public function permissionOverrides(): HasMany
    {
        return $this->hasMany(UserPermission::class);
    }

    public function hasPrivilegedRole(): bool
    {
        return $this->assignments()
            ->where('is_active', true)
            ->whereHas('role', fn ($query) => $query->where('is_privileged', true)->where('is_active', true))
            ->exists();
    }

    public function isSuperAdministrator(): bool
    {
        return $this->assignments()
            ->where('is_active', true)
            ->whereHas('role', fn ($query) => $query->where('code', 'superadministrador')->where('is_active', true))
            ->exists();
    }

    public function hasPermission(string $code): bool
    {
        $override = $this->permissionOverrides()
            ->whereHas('permission', fn ($query) => $query->where('code', $code))
            ->where(fn ($query) => $query->whereNull('expires_at')->orWhere('expires_at', '>', now()))
            ->first();

        if ($override) {
            return $override->effect === 'allow';
        }

        $administrationMode = session('operating_mode') === 'administration';

        if ($administrationMode && $this->isSuperAdministrator()) {
            return true;
        }

        return $this->assignments()
            ->where('is_active', true)
            ->whereHas('role', function ($query) use ($code, $administrationMode): void {
                $query->where('is_active', true)
                    ->when(! $administrationMode, fn ($roleQuery) => $roleQuery->where('is_privileged', false))
                    ->whereHas('permissions', fn ($permissionQuery) => $permissionQuery->where('code', $code));
            })
            ->exists();
    }

    public function canAccessBranch(int $branchId): bool
    {
        if (session('operating_mode') === 'administration' && $this->isSuperAdministrator()) {
            return true;
        }

        return $this->assignments()
            ->where('is_active', true)
            ->where(fn ($query) => $query->whereNull('branch_id')->orWhere('branch_id', $branchId))
            ->exists();
    }

    public function canUseConsolidatedView(): bool
    {
        if (session('operating_mode') === 'administration' && $this->isSuperAdministrator()) {
            return true;
        }

        return $this->assignments()->where('is_active', true)->whereNull('branch_id')->exists();
    }
}
