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/public_html/test.tradze.com/app/Modules/Schedules/Repositories/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/tradze/public_html/test.tradze.com/app/Modules/Schedules/Repositories/BookingClass.php
<?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\SalonServiceDuration;
use App\Modules\Services\Models\SalonCategory;
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;
use App\Modules\Users\Models\UserServiceDuration;
use App\Modules\Schedules\Models\BasketTherapist;
use App\Modules\Schedules\Models\BasketVoucher;

use App\Modules\Users\Models\UserCoverageArea;


class BookingClass
{
    var $defaultServiceDuration = null;
    var $defaultSalonServiceDuration = null;
    public function __construct($defaultSalonServiceDuration = null)
    {
        $this->defaultServiceDuration = ServiceDuration::where('is_default', 1)->first();
        if ($defaultSalonServiceDuration != null)
            $this->defaultSalonServiceDuration = SalonServiceDuration::where('id', $defaultSalonServiceDuration)->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 = ['duration', 'massage', 'date', 'address'];
        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()
            ->with('servicetypes', 'mywork', 'reviews', 'therapistreview', 'stripeSubscription', 'userDoc')
            // ->whereHas('stripeSubscription', function ($query) {
            //     $query->whereDate('expire_date', '<', Carbon::now());
            // })
            // ->whereHas('userDoc', function ($query) {
            //     $query->where('is_approved', 1);
            // })
            // Only include users who have documents
            // ->whereHas('userDoc')
            // Exclude users who have any unapproved document
            ->whereDoesntHave('userDoc', function ($query) {
                $query->where('is_approved', 0);
            })
            ->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()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();

        // dd($users[0]->is_subscribed);

        //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();
                // dd($countSession);
                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()
            ->with('servicetypes', 'mywork', 'stripeSubscription', 'userDoc')
            // ->whereHas('stripeSubscription', function ($query) {
            //     $query->whereDate('expire_date', '>', Carbon::now());
            // })
            // ->whereHas('userDoc', function ($query) {
            //     $query->where('is_approved', 1);
            // })  
            // Only include users who have documents
            // ->whereHas('userDoc')
            // Exclude users who have any unapproved document
            ->whereDoesntHave('userDoc', function ($query) {
                $query->where('is_approved', 0);
            })
            ->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()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();

        // session Address
        $addr_longitude = '';
        $addr_latitude = '';
        $address = $booking['address'];
        if (!empty($address)) {
            //Formatted address
            $formattedAddr = str_replace(' ', '+', $address);
            //Send request and receive json data by address
            $geocodeFromAddr = file_get_contents('https://maps.googleapis.com/maps/api/geocode/json?address=' . $formattedAddr . '&sensor=false&key=' . config("googlemaps.key"));
            $output = json_decode($geocodeFromAddr);
            //Get latitude and longitute from json data
            $addr_longitude = !empty($output->results[0]->geometry->location->lat) ? $output->results[0]->geometry->location->lat : '';
            $addr_latitude = !empty($output->results[0]->geometry->location->lng) ? $output->results[0]->geometry->location->lng : '';
        }
        // check available users in coverage locations 
        $available_user_results = array();

        if (!empty($users)) {
            foreach ($users as $th) {
                $is_in_polygon = 0;
                $therapist_coverages_1 = UserCoverageArea::where('user_id', $th->id)->get();
                $therapist_coverages_last = UserCoverageArea::where('user_id', $th->id)->orderBy('id', 'DESC')->first();
                if (!empty($therapist_coverages_last->polygon_no)) {
                    foreach ($therapist_coverages_1 as $thp_cover) {
                        //for ($i = 0; $i <= $therapist_coverages_last->polygon_no; $i++) {
                        $therapist_coverages = UserCoverageArea::select('lng', 'lat')->where('user_id', $th->id)->where('polygon_no', $thp_cover->polygon_no)->get();
                        $polygon_lat_lng_Arr  = array();
                        foreach ($therapist_coverages as $coverages) {
                            $longitude = $coverages->lng;
                            $latitude  = $coverages->lat;
                            $polygon_lat_lng_Arr[] = $longitude . ' ' . $latitude;
                        }
                        $longitude_x = $addr_longitude; // x-coordinate of the point to test
                        $latitude_y  = $addr_latitude; // y-coordinate of the point to test
                        // echo $point = $longitude_x.' '.$latitude_y;
                        $point = $latitude_y . ' ' . $longitude_x;

                        //print_r($polygon_lat_lng_Arr);
                        if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)) {
                            $is_in_polygon++;
                        }
                        // echo $is_in_polygon;
                    }
                }

                if (!empty($addr_longitude) && !empty($addr_latitude)) {
                    if ($is_in_polygon > 0) {
                        $is_in_polygon = 0;
                        $user_profile = DB::table("users_profile")->where("user_id", $th->id)->first();
                        // dd("here");
                        if ($user_profile->massage_me_now) {
                            $available_user_results[] = $th;
                        }
                    }
                }
            }
        }
        // dd($available_user_results);
        // check unavailable users in coverage locations 
        $unavailable_user_results = array();
        if (!empty($usersUnAvailable)) {
            $is_in_polygon = 0;
            foreach ($usersUnAvailable as $th) {
                $therapist_coverages_1 = UserCoverageArea::where('user_id', $th->id)->get();
                $therapist_coverages_last = UserCoverageArea::where('user_id', $th->id)->orderBy('id', 'DESC')->first();
                if (!empty($therapist_coverages_last->polygon_no) && !empty($therapist_coverages_1)) {
                    foreach ($therapist_coverages_1 as $thp_cover) {
                        //for ($i = 0; $i <= $therapist_coverages_last->polygon_no; $i++) {
                        $therapist_coverages = UserCoverageArea::select('lng', 'lat')->where('user_id', $th->id)->where('polygon_no', $thp_cover->polygon_no)->get();
                        $polygon_lat_lng_Arr  = array();
                        foreach ($therapist_coverages as $coverages) {
                            $longitude = $coverages->lng;
                            $latitude  = $coverages->lat;
                            $polygon_lat_lng_Arr[] = $longitude . ' ' . $latitude;
                        }
                        $longitude_x = $addr_longitude; // x-coordinate of the point to test
                        $latitude_y  = $addr_latitude; // y-coordinate of the point to test
                        //echo $point = $longitude_x.' '.$latitude_y;
                        $point = $latitude_y . ' ' . $longitude_x;

                        //print_r($polygon_lat_lng_Arr);
                        if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)) {
                            $is_in_polygon++;
                        }
                        //echo $is_in_polygon;
                    }
                }

                if (!empty($addr_longitude) && !empty($addr_latitude)) {
                    if ($is_in_polygon > 0) {
                        $is_in_polygon = 0;
                        $user_profile = DB::table("users_profile")->where("user_id", $th->id)->first();
                        if ($user_profile->massage_me_now) {
                            $unavailable_user_results[] = $th;
                        }
                    }
                }
            }
        }



        $allUsers = array();
        // dd($allUsers);
        if ($available_user_results) {
            $allUsers['available'] = $available_user_results;
        }
        if ($unavailable_user_results) {
            $allUsers['unavailable'] = $unavailable_user_results;
        }
        //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()
            ->with('servicetypes', 'stripeSubscription', 'userDoc')
            ->whereDoesntHave('userDoc', function ($query) {
                $query->where('is_approved', 0);
            })
            ->where('is_book_now', 1)
            ->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()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();

        // return $users;
        // $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['address'])
            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['address']}, 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;
        $eta += 7;

        $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);

        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()
            ->with('profile', 'workingdays', 'mywork', 'therapistreview')
            ->where('id', $thId)
            ->first();
        // dd($user);
        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;
        }
    }

    public function salon_info($thId)
    {
        $user = User::OfSalon()
            ->with('profile', 'workingdays', 'mywork', 'reviews', 'salongallaryall')
            ->where('id', $thId)
            ->first();
        // dd($user);
        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;
        }
    }

    public function salon_therapist_info($thId)
    {
        $user = User::OfTherapists()
            ->with('profile', 'workingdays', 'mywork', 'therapistreview')
            ->where('id', $thId)
            ->first();

        if ($user) {
            $servicesArray = [];
            $services = $user->salonservicetypes()->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;
            }
        }
        // dd($current_30_days);
        return $current_30_days;
    }

    public function salon_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);
    }


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

        //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)->first();
        // dd("here 1", $whours);
        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('+10 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 {
            // $bookingDuration1 = ServiceDuration::find($booking['duration']);
            $bookingDuration = UserServiceDuration::where('services_duration_id', $booking['duration'])
                ->where('user_id', $th->id)
                ->first();
            if (!$bookingDuration) {
                $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 = UserServiceDuration::where('services_duration_id', $booking['duration'])
                ->where('user_id', $therapistId)
                ->first();
            // dd($bookingDuration);
            if (!$bookingDuration) {
                $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();
        $twds = $therapist->workingdays()->where('dayoff', 0)->where('weekday', $weekday)->get();
        // dd("ted",$twds);
        if (count($twds) > 0) {
            // $c = 0;
            foreach ($twds as $key => $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) {
                        // echo $c++;
                        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) {
                        // echo $c++;
                        if (isset($th_hours[$key]) && $th_hours[$key]['available'] == 0)
                            continue;
                        else
                            $th_hours[$key] = $value;
                    } //endforeach
                }
            }
        } //endif


        $results = array_merge($hours, $th_hours);
        // dd($results);
        //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;
            }
        }
    }

    public function getAndSetFirstAvailableDayHourSalon()
    {
        $booking = Session::get('booking');
        $cartItem = Session::get('cartItem');
        if (!isset($booking['date'])) {
            $date = date('Y-m-d');
        } else {
            $date = $booking['date'];
        }

        $hours = $this->all_salon_hours($date, $cartItem->duration_id);
        $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('Tradze – 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', 'Tradze', $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;
        }
    }


    // Salon therapist
    public function salontherapists()
    {
        $booking = Session::get('booking');
        $session = Session::all();
        // dd($session['cartItem']->salon_id);
        $users = collect();
        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['date', 'address'];
        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;

        // dd("here 2");
        //query therapists

        DB::enableQueryLog();
        $users = User::with('salonservicetypes', 'profile', 'mywork')->where('salon_id', $session['cartItem']->salon_id)
            ->whereHas('salonservicetypes', function ($query) use ($booking, $session) {
                // dd("here",$session['cartItem']->subcategory_id);
                if (isset($session['cartItem']->subcategory_id) && $session['cartItem']->subcategory_id > 0)
                    $query->where('users_servicetype.servicetype_id', '=', $session['cartItem']->subcategory_id);

                return $query;
            })
            ->whereHas('workingdays', function ($query) use ($booking, $session) {
                $weekday = date('w', strtotime($booking['date']));
                $hour = $booking['hour'] . ':00';
                $duration = SalonServiceDuration::find($session['cartItem']->duration_id)->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, $session) {
                if (!isset($booking['hour']))
                    return $query;

                $duration = SalonServiceDuration::find($session['cartItem']->duration_id)->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);
        // return $users;

        // dd($session);
        //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($session['cartItem'])) {
            $duration = SalonServiceDuration::find($session['cartItem']->duration_id);
            $bufferMin = 55;
            $buffer = $duration->duration + 10;
            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($session, $booking);
        $usersUnAvailable = User::with('salonservicetypes', 'profile', 'mywork')->where('salon_id', $session['cartItem']->salon_id)
            ->whereHas('salonservicetypes', function ($query) use ($booking, $session) {
                // dd("here",$session['cartItem']->subcategory_id);
                if (isset($session['cartItem']->subcategory_id) && $session['cartItem']->subcategory_id > 0)
                    $query->where('users_servicetype.servicetype_id', '=', $session['cartItem']->subcategory_id);

                return $query;
            })
            ->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();

        // return $usersUnAvailable;
        // session Address
        // $addr_longitude = '';
        // $addr_latitude = '';
        // $address = $booking['address'];
        // if(!empty($address)){
        //     //Formatted address
        //     $formattedAddr = str_replace(' ','+',$address);
        //     //Send request and receive json data by address
        //     $geocodeFromAddr = file_get_contents('https://maps.googleapis.com/maps/api/geocode/json?address='.$formattedAddr.'&sensor=false&key='.config("googlemaps.key"));
        //     $output = json_decode($geocodeFromAddr);
        //     //Get latitude and longitute from json data
        //     $addr_longitude = !empty($output->results[0]->geometry->location->lat)?$output->results[0]->geometry->location->lat:'';
        //     $addr_latitude = !empty($output->results[0]->geometry->location->lng)?$output->results[0]->geometry->location->lng:'';
        // }
        // check available users in coverage locations 
        // $available_user_results = array();
        // if(!empty($users)){
        //     foreach($users as $th){
        //     $is_in_polygon = 0;
        //         $therapist_coverages_1 = UserCoverageArea::where('user_id',$th->id)->get();
        //         $therapist_coverages_last = UserCoverageArea::where('user_id',$th->id)->orderBy('id','DESC')->first();
        //         if(!empty($therapist_coverages_last->polygon_no)){
        //             foreach($therapist_coverages_1 as $thp_cover){
        //             //for ($i = 0; $i <= $therapist_coverages_last->polygon_no; $i++) {
        //                 $therapist_coverages = UserCoverageArea::select('lng','lat')->where('user_id',$th->id)->where('polygon_no',$thp_cover->polygon_no)->get();
        //                 $polygon_lat_lng_Arr  = array();
        //                 foreach ($therapist_coverages as $coverages) {
        //                     $longitude = $coverages->lng;
        //                     $latitude  = $coverages->lat;
        //                     $polygon_lat_lng_Arr[] = $longitude.' '.$latitude;
        //                 }
        //                 $longitude_x = $addr_longitude; // x-coordinate of the point to test
        //                 $latitude_y  = $addr_latitude; // y-coordinate of the point to test
        //                 //echo $point = $longitude_x.' '.$latitude_y;
        //                 $point = $latitude_y.' '.$longitude_x;

        //                 //print_r($polygon_lat_lng_Arr);
        //                 if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)){
        //                   $is_in_polygon++;
        //                 }
        //                 //echo $is_in_polygon;
        //             }
        //         }

        //         if(!empty($addr_longitude) && !empty($addr_latitude)){
        //             if($is_in_polygon > 0){
        //                 $is_in_polygon = 0;
        //                 $user_profile = DB::table("users_profile")->where("user_id", $th->id)->first();
        //                 if($user_profile->massage_me_now){
        //                     $available_user_results[] = $th;
        //                 }
        //             }
        //         }
        //     }
        // }
        // return $usersUnAvailable;
        // check unavailable users in coverage locations 
        // $unavailable_user_results = array();
        // if(!empty($usersUnAvailable)){
        //     $is_in_polygon = 0;
        //     foreach($usersUnAvailable as $th){
        //         $therapist_coverages_1 = UserCoverageArea::where('user_id',$th->id)->get();
        //         $therapist_coverages_last = UserCoverageArea::where('user_id',$th->id)->orderBy('id','DESC')->first();
        //         if(!empty($therapist_coverages_last->polygon_no) && !empty($therapist_coverages_1)){
        //             foreach($therapist_coverages_1 as $thp_cover){
        //             //for ($i = 0; $i <= $therapist_coverages_last->polygon_no; $i++) {
        //                 $therapist_coverages = UserCoverageArea::select('lng','lat')->where('user_id',$th->id)->where('polygon_no',$thp_cover->polygon_no)->get();
        //                 $polygon_lat_lng_Arr  = array();
        //                 foreach ($therapist_coverages as $coverages) {
        //                     $longitude = $coverages->lng;
        //                     $latitude  = $coverages->lat;
        //                     $polygon_lat_lng_Arr[] = $longitude.' '.$latitude;
        //                 }
        //                 $longitude_x = $addr_longitude; // x-coordinate of the point to test
        //                 $latitude_y  = $addr_latitude; // y-coordinate of the point to test
        //                 //echo $point = $longitude_x.' '.$latitude_y;
        //                 $point = $latitude_y.' '.$longitude_x;

        //                 //print_r($polygon_lat_lng_Arr);
        //                 if (UserCoverageArea::pointInPolygon($point, $polygon_lat_lng_Arr)){
        //                   $is_in_polygon++;
        //                 }
        //                 //echo $is_in_polygon;
        //             }
        //         }

        //         if(!empty($addr_longitude) && !empty($addr_latitude)){
        //             if($is_in_polygon > 0){
        //                 $is_in_polygon = 0;
        //                 $user_profile = DB::table("users_profile")->where("user_id", $th->id)->first();
        //                 if($user_profile->massage_me_now){
        //                     $unavailable_user_results[] = $th;
        //                 }
        //             }
        //         }
        //     }
        // }



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


    public function salon_th_hours($date, $therapistId)
    {
        $booking = Session::get('booking');
        $session = Session::all();
        // dd($session['cartItem']->duration_id);
        $bookingPrice = 0;
        if (!isset($session['cartItem']) && $this->defaultServiceDuration) {
            Session::set('booking.duration', $this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->price;
            // return $bookingPrice;
            // dd($bookingDuration);
        } else {
            $bookingDuration = SalonServiceDuration::find($session['cartItem']->duration_id);
            $bookingPrice = $bookingDuration->price;
            // dd("here");
        }
        // return $bookingPrice;
        //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 = 10;

        //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('+10 minutes', $start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available' => 0,
                'hour' => date('H:i', $start),
                'price' => $bookingPrice,
                'is_booking' => false,
                'here' => true
            ];
        } //endwhile

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

        $th_hours = [];

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

        if ($twd) {
            $blockedHours = $therapist->blockedSalonHours($date);
            // dd($blockedHours);
            if (count($blockedHours)) {
                foreach ($this->process_salon_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_salon_th_hours($twd->bo_start, $twd->bo_end, null, $therapist, $date) as $key => $value) {
                    // dd($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);
        // dd($results);
        //available hours based on current duration selection and therapist schedule
        if (isset($session['cartItem'])) {
            $unvhours = [];
            $duration = SalonServiceDuration::find($session['cartItem']->duration_id)->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('+10 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
        // dd($results);
        //return hours
        return collect($results);
    }

    protected function process_salon_th_hours($starth, $endh, $excepts = [], $th, $date)
    {
        $booking = Session::get('booking');
        $session = Session::all();

        $bookingPrice = 0;
        if (!isset($session['cartItem']) && $this->defaultServiceDuration) {
            Session::set('booking.duration', $this->defaultServiceDuration->id);
            $bookingDuration = $this->defaultServiceDuration;
            $bookingPrice = $bookingDuration->discounted_price;
        } else {
            $bookingDuration = SalonServiceDuration::find($session['cartItem']->duration_id);
            $bookingPrice = $bookingDuration->discounted_price;
            // dd($bookingPrice, $bookingDuration);
        }
        $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('+10 minutes', $start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available' => 1,
                'hour' => date('H:i', $start),
                'price' => $bookingPrice,
                'is_booking' => $th->isBookingHour($date, $key)
            ];
        }
        // dd($excepts);
        //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;
    }

    /**
     * Remove item from basket
     * @param Request $request
     */
    public function cancel_cart($itemId)
    {
        //remove item from cart
        \Cart::remove($itemId);

        $sesTh = BasketTherapist::where('basket_item_id', $itemId)
            ->get();
        if ($sesTh) {
            foreach ($sesTh as $sth)
                $sth->delete();
        } //endif

        $sesCodes = BasketVoucher::where('basket_item_id', $itemId)
            ->get();
        if ($sesCodes) {
            foreach ($sesCodes as $scode)
                $scode->delete();
        } //endif

        //return response
        return response('success', 200);
    }
}

ZeroDay Forums Mini