<?php

namespace App\Models;

use App\Domain\Identity\Models\LoginHistory;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Models\Role;
use App\Domain\Shared\Helpers\CredentialNormalizer;
use App\Domain\Shared\Traits\Auditable;
use App\Domain\Shared\Traits\BelongsToAcademy;
use App\Domain\Shared\Traits\HasUuid;
use Illuminate\Database\Eloquent\Casts\Attribute;
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\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens, HasUuid, BelongsToAcademy, SoftDeletes, Auditable;

    protected $fillable = [
        'academy_id',
        'name',
        'name_ar',
        'email',
        'phone',
        'national_id',
        'date_of_birth',
        'gender',
        'password',
        'avatar_path',
        'status',
        'person_id',
        'role_id',
        'is_super_admin',
        'preferred_locale',
        'preferred_timezone',
        'preferred_branch_id',
        'last_login_at',
        'last_login_ip',
        'login_count',
        'failed_login_attempts',
        'locked_until',
        'force_password_change',
    ];

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

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'date_of_birth' => 'date',
            'password' => 'hashed',
            'is_super_admin' => 'boolean',
            'last_login_at' => 'datetime',
            'login_count' => 'integer',
            'failed_login_attempts' => 'integer',
            'locked_until' => 'datetime',
            'force_password_change' => 'boolean',
        ];
    }

    // ─── Credential normalisation ─────────────────────────────────
    // Stored in one canonical shape so that login can match on equality.
    // Set-only: nothing about how these are read changes.

    protected function email(): Attribute
    {
        return Attribute::set(fn (?string $value) => CredentialNormalizer::email($value));
    }

    protected function phone(): Attribute
    {
        return Attribute::set(fn (?string $value) => CredentialNormalizer::phone($value));
    }

    // ─── Relationships ────────────────────────────────────────────

    public function person(): BelongsTo
    {
        return $this->belongsTo(Person::class);
    }

    public function primaryRole(): BelongsTo
    {
        return $this->belongsTo(Role::class, 'role_id');
    }

    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class, 'role_user')
            ->withPivot('branch_id', 'assigned_by', 'created_at');
    }

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

    // ─── Helpers ──────────────────────────────────────────────────

    public function isLockedOut(): bool
    {
        return $this->locked_until !== null && $this->locked_until->isFuture();
    }

    public function hasPermission(string $permission): bool
    {
        return $this->roles
            ->flatMap(fn (Role $role) => $role->permissions)
            ->contains('name', $permission);
    }

    /**
     * Resolve branch_id from role_user pivot (primary role's branch assignment).
     * Falls back to session active_branch_id if no pivot branch set.
     */
    public function getBranchIdAttribute(): ?int
    {
        // First: check role_user pivot for explicit branch assignment
        $pivotBranch = $this->roles()
            ->wherePivotNotNull('branch_id')
            ->first()?->pivot?->branch_id;

        if ($pivotBranch) {
            return (int) $pivotBranch;
        }

        // Fallback: session-stored active branch (for branch switcher)
        return session('active_branch_id') ? (int) session('active_branch_id') : null;
    }
}
