<?php

namespace App\Models;

use App\Models\Concerns\NormalizesPhoneNumbers;
use App\Services\BranchAccessService;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Spatie\Permission\Traits\HasRoles;

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

    protected $guard_name = 'web';

    protected $fillable = [
        'name', 'employee_number', 'email', 'username', 'phone', 'nin', 'position',
        'profile_photo_path', 'status', 'employed_at', 'primary_branch_id', 'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    protected static function booted(): void
    {
        static::created(function (User $user): void {
            if ($user->primary_branch_id !== null) {
                $user->forceFill([
                    'employee_number' => self::generateEmployeeNumber($user),
                ])->saveQuietly();
            }
        });

        static::updating(function (User $user): void {
            $originalEmployeeNumber = $user->getOriginal('employee_number');

            if (filled($originalEmployeeNumber) && $user->isDirty('employee_number')) {
                $user->employee_number = $originalEmployeeNumber;
            }

            if (blank($originalEmployeeNumber) && $user->primary_branch_id !== null) {
                $user->employee_number = self::generateEmployeeNumber($user);
            }
        });
    }

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'employed_at' => 'date',
            'password' => 'hashed',
            'two_factor_secret' => 'encrypted',
            'two_factor_recovery_codes' => 'encrypted:array',
            'two_factor_confirmed_at' => 'datetime',
        ];
    }

    private static function generateEmployeeNumber(User $user): string
    {
        $candidate = 'EMP-'.str_pad((string) $user->getKey(), 6, '0', STR_PAD_LEFT);

        if (! self::query()->where('employee_number', $candidate)->whereKeyNot($user->getKey())->exists()) {
            return $candidate;
        }

        do {
            $candidate = 'EMP-'.$user->getKey().'-'.Str::upper(Str::random(4));
        } while (self::query()->where('employee_number', $candidate)->whereKeyNot($user->getKey())->exists());

        return $candidate;
    }

    public function branches(): BelongsToMany
    {
        return $this->belongsToMany(Branch::class)->withPivot('is_primary')->withTimestamps();
    }

    public function primaryBranch(): BelongsTo
    {
        return $this->belongsTo(Branch::class, 'primary_branch_id');
    }

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

    /**
     * Compatibility wrapper for the canonical branch-access service.
     *
     * @return array<int>
     */
    public function accessibleBranchIds(): array
    {
        return app(BranchAccessService::class)->accessibleBranchIds($this);
    }

    public function canAccessBranch(int $branchId): bool
    {
        return app(BranchAccessService::class)->canAccessBranch($this, $branchId);
    }
}
