<?php

namespace App\Models;

use App\Helpers\NumberHelper;
use App\Traits\BelongsToVendor;
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;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use BelongsToVendor, HasApiTokens, HasFactory, Notifiable, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'vendor_id',
        'name',
        'username',
        'password',
        'mobile',
        'notes',
        'bank_accounts',
        'is_active',
        'is_customer',
        'is_featured',
    ];

    /**
     * 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',
            'bank_accounts' => 'array',
            'is_active' => 'boolean',
            'is_customer' => 'boolean',
            'is_featured' => 'boolean',
        ];
    }

    public function roles()
    {
        return $this->belongsToMany(Role::class)->withTimestamps();
    }

    /** وندوری که این کاربر مالک آن است (در صورت وجود). */
    public function ownedVendor()
    {
        return $this->hasOne(Vendor::class, 'owner_id');
    }

    /** دسترسی‌های مستقیم کاربر (علاوه بر دسترسی‌های نقش‌ها) */
    public function permissions()
    {
        return $this->belongsToMany(Permission::class)->withTimestamps();
    }

    /** کش درون‌درخواستی لیست نام دسترسی‌ها */
    protected static ?array $permissionCache = [];

    /**
     * لیست نام تمام دسترسی‌های کاربر (از نقش‌ها + مستقیم)، با کش درون‌درخواستی.
     *
     * @return array<string>
     */
    public function getPermissionNames(): array
    {
        $key = 'acl_' . $this->id;
        if (! isset(self::$permissionCache[$key])) {
            $viaRoles = $this->roles()->with('permissions')->get()->pluck('permissions')->flatten();
            $direct = $this->permissions()->get();
            self::$permissionCache[$key] = $viaRoles->merge($direct)->pluck('name')->unique()->values()->all();
        }
        return self::$permissionCache[$key];
    }

    /**
     * پاک کردن کش دسترسی‌ها (بعد از تغییر نقش/دسترسی کاربر).
     */
    public function clearPermissionCache(): void
    {
        unset(self::$permissionCache['acl_' . $this->id]);
    }

    public function hasRole(string $role): bool
    {
        return $this->roles()->where('name', $role)->exists();
    }

    /**
     * آیا کاربر این دسترسی را دارد؟
     * ادمین همیشه بله؛ بقیه از نقش + دسترسی مستقیم.
     * دسترسی manage_* (مثلاً manage_orders) به‌صورت ضمنی تمام دسترسی‌های آن منبع را می‌دهد.
     */
    public function hasPermission(string $permission): bool
    {
        if ($this->hasRole('admin')) {
            return true;
        }
        $names = $this->getPermissionNames();
        if (in_array($permission, $names, true)) {
            return true;
        }
        $parts = explode('_', $permission, 2);
        if (count($parts) === 2) {
            $manage = 'manage_' . $parts[1];
            if (in_array($manage, $names, true)) {
                return true;
            }
        }
        return false;
    }

    /** اختصاص دسترسی مستقیم به کاربر */
    public function givePermissionTo(string|Permission $permission): void
    {
        $perm = $permission instanceof Permission
            ? $permission
            : Permission::where('name', $permission)->firstOrFail();
        $this->permissions()->syncWithoutDetaching([$perm->id]);
        $this->clearPermissionCache();
    }

    /** گرفتن دسترسی مستقیم از کاربر */
    public function revokePermissionTo(string|Permission $permission): void
    {
        $perm = $permission instanceof Permission
            ? $permission
            : Permission::where('name', $permission)->firstOrFail();
        $this->permissions()->detach($perm->id);
        $this->clearPermissionCache();
    }

    /**
     * Check if user is a customer.
     */
    public function isCustomer(): bool
    {
        return $this->is_customer || $this->hasRole('customer');
    }

    // ================== Customer Relationships ==================

    /**
     * Get orders for this user (customer).
     */
    public function orders()
    {
        return $this->hasMany(Order::class);
    }

    /**
     * Transactions where this user is the payer (from).
     */
    public function transactionsAsPayer()
    {
        return $this->hasMany(Transaction::class, 'from_user_id');
    }

    /**
     * Transactions where this user is the receiver (to).
     */
    public function transactionsAsReceiver()
    {
        return $this->hasMany(Transaction::class, 'to_user_id');
    }

    /**
     * Allocations where this user is the debtor.
     */
    public function debtorAllocations()
    {
        return $this->hasMany(TransactionAllocation::class, 'debtor_user_id');
    }

    /**
     * Allocations where this user is the creditor.
     */
    public function creditorAllocations()
    {
        return $this->hasMany(TransactionAllocation::class, 'creditor_user_id');
    }

    // Ensure numeric-like string fields are always stored with English digits
    public function setMobileAttribute($value)
    {
        $this->attributes['mobile'] = $value !== null
            ? NumberHelper::persianToEnglish($value)
            : null;
    }

    /**
     * Scope for customers only.
     */
    public function scopeCustomers($query)
    {
        return $query->where('is_customer', true);
    }

    /**
     * Scope for featured customers (shown on dashboard).
     */
    public function scopeFeatured($query)
    {
        return $query->where('is_featured', true);
    }

    /**
     * Scope for non-customers (admins/staff).
     */
    public function scopeStaff($query)
    {
        return $query->where('is_customer', false);
    }

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

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

    /**
     * Scope for users created in date range.
     */
    public function scopeCreatedBetween($query, string $from, string $to)
    {
        return $query->whereDate('created_at', '>=', $from)
                     ->whereDate('created_at', '<=', $to);
    }

    /**
     * Scope for users created this month.
     */
    public function scopeCreatedThisMonth($query)
    {
        return $query->whereMonth('created_at', now()->month)
                     ->whereYear('created_at', now()->year);
    }

    /**
     * Scope for users created today.
     */
    public function scopeCreatedToday($query)
    {
        return $query->whereDate('created_at', today());
    }

    /**
     * Scope to search by name or mobile.
     */
    public function scopeSearch($query, string $term)
    {
        $normalizedTerm = NumberHelper::persianToEnglish($term);
        
        return $query->where(function ($q) use ($term, $normalizedTerm) {
            $q->where('name', 'like', "%{$term}%")
              ->orWhere('mobile', 'like', "%{$normalizedTerm}%")
              ->orWhere('username', 'like', "%{$normalizedTerm}%");
        });
    }

    /**
     * Scope for users with orders.
     */
    public function scopeWithOrders($query)
    {
        return $query->whereHas('orders');
    }

    /**
     * Scope for users without orders.
     */
    public function scopeWithoutOrders($query)
    {
        return $query->whereDoesntHave('orders');
    }

    /**
     * Scope for users with a specific role.
     */
    public function scopeWithRole($query, string $roleName)
    {
        return $query->whereHas('roles', function ($q) use ($roleName) {
            $q->where('name', $roleName);
        });
    }
}
