<?php

namespace App\Http\Controllers\BookingEngine\V1;

use App\Core\Mail\BookingInvoiceUpdateMail;
use App\Core\Service\BookingService;
use App\Core\Service\BookingRoomService;
use App\Core\Service\BookingRoomPaxService;
use App\Core\Service\BookingContactService;
use App\Core\Service\BookingPaymentService;
use App\Core\Service\ChannelManagerBookingService;
use App\Core\Service\PropertyBookingEngineSearchService;
use App\Core\Service\PropertyChannelService;
use App\Core\Service\PropertyRoomAvailabilityService;
use App\Core\Service\PropertyChannelMappingService;
use App\Core\Service\PropertyPaymentService;
use App\Core\Service\NewBookingMailService;
use App\Core\Service\NotificationService;
use App\Core\Service\ChannelManagerPropertyMappingService;
use App\Core\Service\PropertyAddonService;
use App\Core\Service\BookingAddonService;
use App\Core\Service\PropertyChannelCouponService;
use App\Core\Mail\BookingPropertyAddonUpdateMail;
use App\Core\Mail\BookingCancellationConfirmCodeMail;
use App\Core\Service\PropertyBookingEngineService;
use App\Core\Mail\CancelBookingMail;
use App\Core\Mail\BookingCancellationRequestMail;
use App\Core\Service\CurrencyService;

use Carbon\Carbon;
use Illuminate\Http\Request;

use App;
use Illuminate\Mail\Mailer;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;
use Exception;
use App\Exceptions\ApiErrorException;

class BookingController extends BookingEngineBaseController
{

    private $request;
    private $newBookingMailService;

    public function __construct(
        Request                              $request,
        BookingService                       $bookingService,
        BookingRoomService                   $bookingRoomService,
        BookingRoomPaxService                $bookingRoomPaxService,
        BookingContactService                $bookingContactService,
        BookingPaymentService                $bookingPaymentService,
        PropertyRoomAvailabilityService      $propertyRoomAvailabilityService,
        PropertyChannelMappingService        $propertyChannelMappingService,
        NewBookingMailService                $newBookingMailService,
        PropertyPaymentService               $propertyPaymentService,
        NotificationService                  $notificationService,
        ChannelManagerBookingService         $channelManagerBookingService,
        ChannelManagerPropertyMappingService $channelManagerPropertyMappingService,
        PropertyAddonService                 $propertyAddonService,
        BookingAddonService                  $bookingAddonService,
        PropertyChannelCouponService         $propertyChannelCouponService,
        PropertyBookingEngineSearchService   $propertyBookingEngineSearchService,
        PropertyChannelService               $channelService,
        PropertyBookingEngineService         $propertyBookingEngineService,
        CurrencyService                      $currencyService,
        Mailer                               $mailer
    )
    {
        $this->request = $request;
        $this->bookingService = $bookingService;
        $this->bookingRoomService = $bookingRoomService;
        $this->bookingRoomPaxService = $bookingRoomPaxService;
        $this->bookingContactService = $bookingContactService;
        $this->bookingPaymentService = $bookingPaymentService;
        $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService;
        $this->propertyChannelMappingService = $propertyChannelMappingService;
        $this->propertyPaymentService = $propertyPaymentService;
        $this->channelManagerBookingService = $channelManagerBookingService;
        $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService;

        $this->channelId = $this->request->channelId;
        $this->channelToken = $this->request->channelToken;
        $this->bookingEngineToken = $this->request->bookingEngineToken;
        $this->bookingEnginePropertyId = $this->request->bookingEnginePropertyId;
        $this->newBookingMailService = $newBookingMailService;
        $this->notificationService = $notificationService;
        $this->propertyAddonService = $propertyAddonService;
        $this->bookingAddonService = $bookingAddonService;
        $this->propertyChannelCouponService = $propertyChannelCouponService;
        $this->propertyBookingEngineSearchService = $propertyBookingEngineSearchService;
        $this->channelService = $channelService;
        $this->propertyBookingEngineService = $propertyBookingEngineService;
        $this->currencyService = $currencyService;
        $this->mailer = $mailer;
    }

    public function checkRoomAndCacheData($roomInfo, $searchCacheData)
    {

        $checkRoomAndCache = true;

        $roomPricesCollect = collect($searchCacheData['roomPrices']);

        foreach ($roomInfo as $roomKey => $room) {

            $cachedSearchPriceKeys = $roomPricesCollect->where('occupancyCode', $room['occupancyCode'])->keys()->toArray();

            if (!in_array($room['rateKey'], $cachedSearchPriceKeys)) {
                $checkRoomAndCache = false;
                continue;
            }
        }

        return $checkRoomAndCache;
    }

    public function getPropertyRoomAndRoomRateAvailability($param = [])
    {

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

        try {

            $requestParam = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $param['property_id']],
                    ['field' => 'availability', 'condition' => '>', 'value' => 0],
                    ['field' => 'date', 'condition' => '>=', 'value' => $param['checkIn']],
                    ['field' => 'date', 'condition' => '<', 'value' => $param['checkOut']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'with' => ['roomDetail.propertyRoomConnected'],
                'orderBy' => [
                    ['field' => 'availability_type_id', 'value' => 'ASC'],
                    ['field' => 'property_room_id', 'value' => 'ASC'],
                    ['field' => 'room_rate_mapping_id', 'value' => 'ASC'],
                    ['field' => 'date', 'value' => 'ASC']
                ],
            ];

            $getPropertyRoomAndRoomRateAvailability = $this->propertyRoomAvailabilityService->select($requestParam);

            if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) {
                throw new ApiErrorException(lang('PropertyRoomAndRoomRateAvailability not found'));
            }

            $response = [
                'status' => true,
                'data' => $getPropertyRoomAndRoomRateAvailability['data']
            ];


        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }


        return $response;

    }

    public function getChannelPropertyDetail($channelId, $propertyId)
    {

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

        try {

            $requestParam = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'with' => [
                    'property', 'channelAvailabilityType', 'channelBookingType', 'channelRoomPricingType',
                    'channelBookingPaymentType.paymentType', 'channel'
                ],
                'firstRow' => true,
            ];

            $getChannelProperty = $this->propertyChannelMappingService->select($requestParam);

            $response = [
                'status' => true,
                'data' => $getChannelProperty['data'],
            ];


        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
        }


        return $response;

    }

    public function booking(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $roomInfo = $params['roomInfo'];
            $contactInfo = $params['contactInfo'];
            $searchKey = $params['searchKey'];
            $couponCode = fillOnUndefined($params, 'couponCode');
            $propertyId = $this->bookingEnginePropertyId;
            $extraParam = $params['extraParam'] ?? null;

            if (empty($propertyId)) {
                $propertyId = $params['propertyId'];
            }

            //SearchKey Check Data
            $searchCacheData = $this->getCacheDataWithSearchKey($searchKey);

            if (empty($searchCacheData)) {
                throw new ApiErrorException(lang('Cache data was expired.'));
            }

            $requestParams = $searchCacheData['requestParams'];

            //$checkRoomAndCacheData Check Data
            $checkRoomAndCacheData = $this->checkRoomAndCacheData($roomInfo, $searchCacheData);
            if (!$checkRoomAndCacheData) {
                throw new ApiErrorException(lang('Room rate keys are not matched.'));
            }


            $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }

            $getChannelPropertyDetail = $getChannelPropertyDetail['data'];

            $currencyCode = null;
            $currencyCodes = [];
            $paymentCodes = [];
            $propertyIds = [];
            $totalRoomsPrice = 0;

            $roomAvailabilityKeyGroup = [];
            $searchCacheRoomPrices = $searchCacheData['roomPrices'];
            foreach ($roomInfo as $roomOccupancy => $room) {

                $rateKeyCodeDecoded = $this->rateKeyCodeDecode($searchCacheRoomPrices[$room['rateKey']]['rateKeyCode']);

                if ($rateKeyCodeDecoded['availabilityTypeId'] == 1) {

                    if (!isset($roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count'])) {
                        $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count'] = 0;
                    }

                    $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['count']++;
                    $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']]['detail'] = $rateKeyCodeDecoded;

                } else {


                    if (!isset($roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count'])) {
                        $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count'] = 0;
                    }

                    $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['count']++;
                    $roomAvailabilityKeyGroup[$rateKeyCodeDecoded['availabilityTypeId']][$rateKeyCodeDecoded['roomId']][$rateKeyCodeDecoded['rateMappingId']]['detail'] = $rateKeyCodeDecoded;

                }


                $totalRoomsPrice += $searchCacheData['roomPrices'][$room['rateKey']]['total'];
                $currencyCodes[] = $searchCacheData['roomPrices'][$room['rateKey']]['currency'];
                $paymentCodes[] = $searchCacheData['roomPrices'][$room['rateKey']]['bookingPaymentType']['code'];
                $propertyIds[] = $rateKeyCodeDecoded['propertyId'];

            }

            $currencyCodes = array_unique($currencyCodes);
            if (count($currencyCodes) != 1) {
                throw new ApiErrorException(lang('Currency Code not matched'));
            }

            $currencyCode = reset($currencyCodes);

            $paymentCodes = array_unique($paymentCodes);
            if (count($paymentCodes) != 1) {
                throw new ApiErrorException(lang('Payment Code not matched'));
            }

            $paymentCode = reset($paymentCodes);

            if ($paymentCode != $params['paymentInfo']['paymentCode']) {
                throw new ApiErrorException(lang('Payment Code and Payment Code Param not matched'));
            }

            $propertyIds = array_unique($propertyIds);
            if (count($propertyIds) != 1) {
                throw new ApiErrorException(lang('Property ID not matched'));
            }

            if (reset($propertyIds) != $propertyId) {
                throw new ApiErrorException(lang('Property ID not matched'));
            }

            //dd($roomInfo);

            $roomInfoCollect = collect($roomInfo)->groupBy('occupancyCode')->toArray();
            $roomsByOccupancies = $this->getRoomsByOccupancies($requestParams['rooms']);
            foreach ($roomsByOccupancies as $roomOccupancyKey => $roomsByOccupancy) {

                if (!isset($roomInfoCollect[$roomOccupancyKey])) {
                    throw new ApiErrorException(lang('Room Occupancy Key not matched'));
                }

                if ($roomsByOccupancy['roomCount'] != count($roomInfoCollect[$roomOccupancyKey])) {
                    throw new ApiErrorException(lang('Room Occupancy Count and Request Room Occupancy Count not matched'));
                }
            }


            $propertyRoomAndRoomRateAvailabilityParam = [
                'property_id' => $propertyId,
                'checkIn' => $searchCacheData['requestParams']['date']['checkIn'],
                'checkOut' => $searchCacheData['requestParams']['date']['checkOut'],
            ];

            $getPropertyRoomAndRoomRateAvailability = $this->getPropertyRoomAndRoomRateAvailability($propertyRoomAndRoomRateAvailabilityParam);

            if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) {
                throw new ApiErrorException(lang('Room or rooms not available.'));
            }

            $propertyRoomAndRoomRateAvailability = collect($getPropertyRoomAndRoomRateAvailability['data']);
            $dateByDay = $this->getDateByDay($searchCacheData['requestParams']['date']);


            $bookingAddon = [];
            $totalAddonPrice = 0;


            if (isset($params['addonInfo'])) {

                $propertyChannelAddonCriteria = [
                    'criteria' => [
                        ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']],
                        ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']],
                        ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ],
                    'with' => ['propertyAddon.fact']
                ];
                $columns = ['id', 'property_id', 'channel_id', 'property_addon_id', 'amount', 'type', 'min_stay'];
                $selectPropertyChannelAddon = $this->propertyAddonService->selectPropertyChannelAddon($propertyChannelAddonCriteria, $columns);

                $propertyChannelAddon = [];
                if ($selectPropertyChannelAddon['status'] == 'success') {
                    $propertyChannelAddon = $selectPropertyChannelAddon['data'];
                }

                if (empty($propertyChannelAddon)) {
                    throw new ApiErrorException(lang('No additional defined products found.'));
                }

                $propertyChannelAddonCollect = collect($propertyChannelAddon);
                $propertyChannelAddonIds = $propertyChannelAddonCollect->pluck('id');
                $propertyChannelAddonIds = $propertyChannelAddonIds ? $propertyChannelAddonIds->toArray() : [];
                $nightOfStay = Carbon::parse($searchCacheData['requestParams']['date']['checkIn'])->diff(Carbon::parse($searchCacheData['requestParams']['date']['checkOut']))->days;


                $addonInfo = collect($params['addonInfo']);
                $addonInfoIds = $addonInfo->pluck('id');
                $addonInfoIds = $addonInfoIds ? $addonInfoIds->toArray() : [];


                if (count(array_intersect($addonInfoIds, $propertyChannelAddonIds)) != count($addonInfoIds)) {
                    throw new ApiErrorException(lang('Additional product values submitted do not match.'));
                }

                foreach ($params['addonInfo'] as $addonInfo) {

                    $selectedAddon = $propertyChannelAddonCollect->where('id', $addonInfo['id'])->first();

                    $addonInfoAttribute = null;
                    /*if (isset($addonInfo['attribute'])) {
                        foreach ($addonInfo['attribute'] as $addonAttribute) {
                            if (count(array_intersect_key($addonAttribute, $selectedAddon['property_addon']['attributeArray'])) != count($addonAttribute)) {
                                throw new ApiErrorException(lang('Additional product attribute values submitted do not match.'));
                            }
                        }
                        $addonInfoAttribute = $addonInfo['attribute'];
                    }*/

                    if (!is_null($selectedAddon['min_stay']) && $selectedAddon['min_stay'] <= $nightOfStay) {
                        $selectedAddon['amount'] = 0;
                    }

                    $bookingAddon[$selectedAddon['id']] = [
                        'property_addon_id' => $selectedAddon['property_addon_id'],
                        'property_channel_addon_id' => $selectedAddon['id'],
                        'count' => $addonInfo['count'],
                        'amount' => $selectedAddon['amount'],
                        'total' => moneyDoubleFormatDecimal($addonInfo['count'] * $selectedAddon['amount']),
                        'currency_code' => $currencyCode,
                        'attribute' => $addonInfoAttribute
                    ];

                    $totalAddonPrice += $bookingAddon[$selectedAddon['id']]['total'];
                }

            }

            $couponCodeData = [];
            if (!is_null($couponCode)) {

                $propertyChannelCouponCriteria = [
                    'criteria' => [
                        ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']],
                        ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']],
                        ['field' => 'code', 'condition' => '=', 'value' => $couponCode],
                        ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()],
                        ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()],
                        ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ],
                    'firstRow' => true
                ];

                $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria);
                if ($propertyChannelCoupon['status'] == 'success') {
                    $couponCodeData = $propertyChannelCoupon['data'];
                }

                //Reservation Date Check
                $reservationDateCheck = true;
                if (!is_null($couponCodeData['reservation_start_date']) && !is_null($couponCodeData['reservation_end_date'])) {
                    if (isset($searchCacheData['requestParams']['date']['checkIn']) && isset($searchCacheData['requestParams']['date']['checkOut'])) {
                        $reservationDateCheck = Carbon::parse($couponCodeData['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($searchCacheData['requestParams']['date']['checkIn']))
                            && Carbon::parse($couponCodeData['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($searchCacheData['requestParams']['date']['checkOut']));
                    }
                }

                if(!$reservationDateCheck) {
                    $couponCodeData = [];
                }

            }

            if (!isset($params['bookingCode'])) {

                //TODO: VALIDATORs
                //1. Roominfo içindeki her bir occupancyCode ile paxes alar validatorden geçecek
                //2. paxes için valdiatorden geçecek, eğer çocuk ise dt zorunlu, type, name, surname, citizen zorunlu
                //3. contactInfo içi komple zorunlu validatorden geçecek
                //4. searchKey, propertyId, contactInfo, roomInfo, paymentInfo zorunlu
                //5. gelen ödeme tipi ile rate lerde ödmee tipleri kontrol edilecek


                ///BOOKING START
                DB::beginTransaction();


                $bookingCode = getCodeGenerate('BKG');
                $bookingStatus = in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) ? 1 : 2;
                //1. Booking
                //2. PreBooking


                $totalRoomsPriceBeforeDiscount = $totalRoomsPrice;
                $bookingDiscountAmount = 0;
                if (!empty($couponCodeData)) {
                    if ($couponCodeData['type'] == 'PER') {
                        $bookingDiscountAmount = ($totalRoomsPriceBeforeDiscount * $couponCodeData['value'] / 100);
                    } elseif ($couponCodeData['type'] == 'FIX') {
                        $bookingDiscountAmount = $couponCodeData['value'];
                    }
                    $totalRoomsPrice = $totalRoomsPrice - $bookingDiscountAmount;
                }

                $bookingTotalAmount = $totalRoomsPrice + $totalAddonPrice;


                //Wholesaler MARKUP
                $channelPropertyMarkup = [];
                if (in_array($getChannelPropertyDetail['channel']['channel_category_id'], [7])) {

                    if (!is_null($getChannelPropertyDetail['markup']) && !empty($getChannelPropertyDetail['channel']['default_currency'])) {

                        $lastExchangeRate = 1;
                        if ($getChannelPropertyDetail['currency_code'] != $getChannelPropertyDetail['channel']['default_currency']) {
                            $lastExchangeRate = $this->currencyService->lastExchangeRate($getChannelPropertyDetail['currency_code'], $getChannelPropertyDetail['channel']['default_currency']);
                            if ($lastExchangeRate['status'] == 'success') {
                                $lastExchangeRate = $lastExchangeRate['data'];
                            }
                        }

                        $connectedChannelAction = !is_null($getChannelPropertyDetail['connected_channel_action']) ? json_decode($getChannelPropertyDetail['connected_channel_action'], 1) : null;

                        $channelPropertyMarkup = [
                            'channel_discount' => !empty($connectedChannelAction) && isset($connectedChannelAction['value']) ? $connectedChannelAction['value'] : null,
                            'channel_markup' => $getChannelPropertyDetail['markup'],
                            'channel_currency_code' => $getChannelPropertyDetail['channel']['default_currency'],
                            'channel_currency_exchange' => $lastExchangeRate,
                        ];

                    }
                }
                //Wholesaler MARKUP

                $bannedCharacters = ['<','>', ';', '(', ')', '=', ':', 'shell_exec'];
                $bannedCheckParameter = ['name', 'surname', 'phone_number', 'email', 'note'];

                $bookingCreateParam = [
                    'property_id' => $propertyId,
                    'channel_id' => $this->channelId,
                    'booking_code' => $bookingCode,
                    'search_key' => $params['searchKey'],
                    'checkin_date' => $searchCacheData['requestParams']['date']['checkIn'],
                    'checkout_date' => $searchCacheData['requestParams']['date']['checkOut'],
                    'rooms' => json_encode($searchCacheData['requestParams']['rooms']),
                    'payment_type_code' => $params['paymentInfo']['paymentCode'],
                    'room_amount' => $totalRoomsPriceBeforeDiscount,
                    'addon_amount' => $totalAddonPrice,
                    'discount_amount' => $bookingDiscountAmount,
                    'total' => $bookingTotalAmount,
                    'currency_code' => $currencyCode,
                    'channel_token' => $this->channelToken,
                    'booking_engine_token' => $this->bookingEngineToken,
                    'status' => $bookingStatus,
                    'channel_discount' => fillOnUndefined($channelPropertyMarkup, 'channel_discount'),
                    'channel_markup' => fillOnUndefined($channelPropertyMarkup, 'channel_markup'),
                    'channel_currency_code' => fillOnUndefined($channelPropertyMarkup, 'channel_currency_code'),
                    'channel_currency_exchange' => fillOnUndefined($channelPropertyMarkup, 'channel_currency_exchange'),
                    'extra_param' => !empty($extraParam) ? json_encode($extraParam) : null,
                    'coupon_code' => fillOnUndefined($couponCodeData,'code')
                    //Burada sor sat için bişey gelebilir
                ];

                $bookingCreate = $this->bookingService->create($bookingCreateParam);

                //$bookingCreate['status'] = 'success';
                //$bookingCreate['data']['id'] = 16;

                if ($bookingCreate['status'] != 'success') {
                    throw new ApiErrorException(lang('Booking could not be made'));
                }

                //INSERT CONTACT DATA
                $bookingContactCreateParam = [
                    'booking_id' => $bookingCreate['data']['id'],
                    'name' => $params['contactInfo']['name'],
                    'surname' => $params['contactInfo']['surName'],
                    'phone_code' => $params['contactInfo']['phoneCode'],
                    'phone_number' => $params['contactInfo']['phoneNumber'],
                    'email' => $params['contactInfo']['email'],
                    'note' => fillOnUndefined($params['contactInfo'], 'note'),
                    'language_code' => fillOnUndefined($params['contactInfo'], 'language_code', 'en'),
                    'invoice_request' => fillOnUndefined($params['contactInfo'], 'invoice_request') ? 1 : null,
                    'status' => 1
                ];

                foreach ($bannedCheckParameter as $checkParameter) {
                    if (isset($bookingContactCreateParam[$checkParameter])) {
                        foreach ($bannedCharacters as $bannedCharacter) {
                            if (str_contains($bookingContactCreateParam[$checkParameter], $bannedCharacter)) {
                                throw new ApiErrorException(lang('Booking could not be made! - Contact'));
                            }
                        }
                    }
                }

                $bookingContactCreate = $this->bookingContactService->create($bookingContactCreateParam);
                //$bookingContactCreate['status'] = 'success';

                if ($bookingContactCreate['status'] != 'success') {
                    throw new ApiErrorException(lang('Booking Contact could not be made'));
                }

                //INSERT ADDON DATA
                foreach ($bookingAddon as $propertyAddonId => $propertyAddon) {

                    $bookingAddonCreateParam = [
                        'booking_id' => $bookingCreate['data']['id'],
                        'property_channel_addon_id' => $propertyAddon['property_channel_addon_id'],
                        'count' => $propertyAddon['count'],
                        'amount' => $propertyAddon['amount'],
                        'total' => $propertyAddon['total'],
                        'currency_code' => $propertyAddon['currency_code'],
                        'attribute' => !empty($propertyAddon['attribute']) ? json_encode($propertyAddon['attribute']) : null,
                    ];

                    $bookingAddonCreate = $this->bookingAddonService->create($bookingAddonCreateParam);
                    //$bookingRoomCreate['status'] = 'success';
                    //$bookingRoomCreate['data']['id'] = 1;

                    if ($bookingAddonCreate['status'] != 'success') {
                        throw new ApiErrorException(lang('Booking Addon could not be made'));
                    }

                }


                //INSERT ROOM DATA
                foreach ($roomInfo as $roomOrder => $room) {

                    $rateKeyCodeDecoded = $this->rateKeyCodeDecode($searchCacheRoomPrices[$room['rateKey']]['rateKeyCode']);

                    $roomPrice = $searchCacheRoomPrices[$room['rateKey']]['total'];
                    $roomPriceBeforeDiscount = $searchCacheRoomPrices[$room['rateKey']]['total'];
                    $roomDiscountAmount = 0;
                    if (!empty($couponCodeData)) {
                        if ($couponCodeData['type'] == 'PER') {
                            $roomDiscountAmount = ($roomPriceBeforeDiscount * $couponCodeData['value'] / 100);
                        } elseif ($couponCodeData['type'] == 'FIX') {
                            $roomDiscountAmount = $couponCodeData['value'];
                        }
                        $roomPrice = $roomPriceBeforeDiscount - $roomDiscountAmount;
                    }

                    $bookingRoomCreateParam = [
                        'booking_id' => $bookingCreate['data']['id'],
                        'room_order_number' => ($roomOrder + 1),
                        'occupancy_code' => $room['occupancyCode'],
                        'checkin_date' => $searchCacheData['requestParams']['date']['checkIn'],
                        'checkout_date' => $searchCacheData['requestParams']['date']['checkOut'],
                        'rate_key' => $room['rateKey'],
                        'rate_key_code' => $searchCacheRoomPrices[$room['rateKey']]['rateKeyCode'],
                        'availability_id' => $rateKeyCodeDecoded['availabilityTypeId'],
                        'availability_code' => $searchCacheRoomPrices[$room['rateKey']]['availability']['code'],
                        'room_id' => $rateKeyCodeDecoded['roomId'],
                        'room_name' => $searchCacheRoomPrices[$room['rateKey']]['room']['name'],
                        'room_rate_mapping_id' => $rateKeyCodeDecoded['rateMappingId'],
                        'room_rate_name' => $searchCacheRoomPrices[$room['rateKey']]['rate']['name'],
                        'cancellation_policy' => json_encode($searchCacheRoomPrices[$room['rateKey']]['cancellationPolicy']),
                        'payment_type_code' => $searchCacheRoomPrices[$room['rateKey']]['bookingPaymentType']['code'],
                        'daily_amount' => json_encode($searchCacheRoomPrices[$room['rateKey']]['policyPriceDaily']),
                        'rate_detail' => json_encode($searchCacheRoomPrices[$room['rateKey']]),
                        'amount' => $roomPriceBeforeDiscount,
                        'discount_amount' => $roomDiscountAmount,
                        'total' => $roomPrice,
                        'currency_code' => $searchCacheRoomPrices[$room['rateKey']]['currency'],
                        'status' => 1
                    ];

                    $bookingRoomCreate = $this->bookingRoomService->create($bookingRoomCreateParam);
                    //$bookingRoomCreate['status'] = 'success';
                    //$bookingRoomCreate['data']['id'] = 1;

                    if ($bookingRoomCreate['status'] != 'success') {
                        throw new ApiErrorException(lang('Booking Room could not be made'));
                    }

                    foreach ($room['paxes'] as $roomPax) {

                        $bookingRoomPaxParam = [
                            'booking_id' => $bookingCreate['data']['id'],
                            'booking_room_id' => $bookingRoomCreate['data']['id'],
                            'type' => fillOnUndefined($roomPax, 'type'),
                            'name' => fillOnUndefined($roomPax, 'name'),
                            'surname' => fillOnUndefined($roomPax, 'surName'),
                            'gender' => fillOnUndefined($roomPax, 'gender'),
                            'citizen' => fillOnUndefined($roomPax, 'citizen'),
                            'birth_date' => fillOnUndefined($roomPax, 'birthDate'),
                        ];

                        foreach ($bannedCheckParameter as $checkParameter) {
                            if (isset($bookingRoomPaxParam[$checkParameter])) {
                                foreach ($bannedCharacters as $bannedCharacter) {
                                    if (str_contains($bookingRoomPaxParam[$checkParameter], $bannedCharacter)) {
                                        throw new ApiErrorException(lang('Booking could not be made! - Pax'));
                                    }
                                }
                            }
                        }

                        $bookingRoomPaxCreate = $this->bookingRoomPaxService->create($bookingRoomPaxParam);
                        //$bookingRoomCreate['status'] = 'success';
                        //$bookingRoomCreate['data']['id'] = 1;

                        if ($bookingRoomPaxCreate['status'] != 'success') {
                            throw new ApiErrorException(lang('Booking Room Pax could not be made'));
                        }


                    }

                }


            } else {

                $bookingDetailParam = [
                    'criteria' => [
                        ['field' => 'booking_code', 'condition' => '=', 'value' => $params['bookingCode']]
                    ],
                    'with' => ['bookingPayment', 'bookingRoom', 'bookingAddon'],
                    'firstRow' => true
                ];

                $bookingCreate = $this->bookingService->select($bookingDetailParam);

                if ($bookingCreate['status'] != 'success' || empty($bookingCreate['data'])) {
                    throw new ApiErrorException(lang('Booking not found'));
                }

                $bookingDetail = $bookingCreate['data'];


                //DELETE OLD ADDON DATA
                if (!empty(pickItemFromArray('id', $bookingDetail['booking_addon']))) {
                    $this->bookingAddonService->delete(pickItemFromArray('id', $bookingDetail['booking_addon']));
                }


                //INSERT ADDON DATA
                foreach ($bookingAddon as $propertyAddonId => $propertyAddon) {

                    $bookingAddonCreateParam = [
                        'booking_id' => $bookingDetail['id'],
                        'property_channel_addon_id' => $propertyAddon['property_channel_addon_id'],
                        'count' => $propertyAddon['count'],
                        'amount' => $propertyAddon['amount'],
                        'total' => $propertyAddon['total'],
                        'currency_code' => $propertyAddon['currency_code'],
                        'attribute' => !empty($propertyAddon['attribute']) ? json_encode($propertyAddon['attribute']) : null,
                    ];

                    $bookingAddonCreate = $this->bookingAddonService->create($bookingAddonCreateParam);

                    if ($bookingAddonCreate['status'] != 'success') {
                        throw new ApiErrorException(lang('Booking Addon could not be made'));
                    }

                }

                $bookingCode = $bookingCreate['data']['booking_code'];
                $bookingStatus = in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) ? 1 : 2;

                $totalRoomsPriceBeforeDiscount = $totalRoomsPrice;
                $bookingDiscountAmount = 0;
                if (!empty($couponCodeData)) {
                    if ($couponCodeData['type'] == 'PER') {
                        $bookingDiscountAmount = ($totalRoomsPriceBeforeDiscount * $couponCodeData['value'] / 100);
                    } elseif ($couponCodeData['type'] == 'FIX') {
                        $bookingDiscountAmount = $couponCodeData['value'];
                    }
                    $totalRoomsPrice = $totalRoomsPrice - $bookingDiscountAmount;
                }

                $bookingTotalAmount = $totalRoomsPrice + $totalAddonPrice;


                $bookingDetailUpdate = [
                    'room_amount' => $totalRoomsPriceBeforeDiscount,
                    'addon_amount' => $totalAddonPrice,
                    'total' => $bookingTotalAmount,
                ];

                $this->bookingService->update($bookingDetail['id'], $bookingDetailUpdate);


            }

            //Eğer kredi kartı ise
            $initializePayment = [];
            if (!in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN'])) {

                $isDirectPayment = true;

                //TODO: validator
                $params['paymentInfo']['creditCard']['cardExpireMonth'] = strlen($params['paymentInfo']['creditCard']['cardExpireMonth']) == 2 ? $params['paymentInfo']['creditCard']['cardExpireMonth'] : str_pad($params['paymentInfo']['creditCard']['cardExpireMonth'], 2, 0, STR_PAD_LEFT);

                $initializePaymentParam = [
                    'propertyId' => $propertyId,
                    'orderId' => $bookingCode,
                    'installment' => isset($params['paymentInfo']['creditCard']['installment']) ? $params['paymentInfo']['creditCard']['installment'] : 1,
                    'amount' => (double)$bookingTotalAmount,
                    'currency' => $currencyCode,
                    'type' => 'BKG',
                    'responseUrl' => $params['paymentInfo']['responseUrl'],
                    'creditCard' => [
                        'name' => $params['paymentInfo']['creditCard']['cardHolder'],
                        'number' => $params['paymentInfo']['creditCard']['cardNumber'],
                        'month' => $params['paymentInfo']['creditCard']['cardExpireMonth'],
                        'year' => $params['paymentInfo']['creditCard']['cardExpireYear'],
                        'cvv' => $params['paymentInfo']['creditCard']['cardCvv'],
                    ]
                ];


                $initializePayment = $this->propertyPaymentService->initializePayment($initializePaymentParam);

                if (!$initializePayment['status']) {
                    Log::error($initializePayment);
                    throw new ApiErrorException(lang('Initialize Payment could not be made'));
                }

                if (isset($initializePayment['data']['redirectUrl'])) {
                    $isDirectPayment = false;
                }
            }

            //Eğer direk ödeme ile para çekilip işlem yapılmış ise
            if (isset($isDirectPayment) && $isDirectPayment) {
                $bookingStatus = 1;
                $this->bookingService->update($bookingCreate['data']['id'], ['status' => 1]);
            }

            $bookingPaymentCreateParam = [
                'booking_id' => $bookingCreate['data']['id'],
                'payment_code' => isset($initializePayment['data']['paymentCode']) ? $initializePayment['data']['paymentCode'] : null,
                'payment_type_code' => $params['paymentInfo']['paymentCode'],
                'total' => in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && $isDirectPayment) ? $bookingTotalAmount : null,
                'currency_code' => in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && $isDirectPayment) ? $currencyCode : null,
                'status' => (isset($isDirectPayment) && $isDirectPayment) ? 1 : 2
            ];

            $bookingPaymentCreate = $this->bookingPaymentService->create($bookingPaymentCreateParam);
            //$bookingContactCreate['status'] = 'success';

            if ($bookingPaymentCreate['status'] != 'success') {
                throw new ApiErrorException(lang('Booking Payment could not be made'));
            }

            //Buradan eksiltme transaction ları başlatılacak ki ok ise düşsün
            $roomAvailabilityStatus = true;
            foreach ($roomAvailabilityKeyGroup as $roomAvailabilityKey => $roomAvailability) {

                if ($roomAvailabilityKey == 1) {

                    foreach ($roomAvailability as $roomKey => $room) {

                        foreach ($dateByDay as $day) {

                            $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability
                                ->where('date', $day)
                                ->where('property_room_id', $room['detail']['roomId'])
                                ->where('availability_type_id', $room['detail']['availabilityTypeId'])
                                ->where('availability', '>=', $room['count'])
                                ->where('status', 1)
                                ->first();

                            if (is_null($roomAndRoomRateAvailability)) {
                                $roomAvailabilityStatus = false;
                                break 3;
                            }

                            //Kontenjan Azaltma
                            //Otelde öde ise düşürülecek kontenjan
                            if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || $isDirectPayment) {
                                $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - $room['count'];
                                $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]);

                                //Connected Room Case
                                if ($roomAndRoomRateAvailability['room_detail']['is_connected_room'] && $roomAndRoomRateAvailability['room_detail']['is_connected_room_availability']) {

                                    foreach ($roomAndRoomRateAvailability['room_detail']['property_room_connected'] as $connectedRoom) {

                                        $roomAndRoomRateAvailabilityConnected = $propertyRoomAndRoomRateAvailability
                                            ->where('date', $day)
                                            ->where('property_room_id', $connectedRoom['connected_room_id'])
                                            ->where('availability_type_id', $room['detail']['availabilityTypeId'])
                                            ->where('availability', '>=', $room['count'])
                                            ->where('status', 1)
                                            ->first();

                                        if (is_null($roomAndRoomRateAvailabilityConnected)) {
                                            continue;
                                        }

                                        $roomNewAvailabilityConnected = $roomAndRoomRateAvailabilityConnected['availability'] - $room['count'];
                                        $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailabilityConnected['id'], ['availability' => $roomNewAvailabilityConnected]);
                                    }
                                }
                                //Connected Room Case

                            }
                        }

                    }

                    //Connected Room Case
                    $roomAvailabilityUpdateForConnectedRoomParams = [
                        'property_id' => $propertyId,
                        'channel_id' => $this->channelId,
                        'availability_type_id' => [$roomAvailabilityKey],
                        'startDate' => reset($dateByDay),
                        'endDate' => last($dateByDay),
                        'user_id' => fillOnUndefined($params, "user_id", 0)
                    ];

                    $this->propertyRoomAvailabilityService->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams);
                    //Connected Room Case

                } else {

                    foreach ($roomAvailability as $roomKey => $roomRateMapping) {

                        foreach ($roomRateMapping as $roomRateMappingKey => $roomRate) {

                            foreach ($dateByDay as $day) {

                                $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability
                                    ->where('date', $day)
                                    ->where('property_room_id', $roomRate['detail']['roomId'])
                                    ->where('availability_type_id', $roomRate['detail']['availabilityTypeId'])
                                    ->where('room_rate_mapping_id', $roomRate['detail']['rateMappingId'])
                                    ->where('channel_id', $roomRate['detail']['channelId'])
                                    ->where('availability', '>=', $roomRate['count'])
                                    ->where('status', 1)
                                    ->first();

                                if (is_null($roomAndRoomRateAvailability)) {
                                    $roomAvailabilityStatus = false;
                                    break 4;
                                }

                                //Kontenjan Azaltma
                                //Otelde öde ise düşürülecek kontenjan
                                if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || $isDirectPayment) {
                                    //TODO: Garantili kontenjan ve limitlide havuzdan da azaltılmalı
                                    $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - $roomRate['count'];
                                    $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]);
                                }

                            }

                        }

                    }

                }

            }

            if (!$roomAvailabilityStatus) {
                throw new ApiErrorException(lang('One or more of the requested rooms are not available'));
            }
            //BOOKING FINISHED


            $responseData = [
                'bookingCode' => $bookingCode,
                'total' => $bookingTotalAmount,
                'currency' => $currencyCode,
                'bookingStatus' => $bookingStatus,
                'bookingStatusText' => $bookingStatus == 1 ? 'Booking' : 'PreBooking',

            ];

            //TODO: Eğer Booking yapılmış ise
            if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && isset($isDirectPayment) && $isDirectPayment)) {

                $mailParams = ['booking_id' => $bookingCreate['data']['id']];
                $this->newBookingMailService->process($mailParams);

                //PUSH NOTIFICATION
                $newBookingNotificationParam = ['booking_id' => $bookingCreate['data']['id']];
                $this->notificationService->sendNewBookingNotification($newBookingNotificationParam);
                //PUSH NOTIFICATION


                //PUSH CHANNEL MANAGER QUEUE
                //if ($bookingCreate['data']['channel_id'] == 1) {

                $channelManagerPropertyMappingCriteria = [
                    'criteria' =>
                        [
                            ['field' => 'property_id', 'condition' => '=', 'value' => $bookingCreate['data']['property_id']],
                            //['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null],
                            ['field' => 'status', 'condition' => '=', 'value' => 1],
                        ]
                ];

                $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria);

                if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) {

                    foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) {

                        //Trivago
                        if ($channelPropertyData['channel_manager_id'] == 11 && !isset($params['extraParam']['trv_reference'])) {
                            continue;
                        }

                        //Hyperguest
                        if ($channelPropertyData['channel_manager_id'] != 10 && !is_null($channelPropertyData['channel_manager_property_id'])) {
                            continue;
                        }

                        if(in_array($channelPropertyData['channel_manager_id'],[13])) {
                            continue;
                        }

                        $channelManagerBookingCreateParam = [
                            'property_id' => $bookingCreate['data']['property_id'],
                            'booking_id' => $bookingCreate['data']['id'],
                            'channel_manager_id' => $channelPropertyData['channel_manager_id'],
                            'type' => 'Booking',
                        ];

                        $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam);

                    }

                }

                //}
                //PUSH CHANNEL MANAGER QUEUE


            }

            //Eğer kredi kartı ise ve ödeme başlamış ise
            if (!empty($initializePayment)) {
                $responseData['paymentCode'] = $initializePayment['data']['paymentCode'];
                if (isset($initializePayment['data']['redirectUrl'])) {
                    $responseData['redirectUrl'] = $initializePayment['data']['redirectUrl'];
                }
            }

            //Pay at Hotel - Info Store
            if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) && isset($params['paymentInfo']['creditCard']) && !empty($params['paymentInfo']['creditCard']['cardNumber'])) {

                $creditCard = $params['paymentInfo']['creditCard'];

                $cardNumber = $creditCard['cardNumber'];
                $cardLengthSize = ceil(strlen($cardNumber) / 4);

                $cardNumberParse = [];
                for ($i = 0; $i < $cardLengthSize; $i++) {
                    $cardNumberParse['cc'][] = Crypt::encrypt(mb_substr($cardNumber, $i * 4, 4));
                }

                $cardNumberParse['cm'] = Crypt::encrypt($creditCard['cardExpireMonth']);
                $cardNumberParse['cy'] = Crypt::encrypt(mb_substr($creditCard['cardExpireYear'], -2, 2));
                $cardNumberParse['cv'] = Crypt::encrypt($creditCard['cardCvv']);
                $cardNumberParse['ch'] = Crypt::encrypt($creditCard['cardHolder']);


                foreach ($cardNumberParse as $type => $value) {

                    $bookingPaymentDataCreateParam = [];

                    if ($type == 'cc') {
                        foreach ($value as $cardValue) {
                            $bookingPaymentDataCreateParam = [
                                'booking_id' => $bookingCreate['data']['id'],
                                'type' => $type,
                                'data' => $cardValue,
                            ];
                            $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam);
                        }
                    } else {
                        $bookingPaymentDataCreateParam = [
                            'booking_id' => $bookingCreate['data']['id'],
                            'type' => $type,
                            'data' => $value,
                        ];

                        $this->bookingPaymentService->createPaymentData($bookingPaymentDataCreateParam);
                    }

                }

            }
            //Pay at Hotel - Info Store


            //Booking Engine Search Update
            $bookingEngineSearchParam = [
                'criteria' => [
                    ['field' => 'search_key', 'condition' => '=', 'value' => $params['searchKey']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => true
            ];

            $bookingEngineSearch = $this->propertyBookingEngineSearchService->select($bookingEngineSearchParam);

            if ($bookingEngineSearch['status'] == 'success' && !empty($bookingEngineSearch['data'])) {

                $bookingEngineSearchUpdateParam = [
                    'booking_code' => $bookingCode,
                    'amount' => $bookingTotalAmount,
                    'status' => 2,
                ];

                if (in_array($params['paymentInfo']['paymentCode'], ['HTL', 'CHN']) || ($bookingStatus == 1 && isset($isDirectPayment) && $isDirectPayment)) {
                    $bookingEngineSearchUpdateParam['status'] = 3;
                }

                $bookingEngineSearchUpdate = $this->propertyBookingEngineSearchService->update($bookingEngineSearch['data']['id'], $bookingEngineSearchUpdateParam);

            }

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $responseData];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }


        if ($response['status']) {
            DB::commit();
        } else {
            DB::rollBack();
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingConfirm(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $bookingCode = $params['bookingCode'];
            $paymentCode = $params['paymentCode'];

            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode]
                ],
                'with' => ['property', 'bookingPayment', 'bookingRoom'],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) {
                throw new ApiErrorException(lang('Booking not found'));
            }

            $bookingDetail = $bookingDetail['data'];

            $paymentDetailParam = [
                'criteria' => [
                    ['field' => 'code', 'condition' => '=', 'value' => $paymentCode]
                ],
                'firstRow' => true
            ];

            $paymentDetail = $this->propertyPaymentService->selectPaymentTransaction($paymentDetailParam);

            if ($paymentDetail['status'] != 'success' || empty($paymentDetail['data'])) {
                throw new ApiErrorException(lang('Payment not found'));
            }

            $paymentDetail = $paymentDetail['data'];

            if ($paymentDetail['status'] != 1) {
                throw new ApiErrorException('Payment not confirmed');
            }

            if ($bookingDetail['status'] == 1) {
                throw new ApiErrorException('Previously approved transaction');
            }


            DB::beginTransaction();


            //Booking Confirm
            $this->bookingService->update($bookingDetail['id'], ['status' => 1]);


            //Availability Change
            if ($bookingDetail['status'] == 2) {

                $propertyRoomAndRoomRateAvailabilityParam = [
                    'property_id' => $bookingDetail['property_id'],
                    'checkIn' => $bookingDetail['checkin_date'],
                    'checkOut' => $bookingDetail['checkout_date'],
                ];

                $getPropertyRoomAndRoomRateAvailability = $this->getPropertyRoomAndRoomRateAvailability($propertyRoomAndRoomRateAvailabilityParam);

                if ($getPropertyRoomAndRoomRateAvailability['status'] != 'success' || empty($getPropertyRoomAndRoomRateAvailability['data'])) {
                    throw new ApiErrorException(lang('Room or rooms not available.'));
                }

                $propertyRoomAndRoomRateAvailability = collect($getPropertyRoomAndRoomRateAvailability['data']);


                $roomAvailabilityStatus = true;
                foreach ($bookingDetail['booking_room'] as $room) {

                    $dateByDay = $this->getDateByDay(['checkIn' => $room['checkin_date'], 'checkOut' => $room['checkout_date']]);

                    foreach ($dateByDay as $day) {

                        $roomRateDetail = json_decode($room['rate_detail'], 1);
                        $rateKeyCodeDecoded = $this->rateKeyCodeDecode($roomRateDetail['rateKeyCode']);

                        if ($rateKeyCodeDecoded['availabilityTypeId'] == 1) {

                            $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability
                                ->where('date', $day)
                                ->where('property_room_id', $rateKeyCodeDecoded['roomId'])
                                ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId'])
                                ->where('availability', '>=', 1)
                                ->where('status', 1)
                                ->first();

                            if (is_null($roomAndRoomRateAvailability)) {
                                $roomAvailabilityStatus = false;
                                break 2;
                            }

                            //Kontenjan Azaltma
                            $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - 1;
                            $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]);

                            //Connected Room Case
                            if ($roomAndRoomRateAvailability['room_detail']['is_connected_room'] && $roomAndRoomRateAvailability['room_detail']['is_connected_room_availability']) {

                                foreach ($roomAndRoomRateAvailability['room_detail']['property_room_connected'] as $connectedRoom) {

                                    $roomAndRoomRateAvailabilityConnected = $propertyRoomAndRoomRateAvailability
                                        ->where('date', $day)
                                        ->where('property_room_id', $connectedRoom['connected_room_id'])
                                        ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId'])
                                        ->where('availability', '>=', 1)
                                        ->where('status', 1)
                                        ->first();

                                    if (is_null($roomAndRoomRateAvailabilityConnected)) {
                                        continue;
                                    }

                                    $roomNewAvailabilityConnected = $roomAndRoomRateAvailabilityConnected['availability'] - 1;
                                    $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailabilityConnected['id'], ['availability' => $roomNewAvailabilityConnected]);
                                }
                            }
                            //Connected Room Case

                        } else {

                            $roomAndRoomRateAvailability = $propertyRoomAndRoomRateAvailability
                                ->where('date', $day)
                                ->where('property_room_id', $rateKeyCodeDecoded['roomId'])
                                ->where('availability_type_id', $rateKeyCodeDecoded['availabilityTypeId'])
                                ->where('room_rate_mapping_id', $rateKeyCodeDecoded['rateMappingId'])
                                ->where('channel_id', $rateKeyCodeDecoded['channelId'])
                                ->where('availability', '>=', 1)
                                ->where('status', 1)
                                ->first();


                            if (is_null($roomAndRoomRateAvailability)) {
                                $roomAvailabilityStatus = false;
                                break 2;
                            }

                            //TODO: Garantili kontenjan ve limitlide havuzdan da azaltılmalı
                            //Kontenjan Azaltma
                            $roomNewAvailability = $roomAndRoomRateAvailability['availability'] - 1;
                            $this->propertyRoomAvailabilityService->update($roomAndRoomRateAvailability['id'], ['availability' => $roomNewAvailability]);


                        }

                    }

                }

                //Connected Room Case
                $roomAvailabilityUpdateForConnectedRoomParams = [
                    'property_id' => $bookingDetail['property_id'],
                    'channel_id' => $bookingDetail['channel_id'],
                    'availability_type_id' => [1],
                    'startDate' => reset($dateByDay),
                    'endDate' => last($dateByDay),
                    'user_id' => fillOnUndefined($params, "user_id", 0)
                ];

                $this->propertyRoomAvailabilityService->roomAvailabilityUpdateForConnectedRooms($roomAvailabilityUpdateForConnectedRoomParams);
                //Connected Room Case

                if (!$roomAvailabilityStatus) {
                    throw new ApiErrorException(lang('One or more of the requested rooms are not available'));
                }

            }

            //Booking Payment Update
            $bookingPaymentUpdateParam = [
                'status' => 1,
                'total' => $paymentDetail['amount'],
                'currency_code' => $paymentDetail['currency'],
            ];

            $this->bookingPaymentService->update($bookingDetail['booking_payment']['id'], $bookingPaymentUpdateParam);


            $mailParams = ['booking_id' => $bookingDetail['id']];
            $this->newBookingMailService->process($mailParams);

            //PUSH NOTIFICATION
            $newBookingNotificationParam = ['booking_id' => $bookingDetail['id']];
            $this->notificationService->sendNewBookingNotification($newBookingNotificationParam);
            //PUSH NOTIFICATION

            //PUSH CHANNEL MANAGER QUEUE
            //if ($bookingDetail['channel_id'] == 1) {

            $channelManagerPropertyMappingCriteria = [
                'criteria' =>
                    [
                        ['field' => 'property_id', 'condition' => '=', 'value' => $bookingDetail['property_id']],
                        //['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => null],
                        ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ]
            ];

            $channelManagerPropertyMapping = $this->channelManagerPropertyMappingService->select($channelManagerPropertyMappingCriteria);

            if ($channelManagerPropertyMapping['status'] == 'success' && !empty($channelManagerPropertyMapping['data'])) {

                foreach ($channelManagerPropertyMapping['data'] as $channelPropertyData) {

                    //Trivago
                    if ($channelPropertyData['channel_manager_id'] == 11 && !isset($params['extraParam']['trv_reference'])) {
                        continue;
                    }

                    //Hyperguest
                    if ($channelPropertyData['channel_manager_id'] != 10 && !is_null($channelPropertyData['channel_manager_property_id'])) {
                        continue;
                    }

                    //Yandex
                    if(in_array($channelPropertyData['channel_manager_id'],[13])) {
                        continue;
                    }

                    $channelManagerBookingCreateParam = [
                        'property_id' => $bookingDetail['property_id'],
                        'booking_id' => $bookingDetail['id'],
                        'channel_manager_id' => $channelPropertyData['channel_manager_id'],
                        'type' => 'Booking',
                    ];

                    $channelManagerBookingCreate = $this->channelManagerBookingService->create($channelManagerBookingCreateParam);

                }

            }

            //}
            //PUSH CHANNEL MANAGER QUEUE


            //Booking Engine Search Update
            //if ($bookingDetail['channel_id'] == 1) {

            $bookingEngineSearchParam = [
                'criteria' => [
                    ['field' => 'search_key', 'condition' => '=', 'value' => $bookingDetail['search_key']],
                    ['field' => 'status', 'condition' => '=', 'value' => 2],
                ],
                'firstRow' => true
            ];

            $bookingEngineSearch = $this->propertyBookingEngineSearchService->select($bookingEngineSearchParam);

            if ($bookingEngineSearch['status'] == 'success' && !empty($bookingEngineSearch['data'])) {

                $bookingEngineSearchUpdateParam = [
                    'booking_code' => $bookingDetail['booking_code'],
                    'amount' => $bookingDetail['total'],
                    'currency_code' => $bookingDetail['currency_code'],
                    'status' => 3,
                ];

                $bookingEngineSearchUpdate = $this->propertyBookingEngineSearchService->update($bookingEngineSearch['data']['id'], $bookingEngineSearchUpdateParam);

            }
            //}

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        if ($response['status']) {
            DB::commit();
        } else {
            DB::rollBack();
        }


        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingDetail(Request $request, $bookingCode)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode],
                    //['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId],//TODO: Kontrol gerekli!
                ],
                'with' => [
                    'bookingPayment.paymentTypeCode', 'bookingRoom.roomPax.paxCountry', 'bookingRoom.roomRateMapping.propertyRoomRate', 'bookingRoom.roomRateMapping.propertyRoom', 'bookingContact',
                    'bookingProperty.propertyContact', 'bookingProperty.propertyBrand', 'bookingProperty.propertyAdditionalInfos.propertyAdditionalInfoKey',
                    'bookingAddon.propertyChannelAddon.propertyAddon.fact', 'bookingChannel','propertyChannelMapping.channelBookingPaymentType',
                    'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', 'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact',
                    'bookingRoom.propertyRoomBed', 'bookingRoom.smokingFact','propertyBookingEngineSearch'
                ],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) {
                throw new ApiErrorException(lang('Booking not found'));
            }

            $bookingDetail['data']['booking_property']['checkin_time'] = '14:00';
            $bookingDetail['data']['booking_property']['checkout_time'] = '12:00';

            if (!empty($bookingDetail['data']['booking_property']['property_additional_infos'])) {
                $propertyAdditionalInfos = collect($bookingDetail['data']['booking_property']['property_additional_infos']);

                $checkinTimeData = $propertyAdditionalInfos->where('property_additional_info_key.additional_info_key', 'checkin_time')->first();
                $bookingDetail['data']['booking_property']['checkin_time'] = isset($checkinTimeData['value']) ? $checkinTimeData['value'] : '14:00';

                $checkoutTimeData = $propertyAdditionalInfos->where('property_additional_info_key.additional_info_key', 'checkout_time')->first();
                $bookingDetail['data']['booking_property']['checkout_time'] = isset($checkinTimeData['value']) ? $checkoutTimeData['value'] : '12:00';
            }
            unset($bookingDetail['data']['booking_property']['property_additional_infos']);


            //BOOKING ADDON
            $bookingAddons = $bookingDetail['data']['booking_addon'];
            unset($bookingDetail['data']['booking_addon']);

            $bookingAddonList = [];
            foreach ($bookingAddons as $bookingAddon) {

                $bookingAddonAttributeList = [];
                $isHasAttribute = false;
                $attributeArray = [];
                if (!empty($bookingAddon['property_channel_addon']['property_addon']['attributeArray'])) {
                    $isHasAttribute = true;
                    $attributeArray = $bookingAddon['property_channel_addon']['property_addon']['attributeArray'];
                    $bookingAddonAttributes = json_decode($bookingAddon['attribute'], 1);
                    if (!is_null($bookingAddonAttributes)) {
                        foreach ($bookingAddonAttributes as $key => $bookingAddonAttribute) {
                            foreach ($bookingAddonAttribute as $bookingAddonAttributeKey => $bookingAddonAttributeValue) {

                                if (isset($bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey])) {
                                    $bookingAddonAttributeList[$key][$bookingAddonAttributeKey] = [
                                        'name' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['name'],
                                        'language_key' => $bookingAddon['property_channel_addon']['property_addon']['attributeArray'][$bookingAddonAttributeKey]['language_key'],
                                        'value' => $bookingAddonAttributeValue,
                                    ];
                                }

                            }
                        }
                    }
                }

                $bookingAddonList[] = [
                    'id' => $bookingAddon['id'],
                    'booking_id' => $bookingAddon['booking_id'],
                    'property_channel_addon_id' => $bookingAddon['property_channel_addon_id'],
                    'count' => $bookingAddon['count'],
                    'amount' => $bookingAddon['amount'],
                    'total' => $bookingAddon['total'],
                    'currency_code' => $bookingAddon['currency_code'],
                    'name' => $bookingAddon['property_channel_addon']['property_addon']['fact']['name'],
                    'title' => $bookingAddon['property_channel_addon']['title'],
                    'language_key' => $bookingAddon['property_channel_addon']['property_addon']['fact']['language_key'],
                    'icon' => $bookingAddon['property_channel_addon']['property_addon']['fact']['icon'],
                    'isHasAttribute' => $isHasAttribute,
                    'attributeArray' => $attributeArray,
                    'attribute' => $bookingAddonAttributeList
                ];

            }

            $bookingDetail['data']['booking_addon'] = $bookingAddonList;
            //BOOKING ADDON

            //DAILY AMOUNT BY ROOM
            $hasNonRefundableRoom = false;
            foreach ($bookingDetail['data']['booking_room'] as $roomKey => $roomDetail) {

                if (!empty($roomDetail['room_rate_mapping']['property_room'])) {
                    $bookingDetail['data']['booking_room'][$roomKey]['room_name'] = $roomDetail['room_rate_mapping']['property_room']['name'];
                }

                if (!empty($roomDetail['room_rate_mapping']['property_room_rate'])) {
                    $bookingDetail['data']['booking_room'][$roomKey]['room_rate_name'] = $roomDetail['room_rate_mapping']['property_room_rate']['name'];
                }

                $diffInDays = Carbon::parse($roomDetail['checkin_date'])->diffInDays(Carbon::parse($roomDetail['checkout_date']));
                $baseRateDaily = moneyDoubleFormatDecimal($roomDetail['total'] / $diffInDays);

                $roomDailyAmount = [];
                if (!empty($roomDetail['daily_amount'])) {
                    $roomDetailDailyAmount = json_decode($roomDetail['daily_amount'], 1);
                    if (!empty($roomDetailDailyAmount) && isset($roomDetailDailyAmount)) {
                        foreach ($roomDetailDailyAmount as $roomRateAmount) {
                            $roomDailyAmount[] = [
                                'date' => $roomRateAmount['date'],
                                'amount' => $roomRateAmount['amount'],
                                'currency_code' => $roomRateAmount['currency_code'],
                            ];
                        }
                    }
                }

                if (empty($roomDailyAmount)) {
                    $roomRateDetail = json_decode($roomDetail['rate_detail'], 1);
                    if (!empty($roomRateDetail) && isset($roomRateDetail['days'])) {
                        foreach ($roomRateDetail['days'] as $roomRateDay => $roomRateAmount) {
                            $roomDailyAmount[] = [
                                'date' => Carbon::parse($roomRateDay)->toDateString(),
                                'amount' => $roomRateAmount,
                                'currency_code' => $roomDetail['currency_code'],
                            ];
                        }
                    }
                }

                if (empty($roomDailyAmount)) {
                    $currentDate = $roomDetail['checkin_date'];
                    for ($i = 0; $i < $diffInDays; $i++) {
                        $roomDailyAmount[] = [
                            'date' => $currentDate,
                            'amount' => $baseRateDaily,
                            'currency_code' => $roomDetail['currency_code'],
                        ];
                        $currentDate = Carbon::parse($currentDate)->addDay()->toDateString();
                    }
                }

                $bookingDetail['data']['booking_room'][$roomKey]['daily_amount'] = $roomDailyAmount;

                $extraParam = null;
                if (!empty($roomDetail['extra_param'])) {
                    $extraParamDecode = json_decode($roomDetail['extra_param'], 1);

                    foreach ($extraParamDecode as $extraParamKey => $extraParamValue) {

                        $extraParamTitle = explode('_', $extraParamKey);
                        foreach ($extraParamTitle as $extraParamTitleKey => $extraParamTitleValue) {
                            $extraParamTitle[$extraParamTitleKey] = ucwords($extraParamTitleValue);
                        }
                        $extraParamTitle = implode(' ', $extraParamTitle);

                        $extraParam[$extraParamKey] = [
                            'title' => $extraParamTitle,
                            'value' => $extraParamValue,
                        ];
                    }

                }

                $bookingDetail['data']['booking_room'][$roomKey]['extra_param'] = $extraParam;
                $bookingDetail['data']['booking_room'][$roomKey]['occupancyFormatted'] = occupancyCodeFormatted($roomDetail['occupancy_code']);

                $cancellationPolicy = !empty($roomDetail['cancellation_policy']) ? json_decode($roomDetail['cancellation_policy'], 1) : null;

                if (is_array($cancellationPolicy) && isset($cancellationPolicy['isNonRefundable']) && $cancellationPolicy['isNonRefundable']) {
                    $hasNonRefundableRoom = true;
                }


                //property_room_bed_group
                //unset($bookingDetail['data']['booking_room'][$roomKey]['room_rate_mapping']['property_room']['property_room_bed_group']);
                $propertyRoomBedGroup = collect($roomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->groupBy('bed_group');
                $propertyRoomBedGroup = $propertyRoomBedGroup ? $propertyRoomBedGroup->toArray() : null;
                $bookingDetail['data']['booking_room'][$roomKey]['room_rate_mapping']['property_room']['property_room_bed_group'] = $propertyRoomBedGroup;


                $bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group'] = null;
                if(isset($propertyRoomBedGroup[$bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group_id']])) {
                    $bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group'] = $propertyRoomBedGroup[$bookingDetail['data']['booking_room'][$roomKey]['property_room_bed_group_id']];
                }


            }
            //DAILY AMOUNT BY ROOM

            $bookingChannel = $bookingDetail['data']['booking_channel'];
            unset($bookingDetail['data']['booking_channel']);
            $bookingDetail['data']['booking_channel'] = [
                'name' => $bookingChannel['name'],
                'logo' => $bookingChannel['logoUrl'],
                'channel_category_id' => $bookingChannel['channel_category_id'],
            ];

            $bookingContactExtraParam = null;
            if (!empty($bookingDetail['data']['booking_contact']['extra_param'])) {
                $bookingContactExtraParam = json_decode($bookingDetail['data']['booking_contact']['extra_param'], 1);
            }
            $bookingDetail['data']['booking_contact']['extra_param'] = $bookingContactExtraParam;


            $bookingDetail = $bookingDetail['data'];

            $bookingDetail['isOnlineCancellation'] = false;
            if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [2]) && !$hasNonRefundableRoom) {
                $bookingDetail['isOnlineCancellation'] = false;
            }

            if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [3]) && in_array($bookingDetail['booking_property']['country'], ['GE'])) {
                $bookingDetail['isOnlineCancellation'] = false;
            }

            $payAtHotelCreditCardCheck = collect($bookingDetail['property_channel_mapping']['channel_booking_payment_type'])->where('is_get_payment_data',1)->where('payment_type_id',2)->isNotEmpty();
            $bookingDetail['credit_card_required'] = $payAtHotelCreditCardCheck;
            unset($bookingDetail['property_channel_mapping']);

            //Wholesaler MARKUP CALCULATE
            $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $bookingDetail['property_id']);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }
            $getChannelPropertyDetail = $getChannelPropertyDetail['status'] ? $getChannelPropertyDetail['data'] : null;

            if ($getChannelPropertyDetail && in_array($getChannelPropertyDetail['channel']['channel_category_id'], [7])) {

                $channelCurrencyCode = fillOnUndefined($bookingDetail, 'channel_currency_code', $bookingDetail['currency_code']);
                $channelCurrencyExchange = fillOnUndefined($bookingDetail, 'channel_currency_exchange', 1);

                $channelMarkup = 1;
                if (!is_null($bookingDetail['channel_markup'])) {
                    $channelMarkup = (($bookingDetail['channel_markup'] + 100) / 100);
                }

                foreach ($bookingDetail['booking_room'] as $roomKey => $room) {

                    foreach ($room['daily_amount'] as $dailKey => $dailyAmount) {
                        $bookingDetail['booking_room'][$roomKey]['daily_amount'][$dailKey]['currency_code'] = $channelCurrencyCode;
                        $bookingDetail['booking_room'][$roomKey]['daily_amount'][$dailKey]['amount'] = moneyDoubleFormatDecimal($dailyAmount['amount'] * $channelMarkup * $channelCurrencyExchange);
                    }


                    $bookingDetail['booking_room'][$roomKey]['amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'][$roomKey]['daily_amount'])->sum('amount'));
                    $bookingDetail['booking_room'][$roomKey]['discount_amount'] = moneyDoubleFormatDecimal($room['discount_amount'] * $channelMarkup * $channelCurrencyExchange);
                    $bookingDetail['booking_room'][$roomKey]['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'][$roomKey]['daily_amount'])->sum('amount'));
                    $bookingDetail['booking_room'][$roomKey]['currency_code'] = $channelCurrencyCode;


                    unset($bookingDetail['booking_room'][$roomKey]['rate_detail']);

                }

                $bookingDetail['currency_code'] = $channelCurrencyCode;
                $bookingDetail['room_amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('amount'));
                $bookingDetail['addon_amount'] = moneyDoubleFormatDecimal($bookingDetail['addon_amount'] * $channelMarkup * $channelCurrencyExchange);
                $bookingDetail['discount_amount'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('discount_amount'));
                $bookingDetail['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('amount'));


                $bookingDetail['booking_payment']['currency_code'] = $channelCurrencyCode;
                $bookingDetail['booking_payment']['total'] = moneyDoubleFormatDecimal(collect($bookingDetail['booking_room'])->sum('total'));


            }
            //Wholesaler MARKUP CALCULATE

            unset($bookingDetail['channel_markup']);
            unset($bookingDetail['channel_currency_code']);
            unset($bookingDetail['channel_currency_exchange']);

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $bookingDetail];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingUpdate(Request $request, $bookingCode)
    {

        DB::beginTransaction();

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);


            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode],
                ],
                'with' => [
                    'bookingRoom.roomRateMapping.propertyRoom',
                    'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType',
                    'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact',
                    'bookingRoom.propertyRoomBed','bookingRoom.smokingFact'
                ],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) {
                throw new ApiErrorException(lang('Booking not found'));
            }

            $bookingDetail = $bookingDetail['data'];

            if(fillOnUndefined($params,'propertyId') != fillOnUndefined($bookingDetail,'property_id')) {
                throw new ApiErrorException('PropertyI ID is not matching.');
            }

            $bookingRoomUpdateParam = ['smoking_fact_id','property_room_bed_id','property_room_bed_group_id'];
            if(isset($params['booking_room']) && isset($params['booking_room']['id'])) {

                $bookingRoomIds = collect($bookingDetail['booking_room'])->pluck('id');
                $bookingRoomIds = $bookingRoomIds ? $bookingRoomIds->toArray() : null;

                if(empty($bookingRoomIds)) {
                    throw new ApiErrorException('Booking Room ID not found.');
                }

                if(!in_array($params['booking_room']['id'],$bookingRoomIds )) {
                    throw new ApiErrorException('Booking Room ID not matching.');
                }

                $bookingRoomDetail = collect($bookingDetail['booking_room'])->where('id',$params['booking_room']['id'])->first();

                foreach ($bookingRoomUpdateParam as $updateColumn) {

                    //smoking_fact_id
                    if($updateColumn == 'smoking_fact_id' && isset($params['booking_room']['smoking_fact_id'])) {
                        $smokingPreferenceIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['smoking_preference'])->pluck('fact_id')->toArray();
                        if(!in_array($params['booking_room']['smoking_fact_id'],$smokingPreferenceIds)) {
                            throw new ApiErrorException('smoking_fact_id: ID params not matching.');
                        }
                        $this->bookingRoomService->update($params['booking_room']['id'], ['smoking_fact_id' => $params['booking_room']['smoking_fact_id']]);
                    }

                    //property_room_bed_id
                    if($updateColumn == 'property_room_bed_id' && isset($params['booking_room']['property_room_bed_id'])) {
                        $propertyRoomBedGroupIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->pluck('id')->toArray();
                        if(!in_array($params['booking_room']['property_room_bed_id'],$propertyRoomBedGroupIds)) {
                            throw new ApiErrorException('property_room_bed_id: ID params not matching.');
                        }
                        $this->bookingRoomService->update($params['booking_room']['id'], ['property_room_bed_id' => $params['booking_room']['property_room_bed_id']]);
                    }


                    //property_room_bed_group_id
                    if($updateColumn == 'property_room_bed_group_id' && isset($params['booking_room']['property_room_bed_group_id'])) {
                        $propertyRoomBedGroupIds = collect($bookingRoomDetail['room_rate_mapping']['property_room']['property_room_bed_group'])->pluck('bed_group')->toArray();

                        if(!in_array($params['booking_room']['property_room_bed_group_id'],$propertyRoomBedGroupIds)) {
                            throw new ApiErrorException('property_room_bed_group_id: ID params not matching.');
                        }
                        $this->bookingRoomService->update($params['booking_room']['id'], ['property_room_bed_group_id' => $params['booking_room']['property_room_bed_group_id']]);
                    }



                }


            } else {
                throw new ApiErrorException('Property Room ID not found.');
            }


            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => null];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        if ($response['status']) {
            DB::commit();
        } else {
            DB::rollBack();
        }


        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }




    public function bookingPaymentInstallment(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];
        try {

            $paymentData = [];

            $paymentData['isExchangeAmount'] = false;


            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $propertyId = $this->bookingEnginePropertyId;
            $cardBinNumber = $params['cardBinNumber'];
            $amount = $params['amount'];
            $currency = $params['currency'];

            $paymentData['amount'] = $amount;
            $paymentData['currency'] = $currency;
            $paymentData['installment'] = [];

            $paymentType = $this->propertyPaymentService->getPaymentTypeMapping($propertyId, $currency, $cardBinNumber);

            if (is_null($paymentType['propertyPaymentMappingId'])) {
                throw new ApiErrorException(lang('Payment Type not found'));
            }

            if ($params['currency'] != $paymentType['propertyPaymentMappingCurrency']) {
                $exchangeRate = $this->propertyPaymentService->getExchangeRates();

                if (isset($exchangeRate[$params['currency'] . $paymentType['propertyPaymentMappingCurrency']])) {
                    $exchangeRateValue = $exchangeRate[$params['currency'] . $paymentType['propertyPaymentMappingCurrency']];
                } else {
                    throw new ApiErrorException(lang('Currency exchange value not found.'));
                }

                $paymentData['isExchangeAmount'] = true;

                $paymentData['amount'] = moneyDoubleFormatDecimal($amount * $exchangeRateValue);
                $paymentData['currency'] = $paymentType['propertyPaymentMappingCurrency'];

                $paymentData['base_amount'] = $amount;
                $paymentData['base_currency'] = $currency;
                $paymentData['exchangeRate'] = $exchangeRateValue;

            }

            if (!empty($paymentType['propertyPaymentMappingInstallment'])) {

                $amountBeforeInstallment = $amount;
                if ($paymentData['isExchangeAmount']) {
                    $amountBeforeInstallment = $paymentData['amount'];
                }

                foreach ($paymentType['propertyPaymentMappingInstallment'] as $installmentData) {
                    $paymentData['installment'][$installmentData['installment']] = [
                        'installment' => $installmentData['installment'],
                        'commission' => $installmentData['commission'],
                        'amount' => moneyDoubleFormatDecimal($amountBeforeInstallment + ($amountBeforeInstallment * $installmentData['commission'] / 100))
                    ];

                }

            }

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $paymentData];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        if ($response['status']) {
            DB::commit();
        } else {
            DB::rollBack();
        }


        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function couponCodeCheck(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $couponData = [];

            $channelId = $this->channelId;
            $propertyId = $this->bookingEnginePropertyId;

            $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }

            $getChannelPropertyDetail = $getChannelPropertyDetail['data'];

            $propertyChannelCouponCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']],
                    ['field' => 'code', 'condition' => '=', 'value' => $params['code']],
                    ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()],
                    ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => true
            ];

            $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria);

            if ($propertyChannelCoupon['status'] == 'success') {
                $propertyChannelCoupon = $propertyChannelCoupon['data'];
            }

            if (empty($propertyChannelCoupon)) {
                throw new ApiErrorException(lang('Code that has expired or is not available.'));
            }

            $codeChecked = fillOnUndefined($propertyChannelCoupon,'checked', 0); $codeChecked++;
            $this->propertyChannelCouponService->update($propertyChannelCoupon['id'], ['checked' => $codeChecked]);

            //Reservation Date Check
            if (!is_null($propertyChannelCoupon['reservation_start_date']) && !is_null($propertyChannelCoupon['reservation_end_date'])) {
                if (isset($params['checkIn']) && isset($params['checkOut'])) {
                    $reservationDateCheck = Carbon::parse($propertyChannelCoupon['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($params['checkIn']))
                        && Carbon::parse($propertyChannelCoupon['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($params['checkOut']));
                    if (!$reservationDateCheck) {
                        throw new ApiErrorException(lang('The code is not within the reservation period entered.'));
                    }
                }
            }


            $couponData = [
                'code' => $propertyChannelCoupon['code'],
                'type' => $propertyChannelCoupon['type'],
                'value' => $propertyChannelCoupon['value'],
            ];

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $couponData];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function couponCodeNotify(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $channelId = $this->channelId;
            $propertyId = $this->bookingEnginePropertyId;

            $getChannelPropertyDetail = $this->getChannelPropertyDetail($this->channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }

            $getChannelPropertyDetail = $getChannelPropertyDetail['data'];

            $propertyChannelCouponCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['property_id']],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $getChannelPropertyDetail['channel_id']],
                    //['field' => 'code', 'condition' => '=', 'value' => $params['code']],
                    ['field' => 'start_date', 'condition' => '<=', 'value' => Carbon::now()->toDateString()],
                    ['field' => 'end_date', 'condition' => '>=', 'value' => Carbon::now()->toDateString()],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'is_notify', 'condition' => '=', 'value' => 1],
                ],
                "orderBy" => [
                    ["field" => "id", "value" => "DESC"]
                ],
                'firstRow' => true
            ];
            //dd($propertyChannelCouponCriteria);

            $propertyChannelCoupon = $this->propertyChannelCouponService->select($propertyChannelCouponCriteria);

            if ($propertyChannelCoupon['status'] == 'success') {
                $propertyChannelCoupon = $propertyChannelCoupon['data'];
            }

            if (empty($propertyChannelCoupon)) {
                throw new ApiErrorException(lang('No active code to notify was found'));
            }

            //Reservation Date Check
            if (!is_null($propertyChannelCoupon['reservation_start_date']) && !is_null($propertyChannelCoupon['reservation_end_date'])) {
                if (isset($params['checkIn']) && isset($params['checkOut'])) {
                    $reservationDateCheck = Carbon::parse($propertyChannelCoupon['reservation_start_date'])->lessThanOrEqualTo(Carbon::parse($params['checkIn']))
                        && Carbon::parse($propertyChannelCoupon['reservation_end_date'])->greaterThanOrEqualTo(Carbon::parse($params['checkOut']));
                    if (!$reservationDateCheck) {
                        throw new ApiErrorException(lang('The code is not within the reservation period entered.'));
                    }
                }
            }

            $couponData = [
                'code' => $propertyChannelCoupon['code'],
                'type' => $propertyChannelCoupon['type'],
                'value' => $propertyChannelCoupon['value'],
            ];

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => $couponData];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingAddonAttribute(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $channelId = $this->channelId;
            $propertyId = $this->bookingEnginePropertyId;
            $bookingCode = $params['bookingCode'];
            $bookingAddonId = $params['bookingAddonId'];

            //TODO: Validator,

            $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }


            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId],
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode]
                ],
                'with' => ['bookingAddon.propertyChannelAddon.propertyAddon.fact'],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || empty($bookingDetail['data'])) {
                throw new ApiErrorException('Booking not found');
            }

            $bookingDetail = $bookingDetail['data'];

            if (empty($bookingDetail['booking_addon'])) {
                throw new ApiErrorException('Booking Addon not found');
            }

            $bookingAddonCollect = collect($bookingDetail['booking_addon']);

            $selectedAddon = $bookingAddonCollect->where('id', $bookingAddonId)->first();

            if (empty($selectedAddon)) {
                throw new ApiErrorException('Booking Selected Addon not found');
            }

            $attributeArray = $selectedAddon['property_channel_addon']['property_addon']['attributeArray'];


            $addonInfoAttribute = null;
            if (isset($params['attribute'])) {
                foreach ($params['attribute'] as $addonAttribute) {
                    if (count(array_intersect_key($addonAttribute, $attributeArray)) != count($addonAttribute)) {
                        throw new ApiErrorException('Additional product attribute values submitted do not match.');
                    }
                }
                $addonInfoAttribute = $params['attribute'];
            }


            if ($selectedAddon['count'] != count($addonInfoAttribute)) {
                throw new ApiErrorException('The number of additional items purchased and the number of additional information entered do not match');
            }

            $updateParam['attribute'] = null;
            if (!is_null($addonInfoAttribute)) {
                $updateParam['attribute'] = json_encode($addonInfoAttribute);
            }

            $this->bookingAddonService->update($bookingAddonId, $updateParam);

            $bookingPropertyAddonUpdateMail = [
                'propertyId' => $bookingDetail['property_id'],
                'bookingCode' => $bookingDetail['booking_code'],
                'addonLanguageKey' => $selectedAddon['property_channel_addon']['property_addon']['fact']['language_key']
            ];
            $this->mailer->onQueue('bookingPropertyAddonUpdateMail', new BookingPropertyAddonUpdateMail($bookingPropertyAddonUpdateMail));

            $response = ['status' => 1, 'statusCode' => 200, 'message' => null, 'data' => []];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingCancellation(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            $confirmCodeGenerate = null;
            $propertyBookingController = \Illuminate\Support\Facades\App::make("App\Http\Controllers\V1\PropertyBookingController");

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $channelId = $this->channelId;
            $propertyId = $this->bookingEnginePropertyId;
            $bookingCode = $params['bookingCode'];
            $confirmCode = fillOnUndefined($params, 'confirmCode');

            $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }

            $getChannelPropertyDetail = $getChannelPropertyDetail['data'];

            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId],
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode]
                ],
                'with' => ['bookingChannel.propertyChannelCategory', 'bookingProperty', 'bookingRoom'],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || ($bookingDetail['status'] == 'success' && empty($bookingDetail['data']))) {
                throw new ApiErrorException('Property and Booking Code are not matched.');
            }

            $bookingDetail = $bookingDetail['data'];

            if ($bookingDetail['status'] != 1) {
                throw new ApiErrorException('Reservation status not available.');
            }


            //Only GE Hotels
            if ($bookingDetail['status'] == 1 && in_array($bookingDetail['booking_channel']['channel_category_id'], [3]) && in_array($bookingDetail['booking_property']['country'], ['GE'])) {


                //Check Cancellation Policy
                $isBookingRefundable = false;
                foreach ($bookingDetail['booking_room'] as $bookingRoom) {
                    $cancellationPolicy = json_decode($bookingRoom['cancellation_policy'], 1);

                    if (!empty($cancellationPolicy)) {
                        if ($cancellationPolicy['isNonRefundable']) {
                            break;
                        }
                        if ($cancellationPolicy['isFreeCancellation']) {
                            if (isset($cancellationPolicy['beforeArrivalDay'])) {
                                if (Carbon::now()->startOfDay()->lessThan(Carbon::parse($bookingRoom['checkin_date'])->subDays($cancellationPolicy['beforeArrivalDay'])->toDateString())) {
                                    $isBookingRefundable = true;
                                }
                            } else {
                                $isBookingRefundable = true;
                            }
                        }
                    }
                }

                if ($isBookingRefundable) {

                    //Cancellation Process
                    $updateBookingParam = [
                        'locale' => $request->getLocale(),
                        'property_id' => $bookingDetail['property_id'],
                        'booking_id' => $bookingDetail['id'],
                        'total' => $bookingDetail['total'],
                        'currency_code' => $bookingDetail['currency_code'],
                        'status' => 0
                    ];

                    $request['requestParams'] = $updateBookingParam;

                    $updateBooking = $propertyBookingController->updateBooking($request);
                    $updateBooking = $updateBooking->getData();

                    if ($updateBooking->status != 200) {
                        throw new ApiErrorException($updateBooking->message);
                    }

                    $mailParams = ['booking_id' => $bookingDetail['id']];
                    $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams));


                    $response = [
                        'status' => 1,
                        'statusCode' => 200,
                        'data' => [
                            'isCancellationRequest' => true
                        ],
                        'message' => __('api-cancellation_confirm-message', [], $request->getLocale())
                    ];


                } else {

                    $mailParams = [
                        'bookingCode' => $bookingCode
                    ];

                    $this->mailer->send(new BookingCancellationRequestMail($mailParams));

                    $response = [
                        'status' => 1,
                        'statusCode' => 200,
                        'data' => [
                            'isCancellationRequest' => true
                        ],
                        'message' => __('api-cancellation_request-message', [], $request->getLocale())
                    ];

                }


                return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

            }

            //Just Offline Booking Engines
            if (!in_array($bookingDetail['booking_channel']['channel_category_id'], [2, 7])) {
                throw new ApiErrorException('Online reservation cancellation is not active for this channel.');
            }

            //TODO: Checkin tarihi geçmemiş olmalı kontrol eklenecek

            $cacheKey = md5('BookingCancellation-' . $bookingDetail['booking_code']);

            if (!empty($confirmCode) || in_array($bookingDetail['booking_channel']['channel_category_id'], [7])) {

                if (!in_array($bookingDetail['booking_channel']['channel_category_id'], [7])) {

                    if (!Cache::has($cacheKey)) {
                        throw new ApiErrorException('Verification code not found, please try again.');
                    }

                    if ($confirmCode != Cache::get($cacheKey)) {
                        throw new ApiErrorException('The entered code could not be verified, please enter the updated code sent to your e-mail address.');
                    }

                }

                //Cancellation Process
                $updateBookingParam = [
                    'locale' => $request->getLocale(),
                    'property_id' => $bookingDetail['property_id'],
                    'booking_id' => $bookingDetail['id'],
                    'total' => $bookingDetail['total'],
                    'currency_code' => $bookingDetail['currency_code'],
                    'status' => 0
                ];

                $request['requestParams'] = $updateBookingParam;

                $updateBooking = $propertyBookingController->updateBooking($request);
                $updateBooking = $updateBooking->getData();

                if ($updateBooking->status != 200) {
                    throw new ApiErrorException($updateBooking->message);
                }

                $mailParams = ['booking_id' => $bookingDetail['id']];
                $this->mailer->onQueue('cancelBookingMail', new CancelBookingMail($mailParams));

                Cache::forget($cacheKey);

                $response = [
                    'status' => 1,
                    'statusCode' => 200,
                    'data' => null,
                    'message' => null
                ];

            } else {

                $confirmCodeGenerate = rand(1000, 9999);
                Cache::put($cacheKey, $confirmCodeGenerate, 60 * 5);//600 10 dk

                //$confirmCode
                $mailParams = [
                    'bookingCode' => $bookingCode,
                    'confirmCode' => $confirmCodeGenerate
                ];

                $this->mailer->send(new BookingCancellationConfirmCodeMail($mailParams));

                $response = [
                    'status' => 1,
                    'statusCode' => 200,
                    'data' => null,
                    'message' => 'Please enter the cancellation verification code sent to your e-mail address.'
                ];

            }

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }

    public function bookingInvoice(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            if (is_null($this->request->getContent())) {
                throw new ApiErrorException(lang('Parameter Error.'));
            }

            $params = json_decode($this->request->getContent(), 1);

            $channelId = $this->channelId;
            $propertyId = $this->bookingEnginePropertyId;
            $bookingCode = $params['bookingCode'];


            $getChannelPropertyDetail = $this->getChannelPropertyDetail($channelId, $propertyId);
            if (!$getChannelPropertyDetail['status']) {
                throw new ApiErrorException(lang('Property and Channel are not matched.'));
            }

            $getChannelPropertyDetail = $getChannelPropertyDetail['data'];

            $bookingDetailParam = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $channelId],
                    ['field' => 'booking_code', 'condition' => '=', 'value' => $bookingCode]
                ],
                'with' => ['bookingChannel.propertyChannelCategory', 'bookingProperty', 'bookingRoom', 'bookingContact'],
                'firstRow' => true
            ];

            $bookingDetail = $this->bookingService->select($bookingDetailParam);

            if ($bookingDetail['status'] != 'success' || ($bookingDetail['status'] == 'success' && empty($bookingDetail['data']))) {
                throw new ApiErrorException('Property and Booking Code are not matched.');
            }

            $bookingDetail = $bookingDetail['data'];

            if ($bookingDetail['status'] != 1) {
                throw new ApiErrorException('Reservation status not available.');
            }

            if ($bookingDetail['booking_contact']['invoice_request'] != 1) {
                throw new ApiErrorException('Reservation invoice status not available.');
            }


            $invoiceParam = [
                'name_surname' => fillOnUndefined($params, 'name_surname'),
                'citizen_number' => fillOnUndefined($params, 'citizen_number'),
                'company' => fillOnUndefined($params, 'company'),
                'tax_number' => fillOnUndefined($params, 'tax_number'),
                'address' => fillOnUndefined($params, 'address'),
            ];

            $bookingInvoiceUpdate = $this->bookingContactService->update($bookingDetail['booking_contact']['id'], ['invoice' => json_encode($invoiceParam)]);

            if ($bookingInvoiceUpdate['status'] != 'success') {
                throw new ApiErrorException('Invoice update operation could not be performed.');
            }

            $bookingPropertyAddonUpdateMail = [
                'bookingCode' => $bookingDetail['booking_code']
            ];
            $this->mailer->onQueue('bookingInvoiceUpdateMail', new BookingInvoiceUpdateMail($bookingPropertyAddonUpdateMail));

            $response = [
                'status' => 1,
                'statusCode' => 200,
                'data' => null,
                'message' => null
            ];

        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }


    public function propertyList(Request $request)
    {

        $response = ['status' => false, 'message' => '', 'data' => null, 'statusCode' => 500];

        try {

            $ipAddress = $request->ip();
            $channelToken = $request->header('channelToken');

            if (!$channelToken) {
                return apiResponse(0, 'Token not provided.', null, 401);
            }

            $ipRestriction = [
                //City Travel
                '1500c5d5-b55e-4fa0-966b-698f4f168f48' => [
                    '127.0.0.1',
                    '185.137.215.118',
                    '176.236.59.130',
                    '176.236.59.131',
                    '176.236.59.132',
                    '176.236.59.133',
                    '176.236.59.134',
                    '176.236.59.135',
                    '176.236.59.136',
                    '176.236.59.137',
                    '176.236.59.138',
                    '176.236.59.139',
                    '54.72.215.85',
                    '54.77.48.206',
                    '34.252.23.103',
                    '52.19.162.59',
                    '54.72.80.155',
                    '52.210.166.9',
                    '54.72.220.60',
                    '52.17.235.55',
                    '176.236.59.130',
                    '54.72.215.85',
                    '54.77.48.206',
                    '52.213.250.14',
                    '34.252.23.103'
                ]
            ];

            if (isset($ipRestriction[$channelToken]) && !in_array($ipAddress, $ipRestriction[$channelToken])) {
                return apiResponse(0, 'Cannot be accessed via this IP address: ' . $ipAddress, null, 401);
            }

            $channelRequest = [
                'criteria' => [
                    ['field' => 'token', 'condition' => '=', 'value' => $channelToken],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => 1
            ];

            $channelCheck = $this->channelService->select($channelRequest);

            if ($channelCheck['status'] != 'success' || empty($channelCheck['data'])) {
                return apiResponse(0, 'Channel Token not found.', null, 401);
            }

            $channelCheck = $channelCheck['data'];


            $propertyBookingEngineParam = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $channelCheck['id']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'with' => ['channel', 'property.propertyBookingEngineToken', 'property.propertyType', 'property.propertyContact', 'property.propertyDestination'],
                "orderBy" => [
                    ["field" => "id", "value" => "DESC"]
                ]
            ];

            $propertyBookingEngine = $this->propertyBookingEngineService->select($propertyBookingEngineParam);
            $propertyBookingEngine = $propertyBookingEngine['status'] == 'success' ? $propertyBookingEngine['data'] : [];

            $propertyList = [];
            foreach ($propertyBookingEngine as $bookingEngine) {

                $propertyList[] = [
                    'id' => $bookingEngine['property']['id'],
                    'name' => $bookingEngine['property']['name'],
                    'type' => $bookingEngine['property']['property_type']['name'],
                    'country' => $bookingEngine['property']['country'],
                    'destination' => $bookingEngine['property']['property_destination']['name'],
                    'destination_id' => $bookingEngine['property']['destination_id'],
                    'token' => $bookingEngine['token'],
                    'mapping' => !empty($bookingEngine['property']['mapping']) ? json_decode($bookingEngine['property']['mapping'], 1) : null,
                    'contact' => [
                        'address' => $bookingEngine['property']['property_contact']['address'],
                        'phone' => $bookingEngine['property']['property_contact']['view_full_phone'],
                        'email' => $bookingEngine['property']['property_contact']['email'],
                        'latitude' => $bookingEngine['property']['property_contact']['latitude'],
                        'longitude' => $bookingEngine['property']['property_contact']['longitude'],
                    ],
                ];

            }

            $response = [
                'status' => 1,
                'statusCode' => 200,
                'data' => $propertyList,
                'message' => null
            ];


        } catch (ApiErrorException $e) {
            $response['message'] = implode(', ', $e->getMessageArr());
            $response['statusCode'] = 400;
        } catch (Exception $e) {
            $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
            Log::error($message);
            $response['message'] = $e->getMessage();
            $response['statusCode'] = 500;
        }

        return apiResponse($response['status'], $response['message'], $response['data'], $response['statusCode']);

    }


}
