Sh3ll
OdayForums


Server : Apache
System : Linux 145.162.205.92.host.secureserver.net 5.14.0-611.45.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Apr 1 05:56:53 EDT 2026 x86_64
User : tradze ( 1001)
PHP Version : 8.1.34
Disable Function : NONE
Directory :  /home/tradze/www/test.tradze.com/app/Modules/Schedules/Repositories/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/tradze/www/test.tradze.com/app/Modules/Schedules/Repositories/BookingClass.php_200121
<?php
namespace App\Modules\Schedules\Repositories;

use App\Modules\Invoices\Repositories\BookingInvoiceRepository;
use App\Modules\Schedules\Models\BookingOrder;
use App\Modules\Schedules\Models\Schedule;
use App\Modules\Postcodes\Models\Postcode;
use App\Modules\Services\Models\ServiceDuration;
use App\Modules\Services\Models\ServiceType;
use App\Modules\Users\Repositories\MessageThreadRepository;
use App\Modules\Vouchers\Models\Voucher;
use App\Modules\Vouchers\Repositories\VoucherRepository;
use App\User;
use App\Modules\Services\Models\FocalPoint;
use App\Modules\Schedules\Models\ScheduleDaysOff;
use Bican\Roles\Models\Role;
use Braintree\Transaction;
use Carbon\Carbon;
use Cmgmyr\Messenger\Models\Message;
use Cmgmyr\Messenger\Models\Participant;
use Cmgmyr\Messenger\Models\Thread;
use GoogleMaps\GoogleMaps;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Mockery\CountValidator\Exception;
use App\Modules\Notifications\Facades\NotifRepository;
use Omnipay\Omnipay;

class BookingClass
{
    var $defaultServiceDuration = null;
    public function __construct()
    {
        $this->defaultServiceDuration = ServiceDuration::where('is_default',1)->first();
    }
    /**
     * Get available therapists
     * @return array
     */
    public function therapists()
    {
        $booking = Session::get('booking');
        //dd($booking);
        $users = collect();
        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['postcode', 'duration', 'massage', 'date'];
        foreach ($mandatory_fields as $field) {
            if (!isset($booking[$field]))
                return $users;
        } //endforeach

        //get postcode zone
        $postcode = Postcode::where('postcode', $booking['postcode'])->first();

        //users are not available for the past dates
        $today = Carbon::today();
        $dateC = Carbon::createFromFormat('Y-m-d', $booking['date']);
        $diff = $today->diffInHours($dateC, false);
        if ($diff < 0)
            return $users;

        //query therapists
        DB::enableQueryLog();
        $users = User::OfTherapists()
            ->whereHas('servicetypes', function ($query) use ($booking) {

                if (isset($booking['massage']) && $booking['massage'] > 0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->whereHas('zones', function ($query) use ($postcode) {
                $query->where('users_area_coverage.zone_id', $postcode->zone_id);
                return $query;
            })
            ->whereHas('workingdays', function ($query) use ($booking) {
                $weekday = date('w', strtotime($booking['date']));
                $hour = $booking['hour'].':00';
                $duration = ServiceDuration::find($booking['duration'])->duration;
                $hourEnd = Carbon::createFromFormat('H:i:s',$hour)->addMinutes($duration)->format('H:i:s');
                if (strtotime($hour)>strtotime($hourEnd))
                    return $query->where('id','-1');

                $query->where('dayoff', 0);
                $query->where('weekday', $weekday);
                $query->whereRaw("TIME(STR_TO_DATE(bo_start, '%H:%i')) <= TIME(STR_TO_DATE('{$hour}','%H:%i')) and TIME(STR_TO_DATE(bo_end, '%H:%i')) >= TIME(STR_TO_DATE('{$hourEnd}','%H:%i'))");

                return $query;
            })
//            ->whereDoesntHave('daysoff', function ($query) use ($booking) {
//                if (!isset($booking['hour']))
//                    return $query;
//
//                $dateHour = $booking['date'].' '.$booking['hour'].':00';
//                $query->whereRaw("STR_TO_DATE('{$dateHour}','%Y-%m-%d %H:%i') between date_start and date_end");
//                return $query;
//            })
            ->whereDoesntHave('daysoff', function ($query) use ($booking) {
                if (!isset($booking['hour']))
                    return $query;

                $duration = ServiceDuration::find($booking['duration'])->duration;
                $duration = $duration-1;
                $dateHour = $booking['date'].' '.$booking['hour'].':00';
                $dateHourEnd = $booking['date'].' '.date('H:i',strtotime($booking['hour']."+{$duration} minutes")).':00';

                $query->whereRaw('STR_TO_DATE("'.$dateHour.'","%Y-%m-%d %H:%i") > date_start and date_end > STR_TO_DATE("'.$dateHour.'","%Y-%m-%d %H:%i")')
                    ->orWhereRaw('STR_TO_DATE("'.$dateHourEnd.'","%Y-%m-%d %H:%i") > date_start and date_end > STR_TO_DATE("'.$dateHourEnd.'","%Y-%m-%d %H:%i")');
                //dd($dateHour,$dateHourEnd);
                return $query;
            })
            ->orderBy('nr_crt')
            ->get();
        //dd($users);
        //unselect unavailable selected users
        if (isset($booking['selected_therapists'])){
            $usersAvailable=[];
            foreach($users as $usr)
                $usersAvailable[] = $usr->id;

            foreach($booking['selected_therapists'] as $key=>$selected){
                if (!in_array($selected,$usersAvailable))
                    unset($booking['selected_therapists'][$key]);
            }

            Session::put('booking',$booking);
        }


        if (isset($booking['hour']) && isset($booking['duration'])){
            $duration = ServiceDuration::find($booking['duration']);
            $bufferMin = 55;
            $buffer = $duration->duration+55;

            foreach($users as $key=>$u){

                //remove user is he has already a booking at selected date&hour + duration interval
                $count = $u->therapistbookings()
                    ->where('date',$booking['date'])
                    ->where(function($query) use ($booking, $duration, $buffer, $bufferMin){
                        return $query->whereRaw('
                                                TIME("'.$booking['hour'].'") BETWEEN SUBTIME(`hour`,STR_TO_DATE(CONCAT(FLOOR('.$buffer.'/60),\':\',MOD('.$buffer.',60)),"%h:%i:%s")) AND ADDTIME(`hour`,STR_TO_DATE(CONCAT(FLOOR((duration+'.$bufferMin.')/60),\':\',MOD((duration+'.$bufferMin.'),60)),"%h:%i:%s"))
                                                ');
                    })

                    ->whereHas('booking',function($query){
                        return $query->where('is_active',1);
                    })
                    ->count();


                if ($count) $users->forget($key);

                $countSession = $u->sessionBlockedTherapists()
                    ->where('date',$booking['date'])
                    ->where(function($query) use ($booking, $duration, $buffer, $bufferMin){
                        return $query->whereRaw('
                                                 `hour` between SUBTIME("'.$booking['hour'].'",STR_TO_DATE(CONCAT(FLOOR('.$buffer.'/60),\':\',MOD('.$buffer.',60)),"%h:%i:%s")) and ADDTIME(ADDTIME("'.$booking['hour'].'",STR_TO_DATE(CONCAT(FLOOR('.$buffer.'/60),\':\',MOD('.$buffer.',60)),"%h:%i:%s")),"0:00:00")
                                                    OR
                                                  ADDTIME(`hour`, STR_TO_DATE(CONCAT(FLOOR(duration_min/60),\':\',MOD(duration_min,60)),"%h:%i:%s")) BETWEEN SUBTIME("'.$booking['hour'].'", "00:55:00") AND ADDTIME(ADDTIME("'.$booking['hour'].'",STR_TO_DATE(CONCAT(FLOOR('.$buffer.'/60),\':\',MOD('.$buffer.',60)),"%h:%i:%s")),"0:00:00")
                                                ');

                    })
                    ->count();
                if ($countSession) $users->forget($key);

            } //end foreach

        } //endif
        $usersAvailable=[];
        //unselect unavailable selected users
        //if (isset($booking['selected_therapists'])){

            foreach($users as $usr)
                $usersAvailable[] = $usr->id;

            if(isset($booking['selected_therapists']))
            {
                foreach($booking['selected_therapists'] as $key=>$selected){
                    if (!in_array($selected,$usersAvailable))
                        unset($booking['selected_therapists'][$key]);
                }
            }

            Session::put('booking',$booking);
        //}
        //dd($usersAvailable);
        $usersUnAvailable = User::OfTherapists()
            ->whereNotIn('id',$usersAvailable)
            ->whereHas('servicetypes', function ($query) use ($booking) {

                if (isset($booking['massage']) && $booking['massage'] > 0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->whereHas('zones', function ($query) use ($postcode) {
                $query->where('users_area_coverage.zone_id', $postcode->zone_id);
                return $query;
            })
            ->orderBy('nr_crt')
            ->get();

        if($users)
        {
            $allUsers['available'] = $users;
        }
        if($usersUnAvailable)
        {
            $allUsers['unavailable'] = $usersUnAvailable;
        }
        //return results
        return $allUsers;
    }

    /**
     * Get available therapists right now based on their location and google time travel estimation
     * @return array
     */
    public function therapistsNow()
    {
        $users = collect();

        $booking = Session::get('booking');
        $booking['date'] = Carbon::today()->format('Y-m-d');
        $booking['hour'] = Carbon::today()->format('H:i');
        
        //check if the current date is marked as day off
        $daysOff = $this->days_off($booking['date']);
       
        if ($daysOff->count())
            return $users;

        //get weekday working hours: if weekday is non working day, return
        $weekday = date('w',strtotime($booking['date']));
        $whours = Schedule::where('weekday',$weekday)->where('open',1)->first();

        
        if (!$whours)
            return $users;

        //the therapists can be booked only in working hours of the weekday
        if (strtotime($whours->bo_end) < strtotime(date('H:i')))
            return $users;

        //if the start working day hour is bigger then the current hour, return no therapists
        if (strtotime($whours->bo_start) > strtotime(date('H:i')))
            return $users;

        //if the current hour + desired massage duration exceed closing time, return no therapists
        if (isset($booking['duration'])){
            $checkduration = ServiceDuration::find($booking['duration'])->duration;
            if (strtotime($whours->bo_end) < strtotime(date('H:i')."+{$checkduration} minutes"))
                return $users;
        }


        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['duration','massage','date'];
        foreach($mandatory_fields as $field){
            if (!isset($booking[$field]))
                return $users;
        } //endforeach

        //get postcode zone
        // $postcode = Postcode::where('postcode',$booking['postcode'])->first();

        //users are not available for the past dates
        $today = Carbon::today();
        $dateC = Carbon::createFromFormat('Y-m-d',$booking['date']);
        $diff = $today->diffInHours($dateC,false);

        if ($diff<0)
            return $users;

        //query therapists
//        DB::enableQueryLog();
        $users = User::OfTherapists()
            ->whereHas('servicetypes', function ($query) use ($booking) {
                if (isset($booking['massage']) && $booking['massage']>0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->whereHas('zones', function ($query) use ($postcode) {
                $query->where('users_area_coverage.zone_id', $postcode->zone_id);
                return $query;
            })
            ->whereHas('workingdays', function ($query) use ($booking) {
                $weekday = date('w',strtotime($booking['date']));
                $hour = Carbon::now()->format('H:i:s');
                $duration = ServiceDuration::find($booking['duration'])->duration;
                $hourEnd = Carbon::createFromFormat('H:i:s',$hour)->addMinutes($duration)->format('H:i:s');
                if (strtotime($hour)>strtotime($hourEnd))
                    return $query->where('id','-1');
//                dd($hour);

                $query->where('dayoff', 0);
                $query->where('weekday',$weekday);
                $query->whereRaw("TIME(STR_TO_DATE(bo_start, '%H:%i')) <= TIME(STR_TO_DATE('{$hour}','%H:%i')) and TIME(STR_TO_DATE(bo_end, '%H:%i')) >= TIME(STR_TO_DATE('{$hourEnd}','%H:%i'))");
                return $query;
            })
            ->whereHas('profile', function ($query) {
                $query->where('massage_me_now', 1);
                return $query;
            })
            ->whereDoesntHave('daysoff', function ($query) use ($booking) {
//                if (!isset($booking['hour']))
//                    return $query;

                $hour = Carbon::now()->format('H:i');

                $duration = ServiceDuration::find($booking['duration'])->duration;

                $dateHour = $booking['date'].' '.$hour.':00';
                $dateHourEnd = $booking['date'].' '.date('H:i',strtotime($hour."+{$duration} minutes")).':00';

                $query->whereRaw('STR_TO_DATE("'.$dateHour.'","%Y-%m-%d %H:%i") between date_start and date_end')
                    ->orWhereRaw('STR_TO_DATE("'.$dateHourEnd.'","%Y-%m-%d %H:%i") between date_start and date_end');
//                $query->whereRaw('STR_TO_DATE("'.$dateHour.'","%Y-%m-%d %H:%i") between SUBTIME(SUBTIME(date_start,STR_TO_DATE(CONCAT(FLOOR('.$duration.'/60),\':\',MOD('.$duration.',60)),"%h:%i:%s")),"0:15:00") and ADDTIME(ADDTIME(date_end,STR_TO_DATE(CONCAT(FLOOR('.$duration.'/60),\':\',MOD('.$duration.',60)),"%h:%i:%s")),"0:15:00")');

                return $query;
            })
            ->whereHas('geoLocation', function($query) use ($booking){
                $query->whereRaw("DATE('".date('Y-m-d')."') between DATE(updated_at) and DATE(updated_at)");
                return $query;
            })
            ->whereHas('geoLocation', function($query) use ($booking){
                $query->whereRaw('"'.date('H:i:0').'" between SUBTIME(TIME(updated_at),"02:30:00") and ADDTIME(TIME(updated_at),"02:30:00")');
                return $query;
            })
            ->where(function($query) use ($booking){

                $query
                    ->whereDoesntHave('therapistbookings', function($query) use ($booking){
                        $duration = ServiceDuration::find($booking['duration']);
//                        $buffer = $duration->duration+0;
                        $bufferMin = 55;
                        $buffer_before = $duration->duration+31;
                        $buffer = $duration->duration+$bufferMin;
                        $buffer_as_time_before = floor($buffer_before/60).':'.($buffer_before%60);
                        $buffer_as_time = floor($buffer/60).':'.($buffer%60);

                        $query
                            ->where('date',date('Y-m-d'))
                            ->where(function($query) use ($booking, $duration, $buffer, $bufferMin){
                                return $query->whereRaw('
                                                    TIME("'.date('H:i').'") BETWEEN SUBTIME(`hour`,STR_TO_DATE(CONCAT(FLOOR('.$buffer.'/60),\':\',MOD('.$buffer.',60)),"%h:%i:%s")) AND ADDTIME(`hour`,STR_TO_DATE(CONCAT(FLOOR((duration+'.$bufferMin.')/60),\':\',MOD((duration+'.$bufferMin.'),60)),"%h:%i:%s"))
                                                    ');
                            })
                            ->whereHas('booking',function($query){
                                return $query->where('is_active',1);
                            });

//                        $query->where('date',date('Y-m-d'))
//                            ->whereRaw('`hour` between SUBTIME("'.date('H:i').'","'.$buffer_as_time_before.'") and ADDTIME(ADDTIME("'.date('H:i').'",STR_TO_DATE(CONCAT(FLOOR('.$duration->duration.'/60),\':\',MOD('.$duration->duration.',60)),"%h:%i")),"'.$buffer_as_time.'")')
////                            ->whereRaw('`hour` between SUBTIME(hour,"'.$buffer_as_time_before.'") and ADDTIME(ADDTIME(hour,STR_TO_DATE(CONCAT(FLOOR(duration/60),\':\',MOD(duration,60)),"%h:%i")),"'.$buffer_as_time.'")')
////                            ->whereRaw('`hour` between SUBTIME("'.date('H:i').'","00:00") and ADDTIME(ADDTIME("'.date('H:i').'",STR_TO_DATE(CONCAT(FLOOR('.$duration->duration.'/60),\':\',MOD('.$duration->duration.',60)),"%h:%i")),"'.$buffer_as_time.'")')
//                            ->whereHas('booking',function($query){
//                                return $query->where('is_active',1);
//                            });
                        return $query;
                    })
                    ->where(function($query) use ($booking){
                        $query->whereDoesntHave('sessionBlockedTherapists', function($query) use ($booking){
                            $duration = ServiceDuration::find($booking['duration']);
                            $buffer_before = $duration->duration+31;
                            $buffer = $duration->duration+30;
//                            $buffer = $duration->duration+0;
                            $buffer_as_time_before = floor($buffer_before/60).':'.($buffer_before%60).':00';
                            $buffer_as_time = floor($buffer/60).':'.($buffer%60).':00';
                            $query->where('date',date('Y-m-d'))
                                ->whereRaw('`hour` between SUBTIME("'.date('H:i:0').'","'.$buffer_as_time_before.'") and ADDTIME("'.date('H:i:0').'","'.$buffer_as_time.'")');
                        });

                        return $query;
                    });

                return $query;
            })
            ->orderBy('nr_crt')
            ->get();
            // $users = User::OfTherapists()->orderBy('nr_crt')
            // ->get();
        //delete unavailable selected users
        if (isset($booking['selected_therapists'])){
            $usersAvailable=[];
            foreach($users as $usr)
                $usersAvailable[] = $usr->id;

            foreach($booking['selected_therapists'] as $key=>$selected){
                if (!in_array($selected,$usersAvailable))
                    unset($booking['selected_therapists'][$key]);
            } //ennforeach

            Session::put('booking',$booking);
        } //endif

        //calculate first available hour
        foreach($users as $user){
            $user->firstAvHour = $this->getFirstHour($user,$booking);
        } //endforeach


        $checkduration = ServiceDuration::find($booking['duration'])->duration;
        foreach($users as $key=>$u){
            $userSchedule = $u->workingdays()->where('weekday',$weekday)->get()->first();

            if (!$u->firstAvHour)
                $users->forget($key);

//            if (strtotime($whours->bo_end) < strtotime($u->firstAvHour))
//                $users->forget($key);

            if (strtotime($userSchedule->bo_end) < strtotime($u->firstAvHour."+{$checkduration} minutes"))
                $users->forget($key);

        } //endforeach

        $sorted = $users->sortBy('firstAvHour');


        //return results
        return $sorted->values()->all();
    }

    /**
     * Get the first available hour for a specific user
     * @param $user
     * @param $booking
     */
    public function getFirstHour($user,$booking)
    {

        $result = null;
        $fromGeo = [
            'lat'=>$user->geoLocation->lat,
            'lng'=>$user->geoLocation->lng
        ];
        if (!$fromGeo || !$booking['postcode'])
            return $result;

        //compose from coordinates
        $from = "{$fromGeo['lat']},{$fromGeo['lng']}";

        //compose to coordonates
//        $response = \GoogleMaps::load('textsearch')
//            ->setParam([
//                'query' => "{$booking['postcode']}, London UK",
//            ])->get('results'); // return $this

        $address = "{$booking['postcode']}, London UK";
        $response = \GoogleMaps::load('geocoding')
            ->setParam (['address' => $address])
            ->get('results');

        if (empty($response['results']))
            $to = $address;
        else{
            $toGeo = $response['results'][0]['geometry']['location'];
            $to = "{$toGeo['lat']},{$toGeo['lng']}";
        } //endif

        $mode = $user->transport_mode;
        $eta = $this->gTimeTravel($from,$to,$mode);

        if (!$eta || $eta>60)
            return null;

        //add booking buffer for reservation process: equivalent with the availability in the basket
        $eta+=15;

        $result = Carbon::now()->addMinutes($eta)->format('H:i');

        //return result
        return $result;
    }

    /**
     * Calculated estimated time arrival using google maps api: directions
     * @param $from
     * @param $to
     * @param string $mode
     * @return mixed
     */
    protected function gTimeTravel($from,$to,$mode="DRIVING")
    {
        $params = [
            'origin'          => $from,
            'destination'     => $to,
            'mode'            => $mode,
            'departure_time'  => 'now',
        ];

        $direction = \GoogleMaps::load('directions')
            ->setParam($params)
            ->get();

        $time = json_decode($direction,true);
        // dd($time);
        if ($time['status']!="OK")
            return null;

        //get estimated time arrival in minutes
//        $buffer = 5;
        $buffer = 0;
        $eta = ceil($time['routes'][0]['legs'][0]['duration']['value']/60)+$buffer;

        //return eta
        return $eta;
    }

    /**
     * Therapist bookings
     * @return \Illuminate\Support\Collection
     */
    public function therapistsbookings()
    {
        $booking = Session::get('booking');

        $users = collect();
        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['postcode','duration','massage','date'];
        foreach($mandatory_fields as $field){
            if (!isset($booking[$field]))
                return $users;
        } //endforeach

        //get postcode zone
        $postcode = Postcode::where('postcode',$booking['postcode'])->first();

        //query therapists
//        DB::enableQueryLog();
        $users = User::OfTherapists()
            ->whereHas('servicetypes', function ($query) use ($booking) {
                $query->where('users_servicetype.servicetype_id', $booking['massage']);
                return $query;
            })
            ->whereHas('zones', function ($query) use ($postcode) {
                $query->where('users_area_coverage.zone_id', $postcode->zone_id);
                return $query;
            })
            ->whereHas('workingdays', function ($query) use ($booking) {
                $weekday = date('w',strtotime($booking['date']));
                $query->where('dayoff', 0);
                $query->where('weekday',$weekday);
                return $query;
            })
//            ->whereDoesntHave('daysoff', function($query) use ($booking){
//                $query->whereRaw("DATE('".$booking["date"]."') between DATE(date_start) and DATE(date_end)");
//                return $query;
////            })
//            ->whereDoesntHave('daysoff', function ($query) use ($booking) {
//                if (!isset($booking['hour']))
//                    return $query;
//
//                $duration = ServiceDuration::find($booking['duration'])->duration;
//
//                $dateHour = $booking['date'].' '.$booking['hour'].':00';
//                $query->whereRaw('STR_TO_DATE("'.$dateHour.'","%Y-%m-%d %H:%i") between SUBTIME(SUBTIME(date_start,STR_TO_DATE(CONCAT(FLOOR('.$duration.'/60),\':\',MOD('.$duration.',60)),"%h:%i:%s")),"0:15:00") and ADDTIME(ADDTIME(date_end,STR_TO_DATE(CONCAT(FLOOR('.$duration.'/60),\':\',MOD('.$duration.',60)),"%h:%i:%s")),"0:15:00")');
////                $query->whereRaw("STR_TO_DATE('{$dateHour}','%Y-%m-%d %H:%i') between SUBTIME(date_start, STR_TO_DATE(CONCAT(FLOOR({$duration}/60):MOD({$duration},60)),\"%h:%i:%s\"))) and date_end");
//
//                return $query;
//            })

            ->whereHas('therapistbookings', function($query) use ($booking){
                $query->where('date',$booking['date']);
                if (isset($booking['hour']))
                    $query->whereRaw('`hour` between SUBTIME("'.$booking['hour'].'","00:15:00") and ADDTIME(ADDTIME("'.$booking['hour'].'",STR_TO_DATE(CONCAT(FLOOR(duration/60),\':\',MOD(duration,60)),"%h:%i:%s")),"0:15:00")');
                return $query;
            })
            ->get();
//        dd(DB::getQueryLog());

//        dd($users);
//        dd($booking);

        //return results
        return $users;
    }

    /**
     * Get selected therapists
     * @return \Illuminate\Support\Collection
     */
    public function selected_therapists()
    {
        $booking = Session::get('booking');

        $users = collect();
        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['selected_therapists'];
        foreach($mandatory_fields as $field){
            if (!isset($booking[$field]))
                return $users;
        } //endforeach

        //query therapists
//        DB::enableQueryLog();
        $users = User::OfTherapists()
            ->whereIn('id', $booking['selected_therapists'])
            ->get();
//        dd($users);

        //return results
        return $users;
    }

    public function therapist_info($thId)
    {
        $user = User::OfTherapists()
            ->where('id', $thId)
            ->first();

        if($user)
        {
            $servicesArray = [];
            $services = $user->servicetypes()->where('name','!=','optional')->get();
            if($services)
            {
                foreach ($services as $service)
                {
                    $servicesArray[] = $service->name;
                }
            }

            $therapist['data'] = $user;
            $therapist['services'] = $servicesArray;
            return $therapist;
        }
        else
        {
            return null;
        }
    }
    /**
     * Get focal points
     *
     * @return array
     */
    public function focal_points()
    {
        $points=[];

        $focalPoints = FocalPoint::all();
        foreach($focalPoints as $fp)
            $points[$fp->side][$fp->slug] = ['id'=>$fp->id,'name'=>$fp->name];

        //return focal points array
        return $points;
    }
    /**
     * Get next 30 days
     * @return array
     */
    public function available_30_days()
    {
        $activeDay = Carbon::now()->format('Y-m-d');
        $booking = Session::get('booking');
        if(isset($booking['date']))
        {
            $activeDay = $booking['date'];
        }
        $activeDayTimeStamp = null;

        if($activeDay)
        {
            $activeDayTimeStamp = Carbon::createFromFormat('Y-m-d', $activeDay)->timestamp;
        }

        $current_30_days = [];
        $startDate = Carbon::now();

        $startDateTimeStamp = $startDate->timestamp;

        $endDate = Carbon::now()->addDays(30);

        Carbon::macro('range', function ($startDate, $endDate) {
            return new \DatePeriod($startDate, new \DateInterval('P1D'), $endDate);
        });
        foreach (Carbon::range($startDate, $endDate) as $date) {
            $dateTimeStamp = $date->timestamp;
            $current_30_days[$dateTimeStamp] =  [
                'date' => $date->format('Y-m-d'),
                'info' => [
                    'name-day' => substr(strtoupper($date->format('l')),0,3),
                    'nr-date' => $date->format('d'),
                    'month-name' => substr($date->format('F'),0,3),
                ],
                'is-active' => false,
                'is-available' => true,
            ];

            if($dateTimeStamp == $startDateTimeStamp && !$activeDay)
            {
                $current_30_days[$dateTimeStamp]['is-active'] = true;
            }
            elseif($activeDay && ($dateTimeStamp == $activeDayTimeStamp))
            {
                $current_30_days[$dateTimeStamp]['is-active'] = true;
            }
        }

        return $current_30_days;
    }

    /**
     * Get operation hours
     *
     * @param $date
     * @param null $therapist
     * @return array|\Illuminate\Support\Collection
     */
    public function all_hours($date,$therapist=null)
    {
        $booking = Session::get('booking');
        $bookingPrice = 0;
        if(!isset($booking['duration']) && $this->defaultServiceDuration)
        {
            Session::set('booking.duration',$this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
        }
        else
        {
            $bookingDuration = ServiceDuration::find($booking['duration']);
            $bookingPrice = $bookingDuration->price;
        }

        //hours collection
        $hours = [];

        //get today
        $today = Carbon::today();
        $today_now = Carbon::now();

        //cast current date into Carbon object
        $dateC = Carbon::createFromFormat('Y-m-d',$date)->startOfDay();

        $diff = $today->diffInHours($dateC,false);

        if ($diff<0)
            return $hours;

        //the buffer between bookings
        $buffer = 30;

        //if selected date is marked as day off, return empty hours array
        $daysOff = $this->days_off($date);
        if ($daysOff->count())
            return $hours;

        //get weekday working hours: if weekday is non working day, return
        $weekday = date('w',strtotime($dateC->format('Y-m-d')));
        $whours = Schedule::where('weekday',$weekday)->where('open',1)->first();
        if (!$whours)
            return $hours;

        //data curenta este aceeasi cu cea selectata si ultima ora este mai mica decat ora curenta, nmu mai pot alege o ora din ziua dea zi
        if ((date('Y-m-d') == date('Y-m-d',strtotime($date))) && (strtotime($whours->bo_end) < strtotime(date('H:i'))))
            return $hours;

        //create working hours interval collection
        $start = strtotime($whours->bo_start);
        $end = strtotime($whours->bo_end);

        //pentru data curenta, daca este trecut de ora 23, nu mai poate alege nicio ora.
        if (date('Y-m-d') == date('Y-m-d',strtotime($date))){
            $h = date('H');
            $i = date('i');
            if ($h==23 and $i>10)
                return $hours;
        } //endif

        $key = date('H:i', $start);
        $hours[$key] = [
            'available'=>1,
            'hour'=>date('H:i', $start),
            'price'=>$bookingPrice
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>1,
                'hour'=>date('H:i', $start),
                'price'=>$bookingPrice
            ];
        } //endwhile

        $results = $hours;

        //available hours based on current duration selection and therapist schedule
//        if(isset($booking['duration'])){
//            $unvhours=[];
//            $duration = ServiceDuration::find($booking['duration'])->duration;
//            $collection = collect($results);
//            $unavailable = $collection->filter(function ($value, $key) {
//                return $value['available'] == 0;
//            });
//
//            $unavailable->all();
//            foreach($unavailable as $key=>$value){
//                $start_hour = Carbon::createFromFormat('H:i',$key)->subMinutes($duration+$buffer);
//                $start = strtotime($start_hour->format('H:i'));
//
//                //the starting hour cannot be lower than 8:00am
//                if ($start < strtotime("08:00"))
//                    $start = strtotime("08:00");
//
//                //set max hour
//                $end = strtotime($key);
//
//                $unvhours[date('H:i',$start)] = [
//                    'available'=>1,
//                    'hour'=>date('H:i', $start)
//                ];
//                while ($start !== $end)
//                {
//                    $start = strtotime('+30 minutes',$start);
//                    $key = date('H:i', $start);
//                    $unvhours[$key] = [
//                        'available'=>1,
//                        'hour'=>date('H:i', $start)
//                    ];
//                } //endwhile
//
//            } //endforeach
//
//            $results = array_merge($results,$unvhours);
//
//        } //endif
//            dd($results);
//        asort($results);

        //available hours for today based on current time
        if ($today->format('Y-m-d') == $dateC->format('Y-m-d')){

            //create disabled hours array for today
            $inow = Carbon::now()->format('i');
            if ($inow>=30)
                $next_time = 60-Carbon::now()->format('i')+60;
            else
                $next_time = 60-Carbon::now()->format('i')+30;

            $first_av_hour = Carbon::now()->addMinutes($next_time)->format('H:i');

            foreach($results as $hour=>$values){

                if (strtotime($hour) < strtotime($first_av_hour)){
                    $results[$hour]['available'] = 0;

                    //if hour is set on unavailable time, reset it with the first available hour
                    if (isset($booking['hour']) && $booking['hour']==$hour){
                        $booking['hour'] = $first_av_hour;
                        Session::put('booking',$booking);
                    }
                } //endif
            } //ensdforeach
        } //endif

        //if (selected hour is not available, deselect it and save the new session value)
        if (isset($booking['hour']) && @$results[$booking['hour']]['available']==0){
            $booking['hour']=null;
            unset($booking['hour']);
            Session::put('booking',$booking);

        } //endif

        //return hours
        return collect($results);
    }


    /**
     * Process th hours
     *
     * @param $starth
     * @param $endh
     * @param array $excepts
     * @return array
     */
    protected function process_th_hours($starth,$endh,$excepts=[],$th,$date)
    {
        $booking = Session::get('booking');

        $bookingPrice = 0;
        if(!isset($booking['duration']) && $this->defaultServiceDuration)
        {
            Session::set('booking.duration',$this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
        }
        else
        {
            $bookingDuration = ServiceDuration::find($booking['duration']);
            $bookingPrice = $bookingDuration->price;
        }
        $hours =[];

        $start = strtotime($starth);
        $end = strtotime($endh);

        $currentHourStart = date('H',$start);
        $currentMinuteStart = date('i',$start);

        if($currentMinuteStart > 0 && $currentMinuteStart < 30)
        {
            $start = strtotime('+'.(30-$currentMinuteStart).' minutes',$start);
        }
        elseif ($currentMinuteStart > 30)
        {
            $start = strtotime('+1 hour',$start);
        }

        //first hour
        $key = date('H:i', $start);

        $hours[$key] = [
            'available'=>1,
            'hour'=>date('H:i', $start),
            'price' => $bookingPrice,
            'is_booking' => $th->isBookingHour($date, $key)
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>1,
                'hour'=>date('H:i', $start),
                'price' => $bookingPrice,
                'is_booking' => $th->isBookingHour($date, $key)
            ];
        }

        //remove hours
        if (!empty($excepts)){
            foreach($excepts as $ex){
                $hours[$ex]['available']=0;
                $hours[$ex]['price']=$bookingPrice;
                $hours[$ex]['is_booking']=$th->isBookingHour($date, $ex);
            }
        }
        //return hours
        return $hours;
    }


    /**
     * Get operation hours
     *
     * @param $date
     * @param null $therapist
     * @return array|\Illuminate\Support\Collection
     */
    public function th_hours($date,$therapistId)
    {
        $booking = Session::get('booking');

        $bookingPrice = 0;
        if(!isset($booking['duration']) && $this->defaultServiceDuration)
        {
            Session::set('booking.duration',$this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
        }
        else
        {
            $bookingDuration = ServiceDuration::find($booking['duration']);
            $bookingPrice = $bookingDuration->price;
        }

        //hours collection
        $hours = [];

        //get today
        $today = Carbon::today();

        //cast current date into Carbon object
        $dateC = Carbon::createFromFormat('Y-m-d',$date)->startOfDay();

        $diff = $today->diffInHours($dateC,false);


        if ($diff<0)
            return $hours;

        //the buffer between bookings
        $buffer = 30;

        //if selected date is marked as day off, return empty hours array
        $daysOff = $this->days_off($date);

        if ($daysOff->count())
            return $hours;

        //get weekday working hours: if weekday is non working day, return
        $weekday = date('w',strtotime($dateC->format('Y-m-d')));
        $whours = Schedule::where('weekday',$weekday)->where('open',1)->first();
        if (!$whours)
            return $hours;

        //create working hours interval collection
        $start = strtotime($whours->bo_start);
        $end = strtotime($whours->bo_end);
        $key = date('H:i', $start);
        $hours[$key] = [
            'available'=>0,
            'hour'=>date('H:i', $start),
            'price'=>$bookingPrice,
            'is_booking'=>false
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>0,
                'hour'=>date('H:i', $start),
                'price'=>$bookingPrice,
                'is_booking'=>false
            ];
        } //endwhile

        $therapist = User::where('id',$therapistId)->first();

        $th_hours=[];

        $twd = $therapist->workingdays()->where('dayoff',0)->where('weekday',$weekday)->first();

        if ($twd){
            $blockedHours = $therapist->blockedHours($date);
            // print_r($blockedHours);
            if (count($blockedHours)){
                foreach($this->process_th_hours($twd->bo_start,$twd->bo_end,$blockedHours,$therapist,$date) as $key=>$value){
                    if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                        continue;
                    else
                        $th_hours[$key] = $value;
                } //endforeach
            }
            else{
                foreach($this->process_th_hours($twd->bo_start,$twd->bo_end,null,$therapist,$date) as $key=>$value){
                    if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                        continue;
                    else
                        $th_hours[$key] = $value;
                } //endforeach
            }
        } //endif


        $results = array_merge($hours,$th_hours);

        //available hours based on current duration selection and therapist schedule
        if(isset($booking['duration'])){
            $unvhours=[];
            $duration = ServiceDuration::find($booking['duration'])->duration;
            $collection = collect($results);
            $unavailable = $collection->filter(function ($value, $key) {
                return $value['available'] == 0;
            });

            $unavailable->all();
            //dd($unavailable, $results);
            foreach ($results as $key => $result)
            {
                if($result['is_booking'])
                {
                    $addedMinutes = $duration+$buffer;
                    $subMinutes = $duration+$buffer;
                }
                else
                {
                    $addedMinutes = $duration;
                    $subMinutes = 0;
                }
                $start_hour_after = Carbon::createFromFormat('H:i',$key)->addMinutes($addedMinutes);
                $start_after = $start_hour_after->format('H:i');

                $start_hour_before = Carbon::createFromFormat('H:i',$key)->subMinutes($subMinutes);
                $start_before = $start_hour_before->format('H:i');

                $end = strtotime($key);
                $start = strtotime($start_before);

                if($result['is_booking'])
                {
                    //dd($start_before, $key);
                }
                    //add remaining hours
                /*while ($start <= $end)
                {
                    $key = date('H:i', $start);

                    if(isset($results[$key]))
                    {
                        $results[$key]['available'] = 0;
                    }

                    $start = strtotime('+30 minutes',strtotime($key));
                }*/ //endwhile

                $ukeys = [];
                if($subMinutes>0)
                {
                    for ($i=$start;$i<=$end;$i=strtotime('+30 minutes',$i))
                    {
                        $ukey = date('H:i', $i);
                        if(isset($results[$ukey]))
                        {
                            $results[$ukey]['available'] = 0;
                        }
                        $ukeys[] = $ukey;
                    }
                    //dd($ukeys);
                }

                foreach ($unavailable as $k => $item)
                {
                    if(strtotime($start_after) > strtotime($k))
                    {
                        $results[$key]['available'] = 0;
                        $unavailable->pull($k);
                        break;
                    }
                }
            }

        } //endif

        //available hours for today based on current time
        if ($today->format('Y-m-d') == $dateC->format('Y-m-d')){
            //create disabled hours array for today
            $next_time = 60-Carbon::now()->format('i')+30;
            $first_av_hour = Carbon::now()->addMinutes($next_time)->format('H:i');

            foreach($results as $hour=>$values){

                if (strtotime($hour) < strtotime($first_av_hour)){
                    $results[$hour]['available'] = 0;

                    //if hour is set on unavailable time, reset it with the first available hour
                    if (isset($booking['hour']) && $booking['hour']==$hour){
                        $booking['hour'] = $first_av_hour;
                        Session::put('booking',$booking);
                    }
                } //endif
            } //ensdforeach
        } //endif

        //if (selected hour is not available, deselect it and save the new session value)
        if (isset($booking['hour']) && @$results[$booking['hour']]['available']==0){
            $booking['hour']=null;
            unset($booking['hour']);
            Session::put('booking',$booking);

        } //endif

        //return hours
        return collect($results);
    }


    /**
     * Get operation hours
     *
     * @param $date
     * @param null $therapist
     * @return array|\Illuminate\Support\Collection
     */
    public function hours($date,$therapist=null)
    {
        $booking = Session::get('booking');

        $bookingPrice = 0;
        if(!isset($booking['duration']) && $this->defaultServiceDuration)
        {
            Session::set('booking.duration',$this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
        }
        else
        {
            $bookingDuration = ServiceDuration::find($booking['duration']);
            $bookingPrice = $bookingDuration->price;
        }

        //hours collection
        $hours = [];

        //get today
        $today = Carbon::today();

        //cast current date into Carbon object
        $dateC = Carbon::createFromFormat('Y-m-d',$date)->startOfDay();

        $diff = $today->diffInHours($dateC,false);


        if ($diff<0)
            return $hours;

        //the buffer between bookings
        $buffer = 30;

        //if selected date is marked as day off, return empty hours array
        $daysOff = $this->days_off($date);
        if ($daysOff->count())
            return $hours;

        //get weekday working hours: if weekday is non working day, return
        $weekday = date('w',strtotime($dateC->format('Y-m-d')));
        $whours = Schedule::where('weekday',$weekday)->where('open',1)->first();
        if (!$whours)
            return $hours;

        //create working hours interval collection
        $start = strtotime($whours->bo_start);
        $end = strtotime($whours->bo_end);
        $key = date('H:i', $start);
        $hours[$key] = [
            'available'=>0,
            'hour'=>date('H:i', $start),
            'price'=>$bookingPrice
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>0,
                'hour'=>date('H:i', $start),
                'price'=>$bookingPrice
            ];
        } //endwhile

        //available hours based on therapist schedule
        if ($therapist!=null){
            //therapist from the booking or any other specific request
            $therapists = User::whereIn('id',$therapist)->get();
        }
        else{
            //available therapists besed on user selection
            $therapists = $this->therapists();
        }

        $th_hours=[];

        //select hours only for selected therapists
        if (isset($booking['selected_therapists']) && !empty($booking['selected_therapists'])){
            $filtered = $therapists->filter(function ($user, $key) use ($booking) {
                return in_array($user->id,$booking['selected_therapists']);
            });
            $therapists = $filtered;
        }

        foreach($therapists as $therapist){

            $twd = $therapist->workingdays()->where('dayoff',0)->where('weekday',$weekday)->first();

            if ($twd){
                $blockedHours = $therapist->blockedHours($date);

                if (count($blockedHours)){
                    //dd($blockedHours);
                    foreach($this->process_hours($twd->bo_start,$twd->bo_end,$blockedHours) as $key=>$value){
                        if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                            continue;
                        else
                            $th_hours[$key] = $value;
                    } //endforeach
                }
                else{
                    foreach($this->process_hours($twd->bo_start,$twd->bo_end) as $key=>$value){
                        if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                            continue;
                        else
                            $th_hours[$key] = $value;
                    } //endforeach
//                    $th_hours = array_merge($th_hours,$this->process_hours($twd->bo_start,$twd->bo_end));
                }
            } //endif

        } //endforeach

        //dd($hours, $th_hours);
        $results = array_merge($hours,$th_hours);

        //available hours based on current duration selection and therapist schedule
        if(isset($booking['duration'])){
            $unvhours=[];
            $duration = ServiceDuration::find($booking['duration'])->duration;
            $collection = collect($results);
            $unavailable = $collection->filter(function ($value, $key) {
                return $value['available'] == 0;
            });

            $unavailable->all();

            foreach($unavailable as $key=>$value){

                $start_hour = Carbon::createFromFormat('H:i',$key)->subMinutes($duration+$buffer);
                //$start_hour = Carbon::createFromFormat('H:i',$key);
                $start = strtotime($start_hour->format('H:i'));

                //the starting hour cannot be lower than 8:00am
                if ($start < strtotime("08:00"))
                    $start = strtotime("08:00");

                //set max hour
                $end = strtotime($key);

                $unvhours[date('H:i',$start)] = [
                    'available'=>0,
                    'hour'=>date('H:i', $start),
                    'price'=>$bookingPrice
                ];

                while($start !== $end){
                    $start = strtotime('+30 minutes',$start);
                    $key = date('H:i', $start);
                    $unvhours[$key] = [
                        'available'=>0,
                        'hour'=>date('H:i', $start),
                        'price'=>$bookingPrice
                    ];

                } //endwhile

            } //endforeach

            //dd($results,$unvhours);
            $results = array_merge($results,$unvhours);

        } //endif

//        asort($results);

        //available hours for today based on current time
        if ($today->format('Y-m-d') == $dateC->format('Y-m-d')){
            //create disabled hours array for today
            $next_time = 60-Carbon::now()->format('i')+30;
            $first_av_hour = Carbon::now()->addMinutes($next_time)->format('H:i');

            foreach($results as $hour=>$values){

                if (strtotime($hour) < strtotime($first_av_hour)){
                    $results[$hour]['available'] = 0;

                    //if hour is set on unavailable time, reset it with the first available hour
                    if (isset($booking['hour']) && $booking['hour']==$hour){
                        $booking['hour'] = $first_av_hour;
                        Session::put('booking',$booking);
                    }
                } //endif
            } //ensdforeach
        } //endif

        //if (selected hour is not available, deselect it and save the new session value)
        if (isset($booking['hour']) && @$results[$booking['hour']]['available']==0){
            $booking['hour']=null;
            unset($booking['hour']);
            Session::put('booking',$booking);

        } //endif

        //return hours
        return collect($results);
    }

    /**
     * Get operation hours
     *
     * @param $date
     * @return array
     */
    public function hours_without_buffer($date,$therapist=null)
    {
        $booking = Session::get('booking');

        //hours collection
        $hours = [];

        //get today
        $today = Carbon::today();

        //cast current date into Carbon object
        $dateC = Carbon::createFromFormat('Y-m-d',$date)->startOfDay();

        $diff = $today->diffInHours($dateC,false);
        if ($diff<0)
            return $hours;


        //if selected date is marked as day off, return empty hours array
        $daysOff = $this->days_off($date);
        if ($daysOff->count())
            return $hours;

        //get weekday working hours: if weekday is non working day, return
        $weekday = date('w',strtotime($dateC->format('Y-m-d')));
        $whours = Schedule::where('weekday',$weekday)->where('open',1)->first();
        if (!$whours)
            return $hours;

        //create working hours interval collection
        $start = strtotime($whours->bo_start);
        $end = strtotime($whours->bo_end);
        $key = date('H:i', $start);
        $hours[$key] = [
            'available'=>0,
            'hour'=>date('H:i', $start)
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>0,
                'hour'=>date('H:i', $start)
            ];
        } //endwhile

        //available hours based on therapist schedule
        if ($therapist!=null){
            //therapist from the booking or any other specific request
            $therapists = User::whereIn('id',$therapist)->get();
        }
        else{
            //available therapists besed on user selection
            $therapists = $this->therapists();
        }

        $th_hours=[];


        //select hours only for selected therapists
        if (isset($booking['selected_therapists'])){

            $filtered = $therapists->filter(function ($user, $key) use ($booking) {
                return in_array($user->id,$booking['selected_therapists']);
            });
            $therapists = $filtered;
        }

        foreach($therapists as $therapist){

            $twd = $therapist->workingdays()->where('dayoff',0)->where('weekday',$weekday)->first();
            if ($twd){
                $blockedHours = $therapist->blockedHours($date);
                if (count($blockedHours)){
                    foreach($this->process_hours($twd->bo_start,$twd->bo_end,$blockedHours) as $key=>$value){
                        if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                            continue;
                        else
                            $th_hours[$key] = $value;
                    } //endforeach
//                    $th_hours = array_merge($th_hours,$this->process_hours($twd->bo_start,$twd->bo_end,$blockedHours));
                }
                else{
                    foreach($this->process_hours($twd->bo_start,$twd->bo_end) as $key=>$value){
                        if (isset($th_hours[$key]) && $th_hours[$key]['available']==0)
                            continue;
                        else
                            $th_hours[$key] = $value;
                    } //endforeach
//                    $th_hours = array_merge($th_hours,$this->process_hours($twd->bo_start,$twd->bo_end));
                }
            } //endif

        } //endforeach

        $results = array_merge($hours,$th_hours);

        //available hours based on current duration selection and therapist schedule
        if(isset($booking['duration'])){
            $unvhours=[];
            $duration = ServiceDuration::find($booking['duration'])->duration;
            $collection = collect($results);
            $unavailable = $collection->filter(function ($value, $key) {
                return $value['available'] == 0;
            });

            $unavailable->all();
            foreach($unavailable as $key=>$value){
                $start_hour = Carbon::createFromFormat('H:i',$key)->subMinutes($duration);
                $start = strtotime($start_hour->format('H:i'));
                $end = strtotime($key);

                $unvhours[date('H:i',$start)] = [
                    'available'=>0,
                    'hour'=>date('H:i', $start)
                ];
//                while ($start !== $end)
//                {
//                    $start = strtotime('+30 minutes',$start);
//                    $key = date('H:i', $start);
//                    $unvhours[$key] = [
//                        'available'=>0,
//                        'hour'=>date('H:i', $start)
//                    ];
//                } //endwhile

            } //endforeach

            $results = array_merge($results,$unvhours);

        } //endif
//            dd($results);
//        asort($results);

        //available hours for today based on current time
        if ($today->format('Y-m-d') == $dateC->format('Y-m-d')){

            //create disabled hours array for today
            $next_time = 60-Carbon::now()->format('i')+30;
            $first_av_hour = Carbon::now()->addMinutes($next_time)->format('H:i');

            foreach($results as $hour=>$values){

                if (strtotime($hour) < strtotime($first_av_hour)){
                    $results[$hour]['available'] = 0;

                    //if hour is set on unavailable time, reset it with the first available hour
                    if (isset($booking['hour']) && $booking['hour']==$hour){
                        $booking['hour'] = $first_av_hour;
                        Session::put('booking',$booking);
                    }
                } //endif
            } //ensdforeach
        } //endif

        //if (selected hour is not available, deselect it and save the new session value)
        if (isset($booking['hour']) && @$results[$booking['hour']]['available']==0){
            $booking['hour']=null;
            unset($booking['hour']);
            Session::put('booking',$booking);

        } //endif

        //return hours
        return collect($results);
    }

    /**
     * Process hours
     *
     * @param $starth
     * @param $endh
     * @param array $excepts
     * @return array
     */
    protected function process_hours($starth,$endh,$excepts=[])
    {
        $booking = Session::get('booking');

        $bookingPrice = 0;
        if(!isset($booking['duration']) && $this->defaultServiceDuration)
        {
            Session::set('booking.duration',$this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
        }
        else
        {
            $bookingDuration = ServiceDuration::find($booking['duration']);
            $bookingPrice = $bookingDuration->price;
        }
        $hours =[];

        $start = strtotime($starth);
        $end = strtotime($endh);

        $currentHourStart = date('H',$start);
        $currentMinuteStart = date('i',$start);

        if($currentMinuteStart > 0 && $currentMinuteStart < 30)
        {
            $start = strtotime('+'.(30-$currentMinuteStart).' minutes',$start);
        }
        elseif ($currentMinuteStart > 30)
        {
            $start = strtotime('+1 hour',$start);
        }

        //first hour
        $key = date('H:i', $start);
        $hours[$key] = [
            'available'=>1,
            'hour'=>date('H:i', $start),
            'price' => $bookingPrice,
        ];

        //add remaining hours
        while ($start !== $end)
        {
            $start = strtotime('+30 minutes',$start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available'=>1,
                'hour'=>date('H:i', $start),
                'price' => $bookingPrice
            ];
        }

        //remove hours
        if (!empty($excepts)){
            foreach($excepts as $ex){
                $hours[$ex]['available']=0;
                $hours[$ex]['price']=$bookingPrice;
//                unset($hours[$ex]);
            }
        }
        //return hours
        return $hours;
    }

    /**
     * Check if a date is a non-working day
     */
    public function days_off($start_date=null)
    {
        //set date
        if (!$start_date)
            $start_date = date('Y-m-d 00:00:00');
        else
            $start_date = Carbon::createFromFormat('Y-m-d',$start_date)->format('Y-m-d H:i:s');

        //query selected date between non working days
        $nwdays = ScheduleDaysOff::whereRaw("'".$start_date."' between date_start and date_end")->get();

        //return results: non working days
        return $nwdays;
    }

    public function getAndSetFirstAvailableDayHour()
    {
        $booking = Session::get('booking');

        if (!isset($booking['date']))
        {
            $date = date('Y-m-d');
        }
        else
        {
            $date = $booking['date'];
        }

        $hours = $this->all_hours($date);
        $firstHour = null;

        if($hours)
        {
            foreach ($hours as $hour)
            {
                if($hour['available'])
                {
                    $firstHour = $hour;
                    break;
                }
            }
            if($firstHour)
            {
                Session::set('booking.hour',$firstHour['hour']);
                return $firstHour;
            }
        }
    }

    /**
     * Get available days
     *
     * @return array
     */
    public function days()
    {
        $days=[];

        //todo

        //return dates
        return $days;
    }

    /**
     * Get Order Details by Card transaction id
     * @param $card_trans_id
     */
    public function get_order_cart($card_trans_id)
    {
        return BookingOrder::where('card_trans_id',$card_trans_id)->get();
    }

    /**
     * Return if the booking can be cancelled
     * @param $id
     * @return bool
     */
    public function can_be_safe_canceled(BookingOrder $obj)
    {
        //default response value
        $response = false;

        //get order info
        $orderInfo = json_decode($obj->orderInfo,true);

        //if in the order has been used an voucher code, the amount is not refundable
//        if ($orderInfo['has_voucher'])
//            return $response=false;

        $now = Carbon::now();
        $dateHourOrder = $obj->date->format('Y-m-d').' '.$obj->hour;
        $orderTime = Carbon::createFromFormat('Y-m-d H:i',$dateHourOrder);

        //if the cancellation is done by the client in less than 3 hours before the booking, the amount is not refundable
        $safe_cancel = $now->diffInMinutes($orderTime,false);
        if ($safe_cancel>90){
            $response=true;
        }
        else{
            $response=false;
        }

        //return response
        return $response;
    }

    /**
     * Cancel order and refund value
     * @param BookingOrder $obj
     */
    public function cancel_order(BookingOrder $obj, $admin=false)
    {

        //start transaction
        DB::beginTransaction();

        //first refund the value
        $refundStatus = $this->refund_value_of($obj,$admin);

        $orderInfo = json_decode($obj->orderInfo,true);
        $therapistsIds = $orderInfo['therapistIds'];

        //cancel order
        $obj->is_active=0;
        $success = $obj->save();

        //refund voucher and cvancel invoice only if the booking can be safe canceled
        if ($this->can_be_safe_canceled($obj)){
            //refund voucher value
            $refundVoucher = $this->refund_voucher_of($obj,$admin);

            //refund invoice
            $refundInvoice = $this->cancel_invoice_of($obj);
        } //end

        //add mobile app notifications to the client and therepists
        //compose message
        $notif_message = trans("schedules::booking.mobile_cancel_order",['number'=>$obj->id,'date'=>$obj->date_to_human,'hour'=>$obj->hour_to_human]);

        //notification users array
        $notif_users[] = $obj->user_id;
        foreach($therapistsIds as $thid)
            $notif_users[] = $thid;

        //store notifications
        foreach($notif_users as $user)
            NotifRepository::add($user,$notif_message, 'cancelBooking', 'Booking cancelled');

        //commit-rollback transaction
        if ($success){
            //commit
            DB::commit();

            //send confirmation email
            $user = User::findOrFail($obj->user_id);
            $data['user'] = $user;
            $data['order'] = $obj;

            Mail::send('schedules::frontend.emails.cancelorder', ['user' => $user, 'order'=>$obj], function ($m) use ($data) {
                $m->from(env('MAIL_FROM'), env('APP_NAME'));
                $m->to($data['user']->email, $data['user']->name);
                $m->bcc(explode(',',env('MAIL_NEWORDER_BCC')), env('MAIL_NEWORDER_BCC_NAME'));
                $m->subject('Zen London Massage website – cancel booking  #'.$data['order']->id);
            });

            //send therapist email confirmation
            //send confirmation email
            $thusers = User::whereIn('id',$notif_users)->get();

            foreach($thusers as $therapist){
                $data['user'] = $therapist;
                Mail::send('schedules::frontend.emails.therapist_cancelorder', ['user' => $therapist, 'order'=>$obj], function ($m) use ($data) {
                    $m->from(env('MAIL_FROM'), env('APP_NAME'));
                    $m->to($data['user']->email, $data['user']->name);
                    $m->bcc(explode(',',env('MAIL_NEWORDER_BCC')), env('MAIL_NEWORDER_BCC_NAME'));
                    $m->subject(env('APP_NAME').' – cancel booking  #'.$data['order']->id);
                });
            }


        }
        else
        {
            //rollback actions
            DB::rollBack();

        } //end elseif transaction

        //create response array
        $response = [
            'success' => $success,
            'message' => $refundStatus['message'],
        ];

        //return response
        return $response;
    }

    /**
     * Refund payments with credit card
     * @param BookingOrder $obj
     * @return bool
     */
    protected function refund_value_of(BookingOrder $obj, $admin=false)
    {
        $response = [
            'success'=>false,
            'message'=>null,
        ];

        if (!$admin){
            $safe_cancel = $this->can_be_safe_canceled($obj);
            $response = [
                'success'=>false,
                'message'=>null,
            ];
        }
        else
            $safe_cancel=true;

        //to refund, all the conditions must be valid
//        if (!$safe_cancel || !$obj->card_trans_id || !$obj->card_trans_status || !$obj->amount>0 || !$obj->is_active)
//            return $response;

        //if the booking was paid by cash
        if (!$safe_cancel && empty($obj->card_trans_id) && $obj->amount>0 && $obj->is_active){
            $response['message'] = $this->charge_from_card($obj);
            return $response;
        } //end


        //The Booking was paid with card and cannot be canceled.
        if  (!$obj->card_trans_id || !$obj->card_trans_status || !($obj->amount>0) || !$obj->is_active || !$safe_cancel)
            return $response;

        // Try to refund the amount
        try{

            //according with sagepay, refund is available only for yesterday
            if ($obj->created_at->format('Y-m-d') != date('Y-m-d'))
                $response = $this->opt_refund($obj);
            else
                $response = $this->opt_void($obj);

//            //BRAINTREE first find breintree transaction
//            $trans = Transaction::find($obj->card_trans_id);
//
//            //then select method based on transaction status
//            switch ($trans->status){
//                case 'settled':
//                case 'settling':
//                    $response = $this->opt_refund($obj);
//                    break;
//                case 'submitted_for_settlement':
//                    $response = $this->opt_void($obj);
//                    break;
//                default:
//                    $response = $this->opt_void($obj);
//                    break;
//            } //endwitch

        }
        catch(\Exception $e){
            //the transaction id not found
            $response['message'] = $e->getMessage();
        }


        //return message
        return $response;
    }

    /**
     * Charge money from card
     * @param $obj
     */
    public function charge_from_card($obj)
    {
        //todo: for the moment, just return null.
        return null;

        $user = User::find($obj->user_id);
        $message = '';

        //check if user exists
        if (!$user)
            return $message;

        //user has no valid card defined
        if (!$user->braintree_id) {
            $message = strtoupper("You have no valid card defined. Please enter one below!");
            return $message;
        } //endif

        try {
            $name = "massage services";
            $total = $obj->amount;

            $response = $user->invoiceFor($name,$total);

        } catch (\Exception $e) {
            $message = $e->getMessage();
            $message = str_ireplace('braintree','Zen London',$message);

            return $message;
        }

        //update transaction type from cash to card
        $data = [
            'card_trans_id' => $response->transaction->id,
            'card_trans_status' => (bool)$response->success,
            'card_details' => json_encode([
                'customerLocation' => $response->transaction->creditCardDetails->customerLocation,
                'cardType' => $response->transaction->creditCardDetails->cardType,
                'expirationDate' => $response->transaction->creditCardDetails->expirationDate,
                'imageUrl' => $response->transaction->creditCardDetails->imageUrl,
                'maskedNumber' => $response->transaction->creditCardDetails->maskedNumber,
                'last4' => $response->transaction->creditCardDetails->last4,
                'bin' => $response->transaction->creditCardDetails->bin,
            ]),
        ];

        //update transaction type
        $obj->update($data);
    }

    /**
     * Refund voucher value of booking order
     * @param BookingOrder $obj
     * @param bool $admin
     */
    protected function refund_voucher_of(BookingOrder $obj, $admin=false)
    {
        $response = [
            'success'=>false,
            'message'=>null,
        ];

        $info = json_decode($obj->orderInfo,true);

        if (!$admin){
            $safe_cancel = $this->can_be_safe_canceled($obj);
            $response = [
                'success'=>false,
                'message'=>null,
            ];
        }
        else
            $safe_cancel=true;

        //to refund voucher value, all conditions must be true
        if (!$safe_cancel || !$info['has_voucher'] || !$info['price_net'] || !$info['voucher']['code'])
            return $response;

        //continue with voucher value refund
        $qty=1;
        switch($info['therapistsOpt']){
            case '4hands':
                $qty = 1;
                break;
            default:
                $qty=1;
                break;
        } //end switch

        $voucherRepo = new VoucherRepository();
        $status = $voucherRepo->refund_value($info['voucher']['code'],$info['price_net'],$qty);

        //cancel invoice


        $response = [
            'success' => $status['status'],
            'message' => $status['message'],
        ];


        //return response
        return $response;
    }

    /**
     * Refund Function
     * @param $obj
     * @return array
     */
    protected function opt_refund($obj)
    {
        $gateway = Omnipay::create('SagePay\Direct');
        $gateway->setVendor(env('SAGEPAY_VENDOR_NAME'));
        $gateway->setTestMode(env('SAGEPAY_TEST_MODE'));

        $payload = json_decode($obj->card_trans_id,true);

        $result = $gateway->refund(array(
            'useAuthenticate' => false,
            'transactionReference' => $obj->card_trans_id,
            'transactionId' => $payload['VendorTxCode'].'-REF',
            'amount' => $obj->amount,
            'currency' => env('SAGEPAY_CCY'),
            'description' => 'Massage services cancelled',
        ))->send();

        $response = [
            'success'=>$result->isSuccessful(),
            'message'=>(!$result->isSuccessful())?$result->getMessage():trans('schedules::booking.text_successfully_refunded'),
        ];

        return $response;
    }

    /**
     * Void Function
     * @param $obj
     * @return array
     */
    protected function opt_void($obj)
    {
        $gateway = Omnipay::create('SagePay\Direct');
        $gateway->setVendor(env('SAGEPAY_VENDOR_NAME'));
        $gateway->setTestMode(env('SAGEPAY_TEST_MODE'));

        $result = $gateway->void(array(
            'useAuthenticate' => false,
            'transactionReference' => $obj->card_trans_id,
            'amount' => $obj->amount,
            'currency' => env('SAGEPAY_CCY'),
            'description' => 'Massage services cancelled',
        ))->send();

        $orderInfo = json_decode($obj->orderInfo,true);

        //void the extension
        if ($orderInfo['has_extension'] && array_key_exists('extension',$orderInfo)){
            $ext_result = $gateway->void(array(
                'useAuthenticate' => false,
                'transactionReference' => $orderInfo['extension']['card_details']['transaction_reference'],
                'amount' => $orderInfo['extension']['price'],
                'currency' => env('SAGEPAY_CCY'),
                'description' => 'Massage services - extension cancelled',
            ))->send();
        } //end void the extension


        $response = [
            'success'=>$result->isSuccessful(),
            'message'=>(!$result->isSuccessful())?$result->getMessage():trans('schedules::booking.text_successfully_refunded'),
        ];

        return $response;
    }

    /**
     * Cancel invoice of booking order
     */
    public function cancel_invoice_of($obj)
    {
        $status = false;
        $invoice = $obj->invoice;
        if (!$invoice)
            return $status;

        $repoInv = new BookingInvoiceRepository();
        $status = $repoInv->cancel_invoice($invoice->id);

        return $status;
    }

    /**
     * Set saved booking data into session
     * @param $obj
     */
    public function set_booking_data($obj)
    {
        //first unset any booking data
        $this->unset_booking_data();

        //get info of booking details
        $info = json_decode($obj->orderInfo,true);

        //set new data
        $data = [
            'postcode' => $info['postcode'],
            'duration' => $info['duration_id'],
            'massage' => $info['massage_type_id'],
            'date' => $obj->date->format('Y-m-d'),
            'hour' => $obj->hour,
            'selected_therapists' => $info['therapistIds'],
            'therapists_opt' => $info['therapistsOpt'],
            'therapists' => $info['therapistsNo'],
            'type' => $info['type'],
        ];

        //store new data into session
        Session::put('booking',$data);
    }

    /**
     * Set saved booking data into session
     * @param $obj
     */
    public function unset_booking_data()
    {
        Session::forget('booking');
    }

    /**
     * User can extends thier booking with another service
     *
     * @param $booking
     * @param $service
     * @return bool
     */
    public function can_extend_with($booking,$service)
    {
        //default status
        $status = false;

        //get booking therapists
        $therapists = $booking->therapists;

        //add extention time to the current booking + 15min buffer to check if booking will overlaps
        $addTime = $booking->duration_min+$service->duration+58;
        $addTime2 = $booking->duration_min+$service->duration;

        //calculate diff in minutes between end of massage session and current time
        $bookingEndTime = Carbon::createFromFormat('Y-m-d H:i',"{$booking->date->format('Y-m-d')} {$booking->hour}")->addMinutes($booking->duration_min);
        $diff = $bookingEndTime->diffInMinutes(Carbon::now(),false);

        $bookingEndTime2 = Carbon::createFromFormat('Y-m-d H:i',"{$booking->date->format('Y-m-d')} {$booking->hour}")->addMinutes($addTime2)->format('Y-m-d H:i');

        //check if extension ca be made regarding the therapist schedule
        $weekday = date('w',strtotime($booking->date));
        foreach($therapists as $th){
            $thWeekDaySchedule = $th->workingdays()->where('weekday',$weekday)->get()->first();
            if (!$thWeekDaySchedule)
                continue;
            $endOfSchedule = Carbon::createFromFormat('Y-m-d H:i A',"{$booking->date->format('Y-m-d')} {$thWeekDaySchedule->bo_end}")->format('Y-m-d H:i');

            $diffEndOfSchedule = strtotime($endOfSchedule) - strtotime($bookingEndTime2);

//            dd([
//                '$thWeekDaySchedule'=>$thWeekDaySchedule,
//                '$endOfSchedule'=>$endOfSchedule,
//                '$bookingEndTime' => $bookingEndTime2,
//                '$diffEndOfSchedule'=>$diffEndOfSchedule,
//            ]);

            if ($diffEndOfSchedule < 0)
                return false;

        } //endforeach


        //if the current time is bigger than the booking session time, add minutes which passed over session booking end time to the addTime variable.
        //this will
        if ($diff>0)
            $addTime += $diff;

        $timewithbuffer = floor($addTime/60).':'.($addTime%60);
        $count = 0;

        //for each therapist, check if selected service-extension overlap on another booking
        foreach($therapists as $trp){
//            DB::enableQueryLog();
            $thBookings = $trp->therapistbookings()
                ->where('date','=',$booking->date->format('Y-m-d'))
                ->whereRaw('hour between SUBTIME("'.$booking->hour.'","00:15") and ADDTIME("'.$booking->hour.'" ,"'.$timewithbuffer.'")')
                ->where('booking_id','!=',$booking->id)
                ->whereHas('booking',function($query){
                    return $query->where('is_active',1);
                })
                ->get();
//            dd(DB::getQueryLog());

            $count += $thBookings->count();

            //todo: check if other booking is in the session basket


        } //endforeach

        //if the therapists has bookings, deny extension
        if ($count)
            return $status;

        //if the therapists has no other bookings, let the user to extend his session
        $status = true;

        //return status
        return $status;
    }

    /**
     * Therapist can delay the booking with number of minutes (@param delay)
     * @param $booking
     * @param int $delay
     * @return bool
     */
    public function can_delay_with($booking,$delay)
    {
        //default status
        $status = false;
        $delay = (int)$delay;

        //get booking therapists
        $therapists = $booking->therapists;

        //add delay time to the current booking + 15min buffer to determine if booking will overlaps
        $addTime = $booking->duration_min+$delay+15;

        //calculate diff in minutes between end of massage session and current time
        $bookingEndTime = Carbon::createFromFormat('Y-m-d H:i',"{$booking->date->format('Y-m-d')} {$booking->hour}")->addMinutes($booking->duration_min);
        $diff = $bookingEndTime->diffInMinutes(Carbon::now(),false);

        //if the current time is bigger than the booking session time, add minutes which passed over session booking end time to the addTime variable.
        //this will
        if ($diff>0)
            $addTime += $diff;

        $timewithbuffer = floor($addTime/60).':'.($addTime%60);
        $count = 0;

        //for each therapist, check if selected service-extension overlap on another booking
        foreach($therapists as $trp){
//            DB::enableQueryLog();
            $thBookings = $trp->therapistbookings()
                ->where('date','=',$booking->date->format('Y-m-d'))
                ->whereRaw('hour between SUBTIME("'.$booking->hour.'","00:15") and ADDTIME("'.$booking->hour.'" ,"'.$timewithbuffer.'")')
                ->where('booking_id','!=',$booking->id)
                ->whereHas('booking',function($query){
                    return $query->where('is_active',1);
                })
                ->get();
//            dd(DB::getQueryLog());

            $count += $thBookings->count();

            //todo: check if other booking is in the session basket


        } //endforeach

        //if the therapists has bookings, deny extension
        if ($count)
            return $status;

        //if the therapists has no other bookings, let the user to extend his session
        $status = true;

        //return status
        return $status;
    }

    /**
     * Start booking threading
     * @param $booking
     */
    public function create_thread($booking)
    {
        $subject = $booking->treadsubject;
        $thread = Thread::where('subject',$subject)->first();

        //create the thread if not exists
        if (!$thread)
        {
            $thread = Thread::create(
                [
                    'subject' => $subject,
                ]
            );
        } //endif

        // create message
        Message::create(
            [
                'thread_id' => $thread->id,
                'user_id'   => $booking->user_id,
                'body'      => trans('schedules::booking.default_message_thread'),
            ]
        );

        // Add replier as a participant
        $participant = Participant::firstOrCreate(
            [
                'thread_id' => $thread->id,
                'user_id'   => $booking->user_id,
            ]
        );
        $participant->last_read = new Carbon();
        $participant->save();

        //add recipients: is this case the recipients are the therapists
        foreach($booking->therapists as $rec)
            $recipients[] = $rec->id;
        if (!empty($recipients))
            $thread->addParticipant($recipients);

    } //end method

    public function checkPhoneNumber($phone)
    {
        $phone = trim($phone);
        $phone = preg_replace("/[^0-9]/", "",$phone);

        $phoneLen = strlen($phone);
        $firstDigit = substr($phone,0,1);

        if($phoneLen >= 10 && $phoneLen<=14 && $firstDigit==0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

ZeroDay Forums Mini