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/app/Modules/Schedules/Repositories/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/tradze/public_html/app/Modules/Schedules/Repositories/BookingRepository.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\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 Spatie\Permission\Models\Role;
use Braintree\Transaction;
use Carbon\Carbon;
use Cmgmyr\Messenger\Models\Message;
use Cmgmyr\Messenger\Models\Participant;
use Cmgmyr\Messenger\Models\Thread;
use GoogleMaps\GoogleMaps;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Mockery\CountValidator\Exception;
use App\Modules\Notifications\Facades\NotifRepository;
use Omnipay\Omnipay;

class BookingRepository
{

    /**
     * Get available therapists
     * @return array
     */
    public function therapists()
    {
        $booking = Session::get('booking');
        // return $booking;
        $users = collect();
        //if one of mandatory fields is missing, return empty array results
        $mandatory_fields = ['postcode', 'duration', 'massage', 'date'];
        foreach ($mandatory_fields as $field) {
            if (!isset($booking[$field]))
                return $users;
        } //endforeach
        //get postcode zone
        $postcode = Postcode::where('postcode', $booking['postcode'])->first();

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

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

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

                return $query;
            })
            // ->whereHas('zones', function ($query) use ($postcode) {
            //     $query->where('users_area_coverage.zone_id', $postcode->zone_id);
            //     return $query;
            // })
            ->whereHas('workingdays', function ($query) use ($booking) {
                if (!array_key_exists('hour', $booking))
                    return $query;

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

                $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") 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 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")');
                //                $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;
            })

            //                    ->where(function($query) use ($booking){
            //                        if ($booking['selected_therapists']){
            ////                            $query->whereIn('id',$booking['selected_therapists']);
            //                        }
            //                        return $query;
            //                    })
            //            ->where(function($query) use ($booking){
            //                if (isset($booking['hour'])){
            //                    $query->whereDoesntHave('therapistbookings', function($query) use ($booking){
            //                            $query->where('date',$booking['date']);
            //                            $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();


        //        if (isset($booking['hour'])){
        //            foreach ($users as $key => $u) {
        //
        //                //remove user if has booking
        //                $count = $u->therapistbookings()
        //                    ->where('date', $booking['date'])
        //                    ->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")')
        //                    ->whereHas('booking', function ($query) {
        //                        return $query->where('is_active', 1);
        //                    })
        //                    ->count();
        //                if ($count) $users->forget($key);
        //
        //                //remove user if he is selected into a basket
        //                $countSession = $u->sessionBlockedTherapists()
        //                    ->where('date', $booking['date'])
        //                    ->whereRaw('`hour` between SUBTIME("' . $booking['hour'] . '","00:15:00") and ADDTIME(ADDTIME("' . $booking['hour'] . '",STR_TO_DATE(CONCAT(FLOOR(duration_min/60),\':\',MOD(duration_min,60)),"%h:%i:%s")),"0:15:00")')
        //                    ->count();
        //                if ($countSession) $users->forget($key);
        //
        //
        //                //check if user has a day off
        //
        //
        //
        //            } //end foreach: ecxclude users from results
        //
        //        } //endif
        //        dd($users);
        //unselect unavailable selected users
        if (isset($booking['selected_therapists'])) {
            $usersAvailable = [];
            foreach ($users as $usr)
                $usersAvailable[] = $usr->id;

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

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


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

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

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

                //                if ($u->id==86)
                //                    dd([
                //                        'booking'=>$booking,
                //                        'bufferMin'=>$bufferMin,
                //                        'buffer'=>$buffer,
                //                        'count' => $count
                //                    ]);

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

                //remove user if he is selected into a basket at the same date&hour + duration interval
                //                $countSession = $u->sessionBlockedTherapists()
                //                    ->where('date',$booking['date'])
                //                    ->whereRaw('`hour` between SUBTIME("'.$booking['hour'].'","'.$buffer.'") and ADDTIME(ADDTIME("'.$booking['hour'].'",STR_TO_DATE(CONCAT(FLOOR(duration_min/60),\':\',MOD(duration_min,60)),"%h:%i:%s")),"'.$buffer.'")')
                //                    ->count();
                $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
        //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);
        }


        //return results
        return $users;
    }

    /**
     * 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;

        // dd("here", $whours->bo_start, strtotime($whours->bo_start), strtotime(date('H:i')), date('H:i'),strtotime($whours->bo_start) > strtotime(date('H:i')));

        // dd("here", $whours->bo_start, strtotime(date('H:i')));
        //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 = ['postcode', 'duration', 'massage', 'date'];
        foreach ($mandatory_fields as $field) {
            if (!isset($booking[$field]))
                return $users;
        } //endforeach
        //get postcode zone
        $postcode = Postcode::where('postcode', $booking['postcode'])->first();

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

        if ($diff < 0)
            return $users;
        //query therapists
        // DB::enableQueryLog();
        $users = User::OfTherapists()
            ->with('servicetypes', 'mywork', 'reviews', 'therapistreview')
            ->whereHas('userDoc', function ($query) {
                $query->where('is_approved', 1)
                    ->where('is_legal_doc', 1)
                    ->whereDate('expire_date', '>=', Carbon::now());
            })
            ->whereHas('servicetypes', function ($query) use ($booking) {
                if (isset($booking['massage']) && $booking['massage'] > 0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->where('is_book_now', 1)
            // ->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');
            //     $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) {
                $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;
            // })
            ->whereHas('geoLocation', function ($query) use ($booking) {})
            ->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;
            })
            ->get()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();

        //delete unavailable selected users
        if (isset($booking['selected_therapists'])) {
            // dd("here", $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);
            // dd($user->firstAvHour);
        } //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();
    }

    public function gettherapistsNowRepo()
    {
        $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 = ['postcode', 'duration', 'massage', 'date'];
        foreach ($mandatory_fields as $field) {
            if (!isset($booking[$field]))
                return $users;
        } //endforeach
        //get postcode zone
        $postcode = Postcode::where('postcode', $booking['postcode'])->first();

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

        if ($diff < 0)
            return $users;

        $address = "{$booking['address']}";

        if(isset($booking['address']) && !empty($booking['address'])){
            $address = "{$booking['address']}";
        }else{
            $address = "{$booking['postcode']}, London UK";
        }

        $response = \GoogleMaps::load('geocoding')
            ->setParam(['address' => $address])
            ->get('results');
    
        if (empty($response['results']))
            $to = $address;
        else {
            $toGeo = $response['results'][0]['geometry']['location'];
            // $to = "{$toGeo['lat']},{$toGeo['lng']}";
            $to = [$toGeo['lat'], $toGeo['lng']];
        }

        $latitude = $to[0];
        $longitude = $to[1];
        $range = 26; //km

        // $usersDataInRange = DB::select("SELECT ul.user_id
        //                             FROM users_locations AS ul
        //                             WHERE ul.updated_at >= DATE_SUB(NOW(), INTERVAL 30 MINUTE)
        //                             AND (
        //                                 (
        //                                     ACOS(
        //                                         SIN(($latitude * PI() / 180)) 
        //                                         * SIN((ul.lat * PI() / 180)) 
        //                                         + COS(($latitude * PI() / 180)) 
        //                                         * COS((ul.lat * PI() / 180)) 
        //                                         * COS((($longitude - ul.lng) * PI() / 180))
        //                                     ) * 180 / PI()
        //                                 ) * 60 * 1.1515 * 1.609344
        //                             ) <= $range
        //                         ");

        $usersDataInRange = DB::select("SELECT ul.user_id
                                    FROM users_locations AS ul
                                    WHERE (
                                        (
                                            ACOS(
                                                SIN(($latitude * PI() / 180)) 
                                                * SIN((ul.lat * PI() / 180)) 
                                                + COS(($latitude * PI() / 180)) 
                                                * COS((ul.lat * PI() / 180)) 
                                                * COS((($longitude - ul.lng) * PI() / 180))
                                            ) * 180 / PI()
                                        ) * 60 * 1.1515 * 1.609344
                                    ) <= $range
                                ");

        // Convert to plain array of IDs
        $usersInRange = array_column($usersDataInRange, 'user_id');

        //query therapists
        // DB::enableQueryLog();
        $users = User::OfTherapists()
            ->with('servicetypes', 'mywork', 'reviews', 'therapistreview')
            ->whereIn('id', $usersInRange)
            ->whereHas('userDoc', function ($query) {
                $query->where('is_approved', 1)
                    ->where('is_legal_doc', 1)
                    ->whereDate('expire_date', '>=', Carbon::now());
            })
            ->whereHas('servicetypes', function ($query) use ($booking) {
                if (isset($booking['massage']) && $booking['massage'] > 0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->where('is_book_now', 1)
            ->where('stripe_connected', 1)
            ->whereHas('profile', function ($query){
                $query->where('massage_me_now', 1);
            })
            ->whereHas('geoLocation', function ($query) use ($booking) {})
            ->where(function ($query) use ($booking) {

                $query
                    ->whereDoesntHave('therapistbookings', function ($query) use ($booking) {
                        $duration = ServiceDuration::find($booking['duration']);
                        $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);
                            });
                        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_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;
            })
            ->get()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();


        //delete unavailable selected users
        if (isset($booking['selected_therapists'])) {
            // dd("here", $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

        foreach ($users as $user) {
            $user->firstAvHour = $this->getEstTravelTime($user, $to);
        }

        //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();

            // dd($checkduration,$userSchedule);
            // dd($userSchedule->bo_end,strtotime($userSchedule->bo_end),$u->firstAvHour . "+{$checkduration} minutes",strtotime($u->firstAvHour . "+{$checkduration} minutes"),strtotime($userSchedule->bo_end) < strtotime($u->firstAvHour . "+{$checkduration} minutes"));
            if (!$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();
    }

    public function gettherapistsNowRepoTestMode()
    {
        $users = collect();
        $booking = Session::get('booking');
        // $booking['date'] = Carbon::today()->format('Y-m-d');
        // $booking['hour'] = Carbon::today()->format('H:i');
        $booking['date'] = '2026-02-05';   // static date
        $booking['hour'] = '01:00';       // static time

        //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 = ['postcode', 'duration', 'massage', 'date'];
        foreach ($mandatory_fields as $field) {
            if (!isset($booking[$field]))
                return $users;
        } //endforeach
        //get postcode zone
        $postcode = Postcode::where('postcode', $booking['postcode'])->first();

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

        if ($diff < 0)
            return $users;

        // $address = "{$booking['postcode']}, London UK";
        $address = "{$booking['address']}";
        $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']}";
            $to = [$toGeo['lat'], $toGeo['lng']];
        }

        $latitude = $to[0];
        $longitude = $to[1];
        $range = 26; //km

        // $usersDataInRange = DB::select("SELECT ul.user_id
        //                             FROM users_locations AS ul
        //                             WHERE ul.updated_at >= DATE_SUB(NOW(), INTERVAL 30 MINUTE)
        //                             AND (
        //                                 (
        //                                     ACOS(
        //                                         SIN(($latitude * PI() / 180)) 
        //                                         * SIN((ul.lat * PI() / 180)) 
        //                                         + COS(($latitude * PI() / 180)) 
        //                                         * COS((ul.lat * PI() / 180)) 
        //                                         * COS((($longitude - ul.lng) * PI() / 180))
        //                                     ) * 180 / PI()
        //                                 ) * 60 * 1.1515 * 1.609344
        //                             ) <= $range
        //                         ");

        $usersDataInRange = DB::select("SELECT ul.user_id
                                    FROM users_locations AS ul
                                    WHERE (
                                        (
                                            ACOS(
                                                SIN(($latitude * PI() / 180)) 
                                                * SIN((ul.lat * PI() / 180)) 
                                                + COS(($latitude * PI() / 180)) 
                                                * COS((ul.lat * PI() / 180)) 
                                                * COS((($longitude - ul.lng) * PI() / 180))
                                            ) * 180 / PI()
                                        ) * 60 * 1.1515 * 1.609344
                                    ) <= $range
                                ");

        // Convert to plain array of IDs
        $usersInRange = array_column($usersDataInRange, 'user_id');

        //query therapists
        // DB::enableQueryLog();
        $users = User::OfTherapists()
            ->with('servicetypes', 'mywork', 'reviews', 'therapistreview')
            ->whereIn('id', $usersInRange)
            ->whereHas('userDoc', function ($query) {
                $query->where('is_approved', 1)
                    ->where('is_legal_doc', 1)
                    ->whereDate('expire_date', '>=', Carbon::now());
            })
            ->whereHas('servicetypes', function ($query) use ($booking) {
                if (isset($booking['massage']) && $booking['massage'] > 0)
                    $query->where('users_servicetype.servicetype_id', $booking['massage']);

                return $query;
            })
            ->where('is_book_now', 1)
            ->whereHas('profile', function ($query){
                $query->where('massage_me_now', 1);
            })
            // ->whereDoesntHave('daysoff', function ($query) use ($booking) {
            //     $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');
            //     return $query;
            // })
            ->whereHas('geoLocation', function ($query) use ($booking) {})
            ->where(function ($query) use ($booking) {

                $query
                    ->whereDoesntHave('therapistbookings', function ($query) use ($booking) {
                        $duration = ServiceDuration::find($booking['duration']);
                        $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);
                            });
                        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_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;
            })
            ->get()
            ->filter(function ($user) {
                return $user->is_subscribed == true;
            })
            ->values();

        //delete unavailable selected users
        if (isset($booking['selected_therapists'])) {
            // dd("here", $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

        foreach ($users as $user) {
            $user->firstAvHour = $this->getEstTravelTime($user, $to);
        }

        //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($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;
        // dd($user->geoLocation);
        $fromGeo = [
            'lat' => $user->geoLocation->lat,
            'lng' => $user->geoLocation->lng
        ];

        if (!$fromGeo || !$booking['postcode'])
            return $result;

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

        $address = "{$booking['postcode']}, London UK";
        $response = \GoogleMaps::load('geocoding')
            ->setParam(['address' => $address])
            ->get('results');
        if (empty($response['results']))
            $to = $address;
        else {
            $toGeo = $response['results'][0]['geometry']['location'];
            $to = "{$toGeo['lat']},{$toGeo['lng']}";
        } //endif
        $mode = $user->transport_mode;
        $eta = $this->gTimeTravel($from, $to, $mode);
        // dd($eta);

        if ((!$eta && $eta != '0.0') || $eta > 60)
            return null;

        // dd("here", $eta);
        //add booking buffer for reservation process: equivalent with the availability in the basket
        $eta += 5;

        $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',
        ];
        // dd($params);
        $direction = \GoogleMaps::load('directions')
            ->setParam($params)
            ->get();

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

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

        //return eta
        return $eta;
    }

    /**
     * Calculated estimated time arrival using google maps api: directions
     * @param $from
     * @param $to
     * @param string $mode
     * @return mixed
     */
    protected function getEstTravelTime($user, $to)
    {
        if (!$user || !$user->geoLocation) {
            return null;
        }

        $lat1 = (float) $user->geoLocation->lat;
        $lon1 = (float) $user->geoLocation->lng;

        $lat2 = (float) $to[0];
        $lon2 = (float) $to[1];

        // Earth radius in KM
        $earthRadius = 6371;

        // Haversine formula
        $dLat = deg2rad($lat2 - $lat1);
        $dLon = deg2rad($lon2 - $lon1);

        $a = sin($dLat / 2) * sin($dLat / 2) +
            cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
            sin($dLon / 2) * sin($dLon / 2);

        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        $distanceKm = $earthRadius * $c;

        // Road & traffic adjustment (city)
        $roadFactor = 1.4;
        $adjustedDistance = $distanceKm * $roadFactor;

        // Average speed (km/h)
        $avgSpeed = 35;

        // ETA in minutes
        $etaMinutes = ceil(($adjustedDistance / $avgSpeed) * 60);

        // Buffer for parking / signals
        $buffer = 0;
        return Carbon::now()
            ->addMinutes($etaMinutes + $buffer)
            ->format('H:i');
    }

    /**
     * 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;
    }

    /**
     * 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 available days
     *
     * @return array
     */
    public function days()
    {
        $days = [];

        //todo
        //return dates
        return $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');

        //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)
        ];

        //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)
            ];
        } //endwhile

        $results = $hours;


        //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 hours($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;

        //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);
        // dd($key);
        $hours[$key] = [
            'available' => 0,
            'hour' => date('H:i', $start)
        ];

        //add remaining hours
        while ($start !== $end && $start <= $end) {
            $start = strtotime('+30 minutes', $start);
            $key = date('H:i', $start);
            $hours[$key] = [
                'available' => 0,
                'hour' => date('H:i', $start)
            ];
        } //endwhile
        
        // dd($therapist);
        //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 = [];
        // dd($booking);
        //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();
            $twds = $therapist->workingdays()->where('dayoff', 0)->where('weekday', $weekday)->get();

            if ($twds) {
                $blockedHours = $therapist->blockedHours($date);
                foreach ($twds as $key => $twd) {
                    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 + $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' => 0,
                    'hour' => date('H:i', $start)
                ];
                // dd($start, $end);
                while ($start !== $end && $start <= $end) {
                    $start = strtotime('+30 minutes', $start);
                    // dd(date('H:i', $start));

                    $key = date('H:i', $start);
                    // echo $key;
                    $unvhours[$key] = [
                        'available' => 0,
                        'hour' => date('H:i', $start)
                    ];
                } //endwhile
            } //endforeach

            $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 = [])
    {
        $hours = [];
        $start = strtotime($starth);
        $end = strtotime($endh);

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

        //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)
            ];
        }

        //remove hours
        if (!empty($excepts)) {
            foreach ($excepts as $ex) {
                $hours[$ex]['available'] = 0;
                //                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;
    }

    /**
     * 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 = [];
        if (isset($orderInfo['salon_id']) && $orderInfo['salon_id'] != "") {
            $therapistsIds[] =  $orderInfo['therapist']['id'];
        } else {
            $therapistsIds = $orderInfo['therapistIds'];
        }
        // dd($therapistsIds);
        //cancel order
        $obj->is_active = 0;
        $current_date_time = Carbon::now()->toDateTimeString();
        $obj->cancelled_at = $current_date_time;
        $success = $obj->save();


        //refund voucher and cvancel invoice only if the booking can be safe canceled
        if ($this->can_be_safe_canceled($obj)) {
            if (isset($orderInfo['salon_id']) && $orderInfo['salon_id'] != "") {
            } else {
                //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 website – cancel booking  #' . $data['order']->id);
            });

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

            foreach ($thusers as $therapist) {
                $data['user'] = $therapist;
                Mail::send('schedules::frontend.emails.therapist_cancelorder', ['user' => $therapist, 'order' => $obj], function ($m) use ($data) {
                    $m->from(env('MAIL_FROM'), env('APP_NAME'));
                    $m->to($data['user']->email, $data['user']->name);
                    $m->bcc(explode(',', env('MAIL_NEWORDER_BCC')), env('MAIL_NEWORDER_BCC_NAME'));
                    $m->subject(env('APP_NAME') . ' – cancel booking  #' . $data['order']->id);
                });
            }
        } else {
            //rollback actions
            DB::rollBack();
        } //end elseif transaction
        //create response array
        $response = [
            'success' => $success,
            'message' => $refundStatus['message'],
        ];

        //return response
        return $response;
    }


    // Edited by CIS
    public function active_order(BookingOrder $obj, $admin = false)
    {
        $obj->is_active = 1;
        $obj->cancelled_at = NULL;
        $success = $obj->save();

        if ($success) {
            $response = [
                'success' => $success,
                'message' => null
            ];
        } else {
            $response = [
                'success' => $success,
                'message' => null
            ];
        }

        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' => !empty($info['postcode']) ? $info['postcode'] : '',
            'duration' => $info['duration_id'],
            'massage' => !empty($info['massage_type_id']) ? $info['massage_type_id'] : 0,
            'date' => $obj->date->format('Y-m-d'),
            'hour' => $obj->hour,
            'selected_therapists' => !empty($info['therapistIds']) ? $info['therapistIds'] : '',
            'therapists_opt' => !empty($info['therapistsOpt']) ? $info['therapistsOpt'] : '',
            'therapists' => !empty($info['therapistsNo']) ? $info['therapistsNo'] : '',
            'type' => !empty($info['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);

              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
}

ZeroDay Forums Mini