<?php

namespace App;

use App\Models\Site;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Laravel\Passport\HasApiTokens;

use Leenooks\Carbon;
use Leenooks\Traits\UserSwitch;
use App\Notifications\ResetPassword as ResetPasswordNotification;
use App\Models\Service;
use Spinen\QuickBooks\HasQuickBooksToken;

class User extends Authenticatable
{
    use HasApiTokens,Notifiable,UserSwitch,HasQuickBooksToken;

	protected $dates = ['created_at','updated_at','last_access'];

    /**
     * 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',
    ];

	protected $appends = [
		'active_display',
		'services_count_html',
		'surfirstname',
		'switch_url',
		'user_id_url',
	];

	protected $visible = [
		'active_display',
		'id',
		'level',
		'services_count_html',
		'switch_url',
		'surfirstname',
		'user_id_url',
	];

	public function accounts()
    {
		return $this->hasMany(Models\Account::class);
    }

	public function agents() {
		return $this->hasMany(static::class,'parent_id','id')->with('agents');
	}

	public function clients() {
		return $this->hasMany(static::class,'parent_id','id')->with('clients');
	}

	public function language()
	{
		return $this->belongsTo(Models\Language::class);
	}

	public function invoices()
	{
		return $this->hasManyThrough(Models\Invoice::class,Models\Account::class);
	}

	public function payments()
	{
		return $this->hasManyThrough(Models\Payment::class,Models\Account::class);
	}

	public function site()
	{
		return $this->belongsTo(Site::class);
	}

	public function services()
	{
		return $this->hasManyThrough(Models\Service::class,Models\Account::class);
	}

	protected function supplier()
	{
		return $this->belongsTo(static::class,'parent_id','id');
	}

	protected function suppliers() {
		return $this->hasMany(static::class,'parent_id','id');
	}

	/** Attributes **/

	public function getActiveDisplayAttribute($value)
	{
		return sprintf('<span class="btn-sm btn-block btn-%s text-center">%s</span>',$this->active ? 'primary' : 'danger',$this->active ? 'Active' : 'Inactive');
	}

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

	public function getInvoicesDueAttribute()
	{
		return $this->invoices
			->where('active',TRUE)
			->sortBy('id')
			->transform(function ($item) { if ($item->due > 0) return $item; })
			->reverse()
			->filter();
	}

	public function getLanguageAttribute($value)
	{
		if (is_null($this->language_id))
			return config('SITE_SETUP')->language;
	}

	/**
	 * Return a Carbon Date if it has a value.
	 *
	 * @param $value
	 * @return \Leenooks\Carbon
	 * @todo This attribute is not in the schema
	 */
	public function getLastAccessAttribute($value)
	{
		if (! is_null($value))
			return new Carbon($value);
	}

	/**
	 * @deprecated Use static::getFullNameAttribute()
	 * @return mixed
	 */
	public function getNameAttribute()
	{
		return $this->full_name;
	}

	public function getPaymentHistoryAttribute()
	{
		return $this->payments
			->sortBy('date_payment')
			->reverse();
	}

	public function getServicesActiveAttribute()
	{
		return $this->services->filter(function($item) {
			return $item->isActive();
		});
	}

	public function getServicesCountHtmlAttribute()
	{
		return sprintf('%s <small>/%s</small>',$this->services->where('active',TRUE)->count(),$this->services->count());
	}

	public function getSurFirstNameAttribute()
	{
		return sprintf('%s, %s',$this->lastname,$this->firstname);
	}

	public function getSwitchUrlAttribute()
	{
		return sprintf('<a href="/a/switch/start/%s"><i class="fa fa-external-link"></i></a>',$this->id);
	}

	public function getUserIdAttribute()
	{
		return sprintf('%02s-%04s',$this->site_id,$this->id);
	}

	public function getUserIdUrlAttribute()
	{
		return sprintf('<a href="/u/account/view/%s">%s</a>',$this->id,$this->user_id);
	}

	public function sendPasswordResetNotification($token)
	{
		$this->notify((new ResetPasswordNotification($token))->onQueue('high'));
	}

	/** Scopes **/

	public function scopeActive()
	{
		return $this->where('active',TRUE);
	}

	/**
	 * Determine if the user is an admin of the account with $id
	 *
	 * @param $id
	 * @return bool
	 */
	public function isAdmin($id)
	{
		return $id AND $this->isReseller() AND in_array($id,$this->all_accounts()->pluck('id')->toArray());
	}

	/** Functions */

	/**
	 * Get a list of accounts for the clients of this user
	 *
	 * @return DatabaseCollection
	 */
	public function all_accounts(): DatabaseCollection
	{
		$result = new DatabaseCollection();
		$clients = $this->all_clients();
		$clients->load('accounts');

		foreach ($clients->pluck('accounts') as $accounts) {
			foreach ($accounts as $o) {
				if (! $o->active)
					continue;

				$result->push($o);
			}
		}

		// Include my accounts
		foreach ($this->accounts as $o) {
			if (! $o->active)
				continue;

			$result->push($o);
		}

		return $result;
	}

	/**
	 * Get a list of clients that this user is responsible for.
	 *
	 * @param int $level
	 * @return Collection
	 */
	public function all_clients($level=0,DatabaseCollection $clients=NULL): DatabaseCollection
	{
		$result = is_null($clients) ? $this->clients : $clients;

		$result
			->filter(function($item) { return $item->active; })
			->transform(function($item) use ($level) { $item->level = $level; return $item; });

		foreach ($result->pluck('clients') as $clients) {
			foreach ($this->all_clients($level+1,$clients) as $o) {
				if (! $o->active)
					continue;

				$result->push($o);
			}
		}

		return $result;
	}

	public function all_client_service_inactive()
	{
		$s = Service::InActive();
		$aa = $this->all_accounts()->pluck('id')->unique()->toArray();

		return $s->get()->filter(function($item) use ($aa) {
			return in_array($item->account_id,$aa);
		});
	}

	public function all_client_service_movements()
	{
		$s = Service::active()->where('order_status','!=','ACTIVE');
		$aa = $this->all_accounts()->pluck('id')->unique()->toArray();

		return $s->get()->filter(function($item) use ($aa) {
			return in_array($item->account_id,$aa);
		});
	}

	// List all the agents, including agents of agents
	public function all_agents($level=0)
	{
		$result = collect();

		foreach ($this->agents as $o)
		{
			if (! $o->active OR ! $o->agents->count())
				continue;

			$o->level = $level;

			$result->push($o);

			// Include agents of agents
			$result->push($o->all_agents($level+1));
		}

		return $result->flatten();
	}

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

	public function isWholesaler()
	{
		return in_array($this->role(),['wholesaler']);
	}

	public function role()
	{
		// If I have agents and no parent, I am the wholesaler
		if (is_null($this->parent_id) AND ($this->all_agents()->count() OR $this->all_clients()->count()))
			return 'wholesaler';

		// If I have agents and a parent, I am a reseller
		elseif ($this->parent_id AND ($this->all_agents()->count() OR $this->all_clients()->count()))
			return 'reseller';

		// If I have no agents and a parent, I am a customer
		elseif (! $this->all_agents()->count() AND ! $this->all_clients()->count())
			return 'customer';
	}
}