<?php

namespace App\Models;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use App\Modules\Bookings\TaskFeedback;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\URL;
use Laravel\Sanctum\HasApiTokens;
use App\Models\Group;
use App\Models\AuthLogs;
use App\Models\ProfilePhoto;
use App\Modules\Chat\Channel;
use App\Modules\Bookings\Task;
use App\Modules\Clients\Client;
use App\Modules\Clients\Customer;
use App\Modules\Clients\SiteUser;
use App\Modules\Bookings\Part;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract
{
    use HasApiTokens, Authenticatable, Authorizable, CanResetPassword, HasFactory;

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'first_name', 'last_name', 'username', 'email', 'role','password', 'mobile', 'extension', 'direct_dial', 'asana_private_api_key', 'account_type', 'active', 'suspended', 'suspended_time', 'oauth_type', 'oauth_id', 'oauth_billing', 'mute_sounds', 'repair_target',
    ];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'remember_token'];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['name', 'profile_photo_url', 'feedback'];

    /**
     * The name used in polimorphisms.
     *
     * @var string
     */
    protected $morphClass = 'user';

    /**
     * The relations to eager load on every query.
     *
     * @var array
     */
    // protected $with = ['profile_photo', 'feedback_average'];

    /**
     * The list of users that are active.
     *
     * @var \Illuminate\Database\Eloquent\Collection
     */
    protected static $activeUserList;

    /**
     * Automatically hash the password when set on the user model.
     *
     * @return null
     */
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = Hash::make($value);
    }

    /**
     * Get the user groups.
     *
     * @return BelongsToMany
     */
    public function groups(): BelongsToMany
    {
        return $this->belongsToMany(Group::class);
    }

    /**
     * Get the user authentification logs.
     *
     * @return HasMany
     */
    public function auth_logs(): HasMany
    {
        return $this->hasMany(AuthLogs::class);
    }

    /**
     * Returns the user's profile photo.
     *
     * @return HasOne
     */
    public function profile_photo(): HasOne
    {
        return $this->hasOne(ProfilePhoto::class, 'user_id');
    }

    /**
     * Returns the user's Office365 token.
     *
     * @return HasOne
     */
    public function office365Token(): HasOne
    {
        return $this->hasOne(\App\Modules\Office365\Models\Office365Token::class, 'user_id');
    }

    /**
     * Returns the url to the profile photo.
     *
     * @return string
     */
    public function getProfilePhotoUrlAttribute()
    {
        // Check if user has uploaded a profile photo
        if ($this->profile_photo && file_exists(Config::get('files.photos_path').'/'.$this->profile_photo->filename)) {
            return URL::asset('photos/'.$this->profile_photo->filename);
        }

        // Fall back to Gravatar
        $name = str_replace(' ','+',$this->Name);
        return 'https://www.gravatar.com/avatar/'. md5( strtolower( trim( $this->email ) ) ) . '?d=https%3A%2F%2Fui-avatars.com%2Fapi%2F/'. $name.'/128?r=pg';
    }

    /**
     * Returns the profile photo as a base64 encoded image.
     *
     * @return string
     */
    public function getProfilePhotoImageAttribute()
    {
        return 'https://www.gravatar.com/avatar/';
        return $this->profile_photo && file_exists(Config::get('files.photos_path').'/'.$this->profile_photo->filename)
                ? 'data:image/png;base64,'. base64_encode(file_get_contents(Config::get('files.photos_path').'/'.$this->profile_photo->filename))
                : 'data:image/ng-submit="addComment($event)"png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACtElEQVR4Xu2Y3UtqURDFlyYVqQhBQon5koqYiUIElSD951qa+AkGUWDUgxC9KEhqfnfXgOLl3gKP2Xlw5kXO0T2zZ+2Z+eG2NBqNCdbYLCqAVoC2gM6ANZ6B0CGoFFAKKAWUAkqBNVZAMagYVAwqBhWDawwB/TOkGFQMKgYVg4pBxeAaK7A0Bh8fH/H6+orJZIL9/X0Eg0FYLJaZpH8og/v7e+zs7CAWi/313Ve6r8LnV7GWEuDh4QH1eh02m038D4dD+Hw++P1+eaYoqVRK3m9ubuLy8hJWq/XbeluFz+8CGhZgNBohnU7LiSYSCQwGA7y8vMDpdMLj8UjMWq0m72jb29u4uLhAu91GtVrFxsYG4vE43t/fwaS3trYQiURwc3OzsM/5ilu0mw0L0O/3ZbPj8ViS/vj4kBYIBAKyh263i0wmg8PDQ7RaLXQ6HRGKmy2VSmg2m9jd3ZV1/I5Vw/VGfS6a+PT3hgVgb5fLZfHDFmCZ05hEOByWJJl4MplEPp9Hr9ebCTAvHte4XC6cnp5iGZ+/LgBPOJvNSumen5/LKeZyOen1k5MTFItF2O12eL1ePD09SaUcHR3JM+35+Vne087OzqSKlvVpRATDFcAT5wxgb1OA6TMFCIVCqFQq/+yHA/Dq6gqcH9fX17Oq2dvbQzQanfkw4tNI8lxjWABOeFYAT+3g4EA+2ddutxvHx8dgmdOYdKFQkOR40kzu7u4Ob29vcDgcUjmsDg5ArjXq89cFYEAOsNvbWzlRGtuBvcwk542tQQFIAc4FCjKlB4Ug9zlHpjRZ1KcpFJhPkCij8UR/ylbh8397M9wCP5Wo2X5UAL0R0hshvRHSGyGzJ7GZ8ZUCSgGlgFJAKWDmFDY7tlJAKaAUUAooBcyexGbGVwooBZQCSgGlgJlT2OzYSoF1p8AnDSiNnx2jBucAAAAASUVORK5CYII=';
    }

    /**
     * Returns the full name of the user.
     *
     * @return string
     */
    public function getNameAttribute()
    {
        return $this->attributes['first_name'].' '.$this->attributes['last_name'];
    }

    /**
     * A user has an average feedback.
     *
     * @return HasOne
     */
    public function feedback_average(): HasOne
    {
        return $this->hasOne(TaskFeedback::class)->selectRaw('avg(rating) as average, task_feedback.*')->whereNotNull('rating');
    }

    /**
     * Returns the feedback average as a float.
     * This is the entire feedback as an average
     * that a user has received for all the jobs.
     *
     * @return float
     */
    public function getFeedbackAttribute()
    {
        return $this->feedback_average ? $this->feedback_average->average : 0;
    }

    /**
     * Retrieves a list of users that are active.
     *
     * @return Query
     */
    public static function getActiveList($columns = ['*'])
    {
        if (static::$activeUserList) {
            return static::$activeUserList;
        }
        $instance = new static();
        static::$activeUserList = $instance->newQuery()->with('profile_photo')->where('active', 1)->orderBy('first_name', 'asc')->get();
        return static::$activeUserList;
    }
    
    /**
     * Retrieves a list of users that are active.
     *
     * @return Query
     */
    public static function getActiveListWithGroup($columns = ['*'], $group = null)
    {
        $query = static::query()->with('profile_photo')->where('active', 1);
        if ($group) {
            $query->whereHas('groups', function ($q) use ($group) {
                $q->where('groups.id', $group);
            });
        }
        return $query->orderBy('first_name', 'asc')->get($columns);
    }

    /**
     * Retrieves all the user channels.
     *
     * @return BelongsToMany
     */
    public function channels(): BelongsToMany
    {
        return $this->belongsToMany(Channel::class, 'channel_user', 'user_id', 'channel_id');
    }

    /**
     * Retrieves all the subscribed tasks for the user.
     *
     * @return BelongsToMany
     */
    public function subscribed(): BelongsToMany
    {
        return $this->belongsToMany(Channel::class, 'channel_subscriptions', 'user_id', 'channel_id');
    }
    
    
     /**
     * Retrieves all the client subscriptions for the user.
     *
     * @return BelongsToMany
     */
    public function client_subscriptions(): BelongsToMany
    {
        return $this->belongsToMany(Client::class, 'client_user_subscriptions', 'user_id', 'client_id');
    }

    /**
     * Alias for client_subscriptions - used by ClientsController
     *
     * @return BelongsToMany
     */
    public function subscriptions(): BelongsToMany
    {
        return $this->client_subscriptions();
    }

    /**
     * Returns the Oauth user relation. If the user type is oauth, then he can either be a customer or a site user in which case of
     * these will be returned.
     *
     * @return BelongsTo|null
     */
    public function oauth(): ?BelongsTo
    {
        // This should be refactored if there is a real relationship
        return null;
    }

    /**
     * Returns all the parts for a particular user.
     *
     * @return MorphMany
     */
    public function parts(): MorphMany
    {
        return $this->morphMany(Part::class, 'related');
    }

    public function isMemberOf($groupId)
    {
        return $this->groups()->where('groups.id', $groupId)->exists();
    }

    public function hasPermission($permission)
    {
        return $this->groups()
            ->join('group_permissions', 'groups.id', '=', 'group_permissions.group_id')
            ->join('permissions', 'permissions.id', '=', 'group_permissions.permission_id')
            ->where('permissions.name', $permission)
            ->exists();
    }
}
