<?php

namespace App\Packages\User\Models;

use App\Packages\Capsule\Models\Capsule;
use App\Packages\Capsule\Models\CapsuleComment;
use App\Packages\Capsule\Models\CapsuleSubscriber;
use App\Packages\Core\Models\Localization;
use App\Packages\Core\Traits\RepositoryTrait;
use App\Packages\User\Repository\UserRepository;
use Hash;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
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\SoftDeletes;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Ramsey\Uuid\Uuid;

/**
 * Class User
 * @package App\Models
 *
 * @property int $id
 * @property string $uuid
 * @property string $first_name
 * @property string $last_name
 * @property string $nickname
 * @property string $password
 * @property boolean $is_active
 * @property boolean $is_verified
 * @property boolean $is_private
 *
 * @property string $email
 * @property string $phone_number
 * @property string $phone_code
 *
 * @property string $phone_hash
 *
 * @property string $fb_id
 * @property string $google_id
 * @property string $apple_id
 *
 * @property int $avatar_id
 * @property int $original_avatar_id
 *
 * @property bool $is_suggested
 * @property int $suggested_position
 * @property int $search_position
 *
 * @property string $ios_push_token
 *
 * @property boolean $is_notification_push
 * @property boolean $is_notification_app
 * @property boolean $is_registered
 * @property boolean $is_on_boarding_done
 * @property boolean $is_system_user
 *
 * @property int $count_friends
 * @property int $count_capsules
 * @property int $count_comments
 * @property int $count_subscriptions
 * @property int $last_notification_seen
 * @property string|Carbon $last_follower_seen
 * @property string|Carbon $last_follower_confirmation_seen
 *
 * @property string|Carbon $last_activity_at
 *
 * @property string|Carbon $created_at
 * @property string|Carbon $updated_at
 * @property string|Carbon $deleted_at
 *
 * @property-read string $full_name
 *
 * @property-read Localization $nativeLanguage
 * @property-read UserAvatar $avatar
 * @property-read UserAvatar $originalAvatar
 * @property-read User[]|Collection $friends
 *
 * @method static UserRepository repository()
 */
class User extends Model implements AuthenticatableContract, AuthorizableContract
{
    use Notifiable,
        Authenticatable,
        Authorizable,
        RepositoryTrait;

    use SoftDeletes;

    const WEB_GUARD = 'web_users';

    const FACEBOOK_ID = 'fb_id';
    const GOOGLE_ID = 'google_id';
    const APPLE_ID = 'apple_id';

    /** @var string */
    public static $repositoryClass = UserRepository::class;

    /** @var string */
    protected $table = 'table_user';

    /** @var array */
    protected $hidden = [
        'password',
    ];

    /** @var array */
    protected $dates = [
        'created_at',
        'updated_at',
        'last_activity_at',
        'last_follower_seen',
        'last_follower_confirmation_seen',
        'deleted_at',
    ];

    public static function boot()
    {
        parent::boot();

        static::creating(function (User $model) {
            $model->uuid = Uuid::uuid4();
            $model->last_notification_seen = intMicroTime();
        });
    }

    /**
     * @param $phoneCode
     * @param $phoneNumber
     * @return $this
     */
    public function makeRegistered($phoneCode, $phoneNumber)
    {
        $this->is_registered = true;
        $this->phone_hash = phoneHash($phoneCode, $phoneNumber);
        $this->phone_code = $phoneCode;
        $this->phone_number = $phoneNumber;
        $this->save();

        return $this;
    }

    /**
     * @return $this
     */
    public function setLastActivityNow()
    {
        $this->last_activity_at = Carbon::now();
        $this->save();

        return $this;
    }

    public function setLastFollowerSeen()
    {
        $this->last_follower_seen = Carbon::now();
        $this->save();

        return $this;
    }

    public function setLastConfirmationFollowerSeen()
    {
        $this->last_follower_confirmation_seen = Carbon::now();
        $this->save();

        return $this;
    }

    /**
     * @return $this
     */
    public function refreshSearchPosition()
    {
        $this->search_position = $this->search_position + 1;
        $this->save();

        return $this;
    }

    /**
     * @return $this
     */
    public function refreshFriendCount()
    {
        $this->count_friends = $this->friends()->count();
        $this->save();

        return $this;
    }

    /**
     * @return BelongsToMany|User[]
     */
    public function friends()
    {
        return $this->belongsToMany(User::class, 'table_friend_relation', 'user_id', 'friend_id');
    }

    /**
     * @TODO: Refactore
     *
     * @return $this
     */
    public function refreshCommentCount()
    {
        $this->count_comments = $this->comments->count();
        $this->save();

        return $this;
    }

    /**
     * @return $this
     */
    public function refreshSubscriptionCount()
    {
        $this->count_subscriptions = $this->subscriptions()->count();
        $this->save();

        return $this;
    }

    /**
     * @return HasMany|CapsuleSubscriber[]
     */
    public function subscriptions()
    {
        return $this->hasMany(CapsuleSubscriber::class, 'user_id');
    }

    /**
     * @return $this
     */
    public function updateNotificationSeen()
    {
        $this->last_notification_seen = intMicroTime();
        $this->save();

        return $this;
    }

    /**
     * @TODO: Refactore to service
     *
     * @return UserToken
     */
    public function generateToken()
    {
        $this->removeOldTokens();

        $token = new UserToken();
        $token->user_id = $this->id;
        $token->access_token = str_random(120);
        $token->save();

        return $token;
    }

    /**
     * @TODO: Separate service
     *
     * Remove old tokens
     */
    public function removeOldTokens()
    {
        UserToken::query()
            ->offset(3)
            ->delete();
    }

    /**
     * @TODO: Refactore
     */
    public function saveAccess($name = '')
    {
        $access = new UserAccessLog();
        $access->endpoint_name = $name;

        $this->accessLog()->save($access);
        $this->save();

        return $this;
    }

    /**
     * @return HasMany|UserAccessLog[]
     */
    public function accessLog()
    {
        return $this->hasMany(UserAccessLog::class, 'user_id');
    }

    /**
     * @return string
     */
    public function avatarUrl()
    {
        if ( ! $this->avatar ) {
            return asset('/samples/avatar/def.png');
        }

        return $this->avatar->getAwsFullPath();
    }

    /**
     * @return string
     */
    public function originalAvatarUrl()
    {
        if ( ! $this->originalAvatar ) return false;

        return $this->originalAvatar->getAwsFullPath();
    }

    /**
     * @return BelongsTo
     */
    public function avatar()
    {
        return $this->belongsTo(UserAvatar::class, 'avatar_id');
    }

    /**
     * @return BelongsTo
     */
    public function originalAvatar()
    {
        return $this->belongsTo(UserAvatar::class, 'original_avatar_id');
    }

    /**
     * @param $password
     *
     * @return bool
     */
    public function checkPassword($password)
    {
        return Hash::check($password, $this->password);
    }

    /**
     * @return string
     */
    public function getFullNameAttribute()
    {
        return ucfirst($this->first_name).' '.ucfirst($this->last_name);
    }

    /**
     * @return HasMany|Capsule[]
     */
    public function capsules()
    {
        return $this->hasMany(Capsule::class, 'user_id');
    }

    /**
     * @return HasMany|CapsuleComment[]
     */
    public function comments()
    {
        return $this->hasMany(CapsuleComment::class, 'user_id');
    }

    /**
     * @return BelongsTo
     */
    public function nativeLanguage()
    {
        return $this->belongsTo(Localization::class, 'native_language_id');
    }
}
