<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Log;
use Laravel\Passport\HasApiTokens;
use Leenooks\Traits\ScopeActive;
use Leenooks\Traits\UserSwitch;

use App\Interfaces\IDs;
use App\Notifications\ResetPassword as ResetPasswordNotification;
use App\Traits\SiteID;

/**
 * Class User
 *
 * Attributes for users:
 * + role				: User's role
 */
class User extends Authenticatable implements IDs
{
	use HasFactory,HasApiTokens,Notifiable,UserSwitch,ScopeActive,SiteID;

	private const CACHE_TIME = 3600;

	protected $casts = [
		'last_access' => 'datetime:Y-m-d H:i:s',
		'passkey' => 'json',
	];

	/**
	 * The attributes that are mass assignable.
	 *
	 * @var array
	 */
	protected $fillable = [
		'name',
		'email',
		'password',
	];

	/**
	 * The attributes that should be hidden for arrays.
	 *
	 * @var array
	 */
	protected $hidden = [
		'password',
		'remember_token',
	];

	/**
	 * Role hierarchy order
	 *
	 * @var array
	 */
	public const role_order = [
		'wholesaler',
		'reseller',
		'customer',
	];

	/* OVERRIDES */

	/**
	 * Users password reset email notification
	 *
	 * @param string $token
	 */
	public function sendPasswordResetNotification($token)
	{
		$this->notify((new ResetPasswordNotification($token))
			->onQueue('user'));
	}

	/* INTERFACES */

	public function getLIDAttribute(): string
	{
		return sprintf('#%04s',$this->id);
	}

	public function getSIDAttribute(): string
	{
		return sprintf('%02s-%s',$this->site_id,$this->getLIDAttribute());
	}

	/* RELATIONS */

	/**
	 * This user's accounts
	 */
	public function accounts()
	{
		return $this->hasMany(Account::class)
			->active();
	}

	/**
	 * The accounts that this user manages
	 *
	 * @return \Illuminate\Database\Eloquent\Relations\HasMany
	 * @note This cannot be preloaded with load() or with() - $this is a blank object
	 */
	public function accounts_all()
	{
		return $this->hasMany(Account::class)
			->when((! is_null($this->rtm) && $this->id),
				fn($query)=>$query->orWhereIn('rtm_id',$this->rtm?->children_all()->pluck('id')))
			->active();
	}

	/**
	 * This users language configuration
	 *
	 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
	 */
	public function language()
	{
		return $this->belongsTo(Language::class);
	}

	/**
	 * Return the routes to market account for this user
	 */
	public function rtm()
	{
		return $this->hasOneThrough(Rtm::class,Account::class);
	}

	/* ATTRIBUTES */

	/**
	 * Logged in user's full name
	 *
	 * @return string
	 * @deprecated use getNameFullAttribute()
	 */
	public function getFullNameAttribute(): string
	{
		Log::alert('UMO:! Deprecated function getFullNameAttribute()');
		return $this->getNameFullAttribute();
	}

	/**
	 * This is an alias method, as it is used by the framework
	 *
	 * @return string
	 */
	public function getNameAttribute(): string
	{
		return $this->name_full;
	}

	/**
	 * Logged in user's full name
	 *
	 * @return string
	 */
	public function getNameFullAttribute(): string
	{
		return sprintf('%s %s',$this->firstname,$this->lastname);
	}

	/**
	 * Return the name, surname first
	 *
	 * @return string
	 */
	public function getNameSurFirstAttribute()
	{
		return sprintf('%s, %s',$this->lastname,$this->firstname);
	}

	/**
	 * Return a friendly string of this persons role
	 *
	 * @return string
	 */
	public function getRoleAttribute(): string
	{
		return ucfirst($this->role());
	}

	/**
	 * Return the name, surname first
	 * @return string
	 * @deprecated use getNameSurFirstAttribute()
	 */
	public function getSurFirstNameAttribute()
	{
		Log::alert('UMO:! Deprecated function getSurFirstNameAttribute()');
		return $this->getNameSurFirstAttribute();
	}

	/* SCOPES */

	/**
	 * Search for a record
	 *
	 * @param		$query
	 * @param string $term
	 */
	public function scopeSearch($query,string $term)
	{
		// Build our where clause
		// First Name, Last name
		if (preg_match('/\s+/',$term)) {
			[$fn,$ln] = explode(' ',$term,2);

			$query->where(function($query1) use ($fn,$ln,$term) {
				$query1->where(function($query2) use ($fn,$ln) {
					return $query2
						->where('firstname','ilike','%'.$fn.'%')
						->where('lastname','ilike','%'.$ln.'%');
				});
			});

		} elseif (is_numeric($term)) {
			$query->where('users.id','like','%'.$term.'%');

		} elseif (preg_match('/@/',$term)) {
			$query->where('email','ilike','%'.$term.'%');

		} else {
			$query
				->Where('firstname','ilike','%'.$term.'%')
				->orWhere('lastname','ilike','%'.$term.'%');
		}

		return $query;
	}

	/* METHODS */

	/**
	 * Determine if the user is an admin of the user with $id
	 *
	 * @param User|null $user
	 * @return bool
	 */
	public function isAdmin(User $user=NULL): bool
	{
		return $user->exists
			&& $this->isReseller()
			&& $this->accounts_all
				->pluck('user_id')
				->contains($user->id);
	}

	/**
	 * Determine if the logged in user is a reseller or wholesaler
	 *
	 * @return bool
	 */
	public function isReseller(): bool
	{
		return in_array($this->role(),['wholesaler','reseller']);
	}

	/**
	 * Determine if the logged in user is a wholesaler
	 *
	 * @return bool
	 */
	public function isWholesaler(): bool
	{
		return in_array($this->role(),['wholesaler']);
	}

	/**
	 * Determine what the logged in user's role is
	 * + Wholesaler - aka Super User
	 * + Reseller - services accounts on behalf of their customers
	 * + Customer - end user customer
	 *
	 * @return string
	 */
	public function role()
	{
		return $this->rtm
			? ($this->rtm->parent_id ? 'reseller' : 'wholesaler')
			: 'customer';
	}
}