<?php

namespace App;

use App\Extensions\CustomCompanyBillableTrait;
use App\Extensions\CustomStripeUserBillableTrait;
use App\Repositories\BoxRepository;
use App\Repositories\CompanyRepository;
use App\Repositories\UserRepository;
use Carbon\Carbon;
use Faker\Provider\DateTime;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Date;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    protected $fillable = [
        'name', 'surnames', 'phone', 'email', 'url_profile_photo', 'weight', 'height', 'gender', 'password', "nif",
        "address", "zipcode", "poblacio", "country", "date_of_birth"
    ];

    protected $hidden = [
        'password', 'remember_token', 'favorite_workouts'
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'date_of_birth' => 'date'
    ];

    protected $appends = ["fiscal_address","favorite_workouts_ids"];

    /*****************************************************************************
     *                           GETTERS I SETTERS
     ****************************************************************************/
    public function getFavoriteWorkoutsIdsAttribute() {
        $res = [];
        foreach($this->favorite_workouts as $w) {
            array_push($res, $w->id);
        }
        return $res;
    }

    public function getFiscalAddressAttribute() {
        return $this->fact_address . " " .
            $this->fact_poblacio . ", " .
            $this->fact_zipcode . " " .
            $this->fact_country;
    }

    public function setPasswordAttribute($val) {
        $this->attributes["password"] = bcrypt($val);
    }

    public function setDateOfBirthAttribute($val) {
        if(is_string($val)) $val = Carbon::parse($val);
        $this->attributes["date_of_birth"] = $val;
    }

    /*****************************************************************************
     *                              RELATIONSHIPS
     ****************************************************************************/

    public function company() {
        return $this->hasOne("App\Company", "owner_id");
    }

    public function trainingSessions() { // wods als que esta apuntat
        return $this->belongsToMany("App\TrainingSession", "user_training_sessions", "user_id", "training_session_id")
                    ->withPivot("type")
                    ->withTimestamps();
    }

    public function trainingSessionsToNotify() { // wods als que esta apuntat
        return $this->belongsToMany("App\TrainingSession", "notify_user_training_sessions", "user_id", "training_session_id")
            ->withTimestamps();
    }

    public function trainingSessionsCoach() { //wods que jo faig de coach
        return $this->hasMany("App\TrainingSession", "coach_id");
    }

    public function user_benchmarks() {
        return $this->hasMany("App\UserBenchmark", "user_id");
    }

    //user_subscriptions
    public function user_subscriptions() {
        return $this->hasMany("App\UserSubscription", "user_id");
    }

    public function active_user_subscriptions() {
        $s = [];
        foreach ($this->user_subscriptions as $us) {
            if ($us->active) array_push($s,$us);
        }
        return $s;
    }

    //box orders
    public function boxOrders() {
        return $this->hasMany("App\BoxOrder")->orderByDesc("updated_at");
    }

    public function stripe_users() {
        return $this->hasMany("App\StripeUser");
    }

    //subscription from a box
    public function getUserSubscription($idBox) {
        $pri = null;
        foreach ($this->user_subscriptions as $s){
            foreach($s->pricing->boxes as $b){
                if($idBox==$b->id){
                    $pri = $s->pricing;
                }
            }
        }

        foreach ($this->user_subscriptions as $s){
            if($s->pricing->id==$pri->id){
                return $s;
            }
        }
        return null;
    }

    //credits from a box
    public function getCredits($idBox){
        $pri = null;
        foreach ($this->user_subscriptions as $s){
            foreach($s->pricing->boxes as $b){
                if($idBox==$b->id){
                    $pri = $s->pricing;
                }
            }
        }

        foreach ($this->user_subscriptions as $s){
            if($s->pricing->id==$pri->id){
                return ["wod" => $s->wod_counter,"open" => $s->open_counter,"total" => $s->total_counter];
            }
        }
        return ["wod" => 0,"open" => 0,"total" => 0];
    }

    //Funció per leave i unirse a training session
    public function boxes_id() {
        $b = [];
        foreach ($this->user_subscriptions as $s){
            foreach($s->pricing->boxes as $box){
                array_push($b,$box->id);
            }
        }

        return array_unique($b);
    }

    public function orderPayments() {
        return $this->hasManyThrough("App\Payment", "App\BoxOrder");
    }

    public function favorite_workouts() {
        return $this->belongsToMany("App\TrainingWorkout");
    }

    public function availableWorkoutSessions() {
        $ts = [];
        $currentDate = Carbon::now();
        foreach ($this->active_user_subscriptions() as $s){
            foreach($s->pricing->boxes as $box){
                foreach ($box->trainingSessions as $trainingSession) {
                    if (!$this->trainingSessions->contains($trainingSession->id)) { // Ja estic apuntat
                        // return [$trainingSession, $box,$s->pricing];
                        $trainingSessionStartDay = Carbon::create($trainingSession->getOriginal('day'));
                        if ($trainingSessionStartDay->format('Y-m-d') >= $currentDate->format('Y-m-d')) { // Encara no ha pasat el dia
                            // return $s->pricing;
                            // return $trainingSession;

                            /* return [
                                $box->join_session_before_days ,
                                $currentDate ,
                                Carbon::parse($trainingSession->start_time)->addDays(-($box->join_session_before_days)),
                                $trainingSession->date_start , $currentDate
                            ];*/

                            if ((($box->join_session_before_days && $currentDate > Carbon::parse($trainingSession->start_time)->addDays(-($box->join_session_before_days))) ||
                                    !$box->join_session_before_days)
                                && $trainingSession->date_start >= $currentDate
                            ) {
                                if ($s->pricing->start_time && $s->pricing->end_time) { // La tarifa te horari restringit
                                    /*return [$s->pricing->start_time < $trainingSession->start_time,
                                        $s->pricing->end_time > $trainingSession->start_time,
                                        $s->pricing->end_time > $trainingSession->end_time,
                                        $trainingSession, $s->pricing];*/
                                    if ($s->pricing->start_time < $trainingSession->start_time && $s->pricing->end_time > $trainingSession->start_time // Comença dins el meu horari
                                        && $s->pricing->end_time > $trainingSession->end_time // Acaba dins del meu horari
                                    ) array_push($ts, $trainingSession);
                                } else {
                                    array_push($ts, $trainingSession);
                                }
                            }
                        }
                    }
                }
            }
        }

        return array_unique($ts);
    }

    public function availableWorkoutSessionsId() {
        $ts = [];
        $currentDate = Carbon::now();
        foreach ($this->active_user_subscriptions() as $s){
            foreach($s->pricing->boxes as $box){
                foreach ($box->trainingSessions as $trainingSession) {
                    // return [$trainingSession, $box,$s->pricing];
                    if (!$this->trainingSessions->contains($trainingSession->id)) { // Ja estic apuntat
                        $trainingSessionStartDay = Carbon::create($trainingSession->getOriginal('day'));
                        if ($trainingSessionStartDay->format('Y-m-d') >= $currentDate->format('Y-m-d')) { // Encara no ha pasat el dia
                            // return $s->pricing;
                            // return $trainingSession;

                            /* return [
                                $box->join_session_before_days ,
                                $currentDate ,
                                Carbon::parse($trainingSession->start_time)->addDays(-($box->join_session_before_days)),
                                $trainingSession->date_start , $currentDate
                            ];*/
                            if ((($box->join_session_before_days && $currentDate > Carbon::parse($trainingSession->start_time)->addDays(-($box->join_session_before_days))) ||
                                    !$box->join_session_before_days)
                                && $trainingSession->date_start >= $currentDate
                            ) {
                                if ($s->pricing->start_time && $s->pricing->end_time) { // La tarifa te horari restringit
                                    /*return [$s->pricing->start_time < $trainingSession->start_time,
                                        $s->pricing->end_time > $trainingSession->start_time,
                                        $s->pricing->end_time > $trainingSession->end_time,
                                        $trainingSession, $s->pricing];*/
                                    if ($s->pricing->start_time < $trainingSession->start_time && $s->pricing->end_time > $trainingSession->start_time // Comença dins el meu horari
                                        && $s->pricing->end_time > $trainingSession->end_time // Acaba dins del meu horari
                                    ) array_push($ts, $trainingSession->id);
                                } else {
                                    array_push($ts, $trainingSession->id);
                                }
                            }
                        }
                    }
                }
            }
        }

        return array_unique($ts);
    }

    public function availableWorkoutSessionsToLeave() {
        $ts = [];
        $currentDate = new \DateTime();
        foreach ($this->active_user_subscriptions() as $s){
            foreach($s->pricing->boxes as $box){
                foreach ($box->trainingSessions as $trainingSession) {
                    if ( $this->trainingSessions->contains($trainingSession->id) ) { // Si estic apuntat a aquesta sessio
                        $trainingSessionStartDay = Carbon::create($trainingSession->getOriginal('day'))->format('Y-m-d');
                        if ( $currentDate->format('Y-m-d') < $trainingSessionStartDay ){
                            array_push($ts,$trainingSession);
                        } else {
                            $trainingSessionDate = new \DateTime($trainingSessionStartDay . " " . $trainingSession->start_time);
                            $diff = $currentDate->diff($trainingSessionDate);
                            if ((($diff->h*60 + $diff->i) > ($box->unjoin_session_before_minutes)) && $currentDate<$trainingSessionDate ) array_push($ts,$trainingSession);
                        }
                    }
                }
            }
        }

        return array_unique($ts);
    }

    public function availableWorkoutSessionsToLeaveId() {
        $ts = [];
        $currentDate = new \DateTime();
        foreach ($this->active_user_subscriptions() as $s){
            foreach($s->pricing->boxes as $box){
                foreach ($box->trainingSessions as $trainingSession) {
                    if ( $this->trainingSessions->contains($trainingSession->id) ) { // Si estic apuntat a aquesta sessio
                        $trainingSessionStartDay = Carbon::create($trainingSession->getOriginal('day'))->format('Y-m-d');
                        if ( $currentDate->format('Y-m-d') < $trainingSessionStartDay ){
                            array_push($ts,$trainingSession->id);
                        } else {
                            $trainingSessionDate = new \DateTime($trainingSessionStartDay . " " . $trainingSession->start_time);
                            $diff = $currentDate->diff($trainingSessionDate);
                            if ((($diff->h*60 + $diff->i) > ($box->unjoin_session_before_minutes)) && $currentDate<$trainingSessionDate ) array_push($ts,$trainingSession->id);
                        }
                    }
                }
            }
        }

        return array_unique($ts);
    }

    public function invoices() {
        $user = $this;
        $i = [];
        foreach ($user->boxOrders as $boxOrder) {
            if($boxOrder->invoice!=null) array_push($i,$boxOrder->invoice);
        }
        return $i;
    }

    public function updateFavoriteWorkout($idWorkout) {
        $exists = $this->favorite_workouts()->where('training_workout_id','=',$idWorkout)->first();
        if ($exists) $this->favorite_workouts()->detach($idWorkout);
        else $this->favorite_workouts()->attach($idWorkout);
        return $this;
    }

    /*****************************************************************************
     *                              ROLE AUTHORIZATION
     ****************************************************************************/
    public function itsMe($id) {
        return $this->id == $id;
    }

    /**
     * @param String $role. Possible values = "athlete" || "coach" || "box_admin" || "company_admin" || "super_admin"
     * @param null $id. Pot ser tant company id com box id
     * @param array $options.
     *      -> "active" => sets if the user subscription must be active or not
     *      -> "company_active" => sets if the company plan subscription must be active or not
     *      -> "level" => "default" || "all". If role is athlete, means that $id is a company_id. If role is coach, means
     *                                        that $id is a company_id. If role is box_admin, means that $id is a
     *                                        company_id. If role is company_admin, means that $id is a box_id .
     */
    public function authorize(String $role, $id = null, array $options = ["active" => true, "company_active" => true, "level" => "default"]) {
        $valid = false;
        $role = str_replace("-", "_", $role); //per si de cas

        if(!isset($options["active"]) || $options["active"]===null) $options["active"] = true;
        if(!isset($options["company_active"]) || $options["company_active"]===null) $options["company_active"] = true;
        if(!isset($options["level"]) || $options["level"]===null) $options["level"] = "default";
        if($role!=="super_admin" && $id==null) throw new \Exception("Authorization error: You haven't specified company_id or box_id.");
        if(!$this->isSuperAdmin()) {
            switch($role) {
                case "athlete": //Almenys és atleta
                    $valid = $this->isAthlete($id, $options);
                    break;
                case "coach": //Almenys es coach
                    $valid = $this->isCoach($id, $options);
                    break;
                case "box_admin": //Almenys es box admin
                    $valid = $this->isBoxAdmin($id, $options);
                    break;
                case "company_admin": //Almenys es company admin
                    $valid = $this->isCompanyAdmin($id, $options);
                    break;
                case "super_admin": //es super user
                    $valid = $this->isSuperAdmin($options);
                    break;
                default:
                    abort(401, "Role to check provided is not valid.");
                    break;
            }

            //Autoritzem
            abort_unless($valid, 401, "Unauthorized");
        }


    }

    //if is at least athlete
    public function isAthlete($box_id, array $options = ["active" => true, "company_active" => true, "level" => "default"]) {
        //agafem subscripcions actives
        $level = isset($options["level"]) ? $options["level"] : "default";
        if($level === "default") {
            $user_subscriptions = UserRepository::userSubscriptions($this->id,  null, $box_id, $options);
        }
        else {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, $box_id, null, $options);
        }
        return !empty($user_subscriptions); //si hi ha almenys una subscripció ja tindra permissos d'atleta segur
    }

    //if is at least coach
    public function isCoach($box_id, array $options = ["active" => true, "company_active" => true, "level" => "default"]) {
        $level = isset($options["level"]) ? $options["level"] : "default";
        if($level === "default") {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, null, $box_id, $options);
        }
        else {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, $box_id, null, $options);
        }

        foreach($user_subscriptions as $sub) {
            $sub_model = UserSubscription::findOrFail($sub->id);
            $valid = $sub->role->isCoach() || $sub_model->role->isBoxAdmin() || $sub_model->role->isCompanyAdmin() || $sub_model->role->isSuperAdmin();
            if($valid) return true;
        }
        return false;
    }

    //if is at least box admin
    public function isBoxAdmin($box_id, array $options = ["active" => true, "company_active" => true, "level" => "default"]) {
        $level = isset($options["level"]) ? $options["level"] : "default";
        if($level === "default") {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, null, $box_id, $options);
        }
        else {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, $box_id, null, $options);
        }
        foreach($user_subscriptions as $sub) {
            $sub_model = UserSubscription::findOrFail($sub->id);
            $valid = $sub_model->role->isBoxAdmin() || $sub_model->role->isCompanyAdmin() || $sub_model->role->isSuperAdmin();
            if($valid) return true;
        }
        return false;
    }

    //if is at least company admin
    public function isCompanyAdmin($company_id, array $options = ["active" => true, "company_active" => true, "level" => "default"]) {
        $level = isset($options["level"]) ? $options["level"] : "default";

        if($level === "default") {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, $company_id, null, $options);
        }
        else {
            $user_subscriptions = UserRepository::userSubscriptions($this->id, null, $company_id, $options);
        }
        foreach($user_subscriptions as $sub) {
            $sub_model = UserSubscription::findOrFail($sub->id);
            $valid = $sub_model->role->isCompanyAdmin() || $sub_model->role->isSuperAdmin();
            if($valid) return true;
        }
        return false;
    }

    //if is super admin
    public function isSuperAdmin(array $options = ["active" => true, "company_active" => false, "level" => "default"]) {
        $user_subscriptions = UserRepository::userSubscriptions($this->id, null, null, ["active" => true, "company_active" => false]);
        foreach($user_subscriptions as $sub) {
            $sub_model = $sub->role ? $sub : UserSubscription::findOrFail($sub->id);
            $valid = $sub_model->role->isSuperAdmin();
            if($valid) return true;
        }
        return false;
    }

    /*****************************************************************************
     *                              JWT AUTHENTICATION
     ****************************************************************************/
    //JWT Authentication
    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier() {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }


    /*****************************************************************************
     *                              NOTIFICATIONS
     ****************************************************************************/
    /**
     * Route notifications for the Nexmo channel.
     *
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return string
     */
    public function routeNotificationForNexmo($notification) {
        return $this->phone;
    }

}
