<?php


namespace App\Core\Service\ChannelManager;


use App\Core\Mail\LogMail;
use App\Core\Service\ChannelManagerMappingService;
use App\Core\Service\ChannelManagerPropertyMappingService;
use App\Exceptions\ApiErrorException;

use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Mail\Mailer;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Exception;


class Channex
{

    private $channelManagerId;
    private $channelManagerName;
    private $mailer;
    private $userApiKey;
    private $userName;
    private $password;
    private $pciUrl;
    private $pciApiKey;
    private $pciSecureUrl;

    public function __construct(
        Client $restClient,
        Mailer $mailer,
        ChannelManagerPropertyMappingService $channelManagerPropertyMappingService,
        ChannelManagerMappingService $channelManagerMappingService
    )
    {
        $this->restClient = $restClient;
        $this->mailer = $mailer;

        //if (App::environment() == 'production') {
            $this->url = 'https://app.channex.io/api/v1/';
            $this->userApiKey = 'uuyx1aEdP2whpT58mKbyxynLtgo0XQZZTq277Ww2Z8okT4FUhspFvIwrHD54+T6+';

            $this->pciApiKey = '570751ea538845078a80be6ec0eee6d2';
            $this->pciSecureUrl = 'https://secure.channex.io/';
        /*} else {
            $this->url = 'https://staging.channex.io/api/v1/';
            $this->userApiKey = 'uYOZRhv3tVK+AYNcFNWGwFfwz7+iMtqJaWvsT9p6rYA3RZENe9bAri7eIl95SbV+';

            $this->pciApiKey = '076d9599ffe04159bd280659f13579e4';
            $this->pciSecureUrl = 'https://secure-staging.channex.io/';
        }*/

        $this->pciUrl = 'https://pci.channex.io/api/v1/';

        $this->channelManagerId = 2;
        $this->channelManagerName = 'Channex';
        $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService;
        $this->channelManagerMappingService = $channelManagerMappingService;

    }

    public function request($type, $method, $jsonPayload)
    {
        $response = ['status' => false, 'message' => ''];

        try {
            $this->restClient = new Client(['http_errors' => false]);

            if ($type == 'POST') {

                $parameter = [
                    'headers' => [
                        'Content-Type' => 'application/json',
                        'user-api-key' => $this->userApiKey
                    ]
                ];

                if (!empty($jsonPayload)) {
                    $parameter['body'] = $jsonPayload;
                }

                $res = $this->restClient->request('POST', $this->url . $method, $parameter);

                $getResponseBody = $res->getBody();
                $getResponse = $getResponseBody->getContents();
                $getResponse = json_decode($getResponse, 1);

            } else if ($type == 'GET') {

                $res = $this->restClient->request('GET', $this->url . $method,
                    [
                        'headers' => [
                            //'Content-Type' => 'application/x-www-form-urlencoded',
                            'user-api-key' => $this->userApiKey
                        ],
                        'query' => $jsonPayload
                    ]
                );

                $getResponseBody = $res->getBody();
                $getResponse = $getResponseBody->getContents();
                $getResponse = json_decode($getResponse, 1);


            }

            /*Log::debug('__REQ__');
            Log::debug($this->url);
            Log::debug($type);
            Log::debug($method);
            Log::debug($jsonPayload);
            Log::debug('__RES__');
            Log::debug($getResponse);
            Log::debug(PHP_EOL);*/

            if ($res->getStatusCode() != 200) {
                $errors = singleElementArray($getResponse['errors']);
                $firstError = reset($errors);

                Log::debug('__REQ__');
                Log::debug($this->url);
                Log::debug($type);
                Log::debug($method);
                Log::debug($jsonPayload);
                Log::debug('__RES__');
                Log::debug($getResponse);
                Log::debug(PHP_EOL);

                throw new ApiErrorException($firstError['code'] . ': ' . $firstError['title']);
            }

            $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, '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 requestPCI($type, $method, $jsonPayload)
    {
        $response = ['status' => false, 'message' => ''];

        try {
            $this->restClient = new Client(['http_errors' => false]);

            $parameter = [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'user-api-key' => $this->userApiKey
                ]
            ];

            if (!empty($jsonPayload)) {
                $parameter['body'] = $jsonPayload;
            }

            $res = $this->restClient->request($type, $this->pciUrl . $method, $parameter);

            $getResponseBody = $res->getBody();
            $getResponse = $getResponseBody->getContents();
            $getResponse = json_decode($getResponse, 1);

            /*Log::debug('__REQ__');
            Log::debug($this->url);
            Log::debug($type);
            Log::debug($method);
            Log::debug($jsonPayload);
            Log::debug($parameter);
            Log::debug('__RES__');
            Log::debug($getResponse);
            Log::debug(PHP_EOL);*/

            if (!in_array($res->getStatusCode(), [200, 201, 204])) {
                $errors = [];
                if (isset($getResponse['errors'])) {
                    $errors = singleElementArray($getResponse['errors']);
                } elseif (isset($getResponse['error'])) {
                    $errors[] = [
                        'code' => null,
                        'title' => $getResponse['error']
                    ];
                }

                $firstError = reset($errors);

                Log::debug('__REQ__');
                Log::debug($type);
                Log::debug($method);
                Log::debug($jsonPayload);
                Log::debug('__RES__');
                Log::debug($getResponse);
                Log::debug(PHP_EOL);

                throw new ApiErrorException(fillOnUndefined($firstError, 'code') . ': ' . fillOnUndefined($firstError, 'title'));
            }

            $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, '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 inventoryRoomRateUpdate($method, $jsonPayload)
    {
        $response = ['status' => false, 'message' => ''];

        try {


            $request = $this->request('POST', $method, $jsonPayload);

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

            $response = [
                'status' => true,
                'data' => [
                    'confirmId' => reset($request['data'])['id']
                ]
            ];

        } 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 getSessionToken($param)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $request = $this->requestPCI('POST', 'session_tokens?api_key=' . $this->pciApiKey, json_encode($param));

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

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


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

        return $response;
    }

    public function removeCreditCardToken($token)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $request = $this->requestPCI('DELETE', 'cards/' . $token . '?api_key=' . $this->pciApiKey, null);

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

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


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

        return $response;
    }

    public function getChannelDetails($channelId)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $request = $this->request('GET', 'channels/' . $channelId, []);

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

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


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

        return $response;
    }


    /** Pattern Functions */

    public function availabilityUpdateRequestMultiParam($propertyId, $params)
    {

        $idList = [];

        $requestParam['values'] = [];

        ksort($params);

        $dataByRoomDate = [];
        foreach ($params as $currentDate => $rooms) {
            foreach ($rooms as $roomId => $value) {
                $uniqueKey = $value['availability'];
                $dataByRoomDate[$roomId][$uniqueKey][$currentDate] = $value;
            }
        }


        $requestParam = [];


        $valueKey = 0;
        foreach ($dataByRoomDate as $roomId => $rooms) {

            foreach ($rooms as $uniqueKey => $uniqueValues) {

                $uniqueValueDate = array_keys($uniqueValues);

                $dateKey = 0;
                $dateGroup = [];
                for ($i = 0; $i < count($uniqueValueDate); $i++) {
                    $dateGroup[$dateKey][] = $uniqueValueDate[$i];
                    if ($i + 1 < count($uniqueValueDate)) {
                        if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) {
                            $dateKey++;
                        }
                    }
                }

                foreach ($uniqueValues as $uniqueValue) {
                    $idList = array_merge($idList, $uniqueValue['idList']);
                }

                $currentValue = reset($uniqueValues);


                foreach ($dateGroup as $dates) {

                    if (count($dates) > 1) {

                        $requestParam['values'][$valueKey] = [
                            'property_id' => $propertyId,
                            'room_type_id' => $roomId,
                            'date_from' => reset($dates),
                            'date_to' => last($dates),
                            'availability' => $currentValue['availability']
                        ];

                    } else {

                        $requestParam['values'][$valueKey] = [
                            'property_id' => $propertyId,
                            'room_type_id' => $roomId,
                            'date' => reset($dates),
                            'availability' => $currentValue['availability']
                        ];

                    }

                    $valueKey++;

                }


            }
        }

        //dd($requestParam);

        return [
            'idList' => $idList,
            'payload' => json_encode($requestParam)
        ];
    }

    public function roomAvailabilityPush($propertyId, $bulkQueueData)
    {
        $response = ['status' => false, 'message' => ''];


        $channelManagerPropertyMappingCriteria = [
            'criteria' =>
                [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId],
                    ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null],
                ],
            'with' => ['channelManagerRoomRate.propertyRoomRateMapping'],
            'firstRow' => true,
            'orderBy' => [
                ['field' => 'id', 'value' => 'ASC']
            ]
        ];


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


        $channelManagerPushedIds = [];
        $channelManagerNoneMappingIds = [];
        $channelManagerPushConfirmCode = [];

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

            $channelManagerPropertyMapping = $channelManagerPropertyMapping['data'];
            $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']);


            $roomAvailabilityQueueForUpdate = [];
            $roomAvailabilityQueueForUpdateIdList = [];
            $roomAvailabilityQueue = $bulkQueueData;


            try {

                foreach ($roomAvailabilityQueue as $roomAvailability) {


                    //Dates less than today cannot be updated.
                    if (Carbon::parse($roomAvailability['date'])->lessThan(Carbon::now()->toDateString())) {

                        //$logMessage
                        $mailParams = [
                            'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem',
                            'logMessage' => '<pre>' . print_r($roomAvailability, true) . '</pre>'
                        ];

                        //$this->mailer->onQueue('logMail', new LogMail($mailParams));
                        //$logMessage

                        $channelManagerPushedIds[] = $roomAvailability['id'];

                        continue;

                    }

                    $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping.room_id', $roomAvailability['property_room_id'])->first();

                    if (empty($channelManagerRoomRate)) {
                        //None Mapping!
                        //$channelManagerPushedIds[] = $roomAvailability['id'];
                        $channelManagerNoneMappingIds[] = $roomAvailability['id'];

                        continue;
                    }


                    $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']][] = $roomAvailability['id'];


                    $roomAvailabilityQueueForUpdate[$roomAvailability['date']]
                    [$channelManagerRoomRate['channel_manager_room_id']] = [
                        'id' => $roomAvailability['id'],
                        'idList' => $roomAvailabilityQueueForUpdateIdList[$roomAvailability['date']],
                        'date' => $roomAvailability['date'],
                        'availability' => $roomAvailability['stop_sell'] == 1 ? 0 : $roomAvailability['availability']
                    ];

                }

                $roomAvailabilityUpdateRequestMultiParam = $this->availabilityUpdateRequestMultiParam($channelManagerPropertyMapping['channel_manager_property_id'], $roomAvailabilityQueueForUpdate);
                $request = $this->inventoryRoomRateUpdate('availability', $roomAvailabilityUpdateRequestMultiParam['payload']);

                if ($request['status']) {

                    $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomAvailabilityUpdateRequestMultiParam['idList']);
                    $channelManagerPushConfirmCode[] = $request['data']['confirmId'];

                } else {

                    //$logMessage
                    $mailParams = [
                        'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateAvailabilityUpdate Error',
                        'logMessage' => '<pre>' . print_r(array_merge($request, $roomAvailabilityUpdateRequestMultiParam), true) . '</pre>'
                    ];

                    $this->mailer->onQueue('logMail', new LogMail($mailParams));
                    //$logMessage

                    throw new ApiErrorException($request['message']);
                }


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


        } else {
            $channelManagerNoneMappingIds = collect($bulkQueueData)->pluck('id')->toArray();
        }


        $channelManagerPushedIds = array_unique($channelManagerPushedIds);
        asort($channelManagerPushedIds);
        $channelManagerPushedIds = array_values($channelManagerPushedIds);

        $response = [
            'status' => true,
            'data' => [
                'confirmCode' => $channelManagerPushConfirmCode,
                'channelManagerPushedIds' => $channelManagerPushedIds,
                'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds
            ]
        ];

        return $response;
    }


    public function roomRateUpdateRequestMultiParam($propertyId, $params)
    {

        $idList = [];

        $requestParam['values'] = [];

        ksort($params);

        $dataByRoomRate = [];
        foreach ($params as $currentDate => $rooms) {
            foreach ($rooms as $roomId => $rates) {
                foreach ($rates as $rateId => $value) {
                    $uniqueKey = $value['amount'] . '|' . $value['stop_sell'] . '|' . $value['min_stay'];
                    $dataByRoomRate[$rateId][$uniqueKey][$currentDate] = $value;
                }
            }
        }

        //Log::debug(json_encode($dataByRoomRate));

        $requestParam = [];

        $valueKey = 0;
        foreach ($dataByRoomRate as $rateId => $rates) {

            foreach ($rates as $uniqueKey => $uniqueValues) {


                $uniqueValueDate = array_keys($uniqueValues);

                $dateKey = 0;
                $dateGroup = [];
                for ($i = 0; $i < count($uniqueValueDate); $i++) {
                    $dateGroup[$dateKey][] = $uniqueValueDate[$i];
                    if ($i + 1 < count($uniqueValueDate)) {
                        if (Carbon::parse($uniqueValueDate[$i])->diffInDays(Carbon::parse($uniqueValueDate[$i + 1])) > 1) {
                            $dateKey++;
                        }
                    }
                }

                foreach ($uniqueValues as $uniqueValue) {
                    $idList = array_merge($idList, $uniqueValue['idList']);
                }

                $currentValue = reset($uniqueValues);

                foreach ($dateGroup as $dates) {

                    if (count($dates) > 1) {

                        $requestParam['values'][$valueKey] = [
                            'property_id' => $propertyId,
                            'rate_plan_id' => $rateId,
                            'date_from' => reset($dates),
                            'date_to' => last($dates),
                            'rate' => moneyDoubleFormatDecimal($currentValue['amount'] * 100),
                            'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0),
                            'min_stay_through' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1
                        ];

                        /*if (isset($currentValue['min_stay']) && $currentValue['min_stay'] != 0) {
                            $requestParam['values'][$valueKey]['min_stay_through'] = fillOnUndefined($currentValue, 'min_stay', 1);
                        }*/


                    } else {

                        $requestParam['values'][$valueKey] = [
                            'property_id' => $propertyId,
                            'rate_plan_id' => $rateId,
                            'date' => reset($dates),
                            'rate' => moneyDoubleFormatDecimal($currentValue['amount'] * 100),
                            'stop_sell' => fillOnUndefined($currentValue, 'stop_sell', 0),
                            'min_stay_through' => fillOnUndefined($currentValue, 'min_stay', 1) != 0 ? $currentValue['min_stay'] : 1
                        ];

                        /*if (isset($currentValue['min_stay']) && $currentValue['min_stay'] != 0) {
                            $requestParam['values'][$valueKey]['min_stay_through'] = fillOnUndefined($currentValue, 'min_stay', 1);
                        }*/


                    }

                    if ($requestParam['values'][$valueKey]['rate'] == 0) {
                        unset($requestParam['values'][$valueKey]['rate']);
                        $requestParam['values'][$valueKey]['stop_sell'] = 1;
                    }

                    $valueKey++;

                }

            }
        }


        return [
            'idList' => $idList,
            'payload' => json_encode($requestParam)
        ];


    }

    public function roomRatePricePush($propertyId, $bulkQueueData)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $channelManagerPropertyMappingCriteria = [
                'criteria' =>
                    [
                        ['field' => 'status', 'condition' => '=', 'value' => 1],
                        ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                        ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId],
                        ['field' => 'channel_manager_property_id', 'condition' => '!=', 'value' => null],
                    ],
                'with' => ['channelManagerRoomRate.propertyRoomRateMapping'],
                'firstRow' => true,
                'orderBy' => [
                    ['field' => 'id', 'value' => 'ASC']
                ]
            ];


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

            $channelManagerPushedIds = [];
            $channelManagerNoneMappingIds = [];
            $channelManagerPushConfirmCode = null;

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

                $channelManagerPropertyMapping = $channelManagerPropertyMapping['data'];
                $channelManagerPropertyRoomRateMappingCollect = collect($channelManagerPropertyMapping['channel_manager_room_rate']);


                $roomRatePriceQueueForUpdate = [];
                $roomRatePriceQueueForUpdateIdList = [];
                $roomRatePriceQueue = $bulkQueueData;


                foreach ($roomRatePriceQueue as $roomRatePrice) {


                    //Dates less than today cannot be updated.
                    if (Carbon::parse($roomRatePrice['date'])->lessThan(Carbon::now()->toDateString())) {

                        //$logMessage
                        $mailParams = [
                            'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - Previous Date Problem',
                            'logMessage' => '<pre>' . print_r($roomRatePrice, true) . '</pre>'
                        ];

                        //$this->mailer->onQueue('logMail', new LogMail($mailParams));
                        //$logMessage

                        $channelManagerPushedIds[] = $roomRatePrice['id'];

                        continue;

                    }

                    $channelManagerRoomRate = $channelManagerPropertyRoomRateMappingCollect->where('property_room_rate_mapping_id', $roomRatePrice['room_rate_mapping_id'])->first();

                    if (empty($channelManagerRoomRate)) {
                        //None Mapping!
                        //$channelManagerPushedIds[] = $roomAvailability['id'];
                        $channelManagerNoneMappingIds[] = $roomRatePrice['id'];

                        //TODO: Burada oda eşleşmiyorsa süreç devam ediyor ama uyarı maili atılabilir.

                        continue;
                    }


                    $roomRatePriceQueueForUpdateIdList
                    [$channelManagerPropertyMapping['channel_manager_property_id']]
                    [$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']]
                    [$channelManagerRoomRate['channel_manager_room_rate_id']][] = $roomRatePrice['id'];


                    $roomRatePriceQueueForUpdate
                    [$channelManagerPropertyMapping['channel_manager_property_id']]
                    [$roomRatePrice['date']]
                    [$channelManagerRoomRate['channel_manager_room_id']]
                    [$channelManagerRoomRate['channel_manager_room_rate_id']] = [
                        'id' => $roomRatePrice['id'],
                        'idList' => $roomRatePriceQueueForUpdateIdList[$channelManagerPropertyMapping['channel_manager_property_id']][$roomRatePrice['date']][$channelManagerRoomRate['channel_manager_room_id']][$channelManagerRoomRate['channel_manager_room_rate_id']],
                        'date' => $roomRatePrice['date'],
                        'amount' => $roomRatePrice['amount'],
                        'currency' => $roomRatePrice['currency'],
                        'stop_sell' => $roomRatePrice['stop_sell'],
                        'min_stay' => $roomRatePrice['min_stay'],
                    ];

                }


                foreach ($roomRatePriceQueueForUpdate as $channelManagerPropertyId => $channelManagerProperty) {


                    if (empty($channelManagerProperty)) {
                        throw new ApiErrorException('$channelManagerProperty is Empty!');
                    }


                    ksort($channelManagerProperty);
                    $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty);

                    $request = $this->inventoryRoomRateUpdate('restrictions', $roomRateUpdateRequestMultiParam['payload']);

                    if ($request['status']) {

                        $channelManagerPushedIds = array_merge($channelManagerPushedIds, $roomRateUpdateRequestMultiParam['idList']);
                        $channelManagerPushConfirmCode = $request['data']['confirmId'];

                    } else {

                        //$logMessage
                        $mailParams = [
                            'title' => $this->channelManagerName . ' - RoomAvailabilityPushService Error - InventoryRoomRateUpdate Error',
                            'logMessage' => '<pre>' . print_r($request, true) . '</pre>'
                        ];

                        $this->mailer->onQueue('logMail', new LogMail($mailParams));
                        //$logMessage

                        throw new ApiErrorException($request['message']);
                    }

                }

            }

            $channelManagerPushedIds = array_unique($channelManagerPushedIds);
            asort($channelManagerPushedIds);
            $channelManagerPushedIds = array_values($channelManagerPushedIds);

            $response = [
                'status' => true,
                'data' => [
                    'confirmCode' => $channelManagerPushConfirmCode,
                    'channelManagerPushedIds' => $channelManagerPushedIds,
                    'channelManagerNoneMappingIds' => $channelManagerNoneMappingIds
                ]
            ];

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

        return $response;
    }


    public function reservationPullFormattedData($propertyId, $channelManagerPropertyId, $param)
    {

        $bookingParam = [];

        //TODO: DELETE
        /*$propertyId = 545;
        $paramX = $param;
        $param = [];
        $param['attributes'] = $paramX;*/

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

        //$roomTypes
        $roomTypesParam = [
            'filter[property_id]' => $channelManagerPropertyId
        ];

        $roomTypes = $this->request('GET', 'room_types', $roomTypesParam);
        $roomTypes = $roomTypes['status'] ? $roomTypes['data'] : [];
        $roomTypesCollect = collect($roomTypes);

        //$ratePlans
        $ratePlansParam = [
            'filter[property_id]' => $channelManagerPropertyId
        ];

        $ratePlans = $this->request('GET', 'rate_plans', $ratePlansParam);
        $ratePlans = $ratePlans['status'] ? $ratePlans['data'] : [];
        $ratePlansCollect = collect($ratePlans);

        if (strpos($param['attributes']['unique_id'], 'EXP') !== false) {
            $param['attributes']['ota_name'] = 'Expedia';
        }

        try {

            //Check Channel Manager Mapping
            $channelManagerMappingCriteria =
                [
                    'criteria' =>
                        [
                            ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId],
                            ['field' => 'status', 'condition' => '=', 'value' => 1],
                        ],
                    'with' => ['propertyChannelManager'],
                    'orderBy' => [
                        ['field' => 'id', 'value' => 'ASC']
                    ]
                ];

            $channelManagerMappingData = $this->channelManagerMappingService->select($channelManagerMappingCriteria);
            $channelManagerMappingCollect = collect($channelManagerMappingData['data']);
            $channelManagerMapping = $channelManagerMappingCollect->where('channel_manager_channel_id', $param['attributes']['ota_name'])->first();

            //Eğer kanala ait eşleşen bir kanal yok ise devam et
            if (empty($channelManagerMapping)) {
                //TODO: hata dönecek
                throw new ApiErrorException('Channel Mapping Not Found');
            }
            //Check Channel Manager Mapping


            //Check Channel Manager Property Mapping
            $channelManagerPropertyMappingCriteria = [
                'criteria' =>
                    [
                        ['field' => 'status', 'condition' => '=', 'value' => 1],
                        ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId],
                        ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ],
                'with' => ['channelManagerRoomRate.propertyRoomRateMapping'],
                'firstRow' => true
            ];

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

            //Eğer kanala ait bir otel yok ise devam et
            if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) {
                ///TODO: hata dönecek
                /// throw new ApiErrorException($request['message']);
            }

            $channelManagerRoomRateCollect = collect($channelManagerPropertyMapping['data']['channel_manager_room_rate']);


            $roomRequest = [];
            foreach ($param['attributes']['rooms'] as $room) {
                $roomRequest[] = [
                    'adults' => $room['occupancy']['adults'],
                    'children' => $room['occupancy']['children'],
                    'age' => !empty($room['occupancy']['ages']) ? $room['occupancy']['ages'] : []
                ];
            }

            $paymentTypeCode = 'HTL';
            $paymentSourceCode = null;
            if (isset($param['attributes']['payment_type']) && $param['attributes']['payment_type'] == 'credit_card') {
                $paymentTypeCode = 'CRD';
                if ($param['attributes']['payment_collect'] == 'property') {
                    $paymentSourceCode = 'GST';
                }
                if ($param['attributes']['payment_collect'] == 'ota') {
                    $paymentSourceCode = 'OTA';
                }
            }


            //Booking
            $bookingParam['booking'] = [
                'id' => null,
                'booking_code' => null,
                'property_id' => $propertyId,
                'channel_manager_id' => $this->channelManagerId,
                'channel_id' => $channelManagerMapping['property_channel_id'],
                'channel_booking_code' => fillOnUndefined($param['attributes'], 'ota_reservation_code'),
                'search_key' => $param['attributes']['booking_id'],
                'checkin_date' => $param['attributes']['arrival_date'],
                'checkout_date' => $param['attributes']['departure_date'],
                'rooms' => json_encode($roomRequest),
                'payment_type_code' => $paymentTypeCode,
                'room_amount' => $param['attributes']['amount'],
                'addon_amount' => 0,
                'discount_amount' => 0,
                'total' => $param['attributes']['amount'],
                'currency_code' => $param['attributes']['currency'],
                'channel_token' => null,
                'booking_engine_token' => null,
                'reservation_time' => Carbon::parse($param['attributes']['inserted_at'],'UTC')->timestamp,
                'status' => $param['attributes']['status'] == 'cancelled' ? 0 : 1
            ];

            $extraParamBookingContact = [];
            if (isset($param['attributes']['customer']['meta']['is_genius'])) {
                $extraParamBookingContact['is_genius'] = $param['attributes']['customer']['meta']['is_genius'];
            }

            $phoneNumber['code'] = null;
            $phoneNumber['number'] = null;
            if (isset($param['attributes']['customer']['phone'])) {

                $phoneNumberExplode = explode(' ', $param['attributes']['customer']['phone']);

                if (!empty($phoneNumberExplode) && count($phoneNumberExplode) > 1) {
                    $phoneNumber['code'] = reset($phoneNumberExplode);
                    unset($phoneNumberExplode[0]);
                    $phoneNumber['number'] = implode(' ', $phoneNumberExplode);
                }

            }


            //Booking Contact
            $bookingParam['contact'] = [
                'name' => $param['attributes']['customer']['name'],
                'surname' => !empty($param['attributes']['customer']['surname']) ? $param['attributes']['customer']['surname'] : null,
                'phone_code' => fillOnUndefined($phoneNumber, 'code'),
                'phone_number' => fillOnUndefined($phoneNumber, 'number'),
                'email' => fillOnUndefined($param['attributes']['customer'], 'mail'),
                'country_code' => fillOnUndefined($param['attributes']['customer'], 'country'),
                'note' => !empty($param['attributes']['notes']) ? $param['attributes']['notes'] : null,
                'language_code' => fillOnUndefined($param['attributes']['customer'], 'language', 'en'),
                'extra_param' => !empty($extraParamBookingContact) ? json_encode($extraParamBookingContact) : null,
                'status' => 1
            ];

            $bookingParam['contact']['language_code'] = mb_substr($bookingParam['contact']['language_code'], 0, 2);
            $bookingParam['contact']['country_code'] = mb_strtolower($bookingParam['contact']['country_code']);

            //Booking Room
            $param['attributes']['rooms'] = array_values($param['attributes']['rooms']);
            foreach ($param['attributes']['rooms'] as $roomOrder => $room) {


                //Check Room Rate Mapping
                $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['room_type_id'])->where('channel_manager_room_rate_id', $room['rate_plan_id'])->first();
                if (empty($channelManagerRoomRate)) {
                    $channelManagerRoomRate = $channelManagerRoomRateCollect->where('channel_manager_room_id', $room['room_type_id'])->first();
                }

                $roomTypesSelected = $roomTypesCollect->where('id', $room['room_type_id'])->first();
                $ratePlanSelected = $ratePlansCollect->where('id', $room['rate_plan_id'])->first();


                $dailyAmount = [];
                foreach ($room['days'] as $roomDay => $roomAmount) {
                    $dailyAmount[] = [
                        'date' => Carbon::parse($roomDay)->toDateString(),
                        'amount' => $roomAmount,
                        'currency_code' => $param['attributes']['currency']
                    ];
                }

                $occupancyCodeByRoom = str_repeat('A', $room['occupancy']['adults']);
                if ($room['occupancy']['children'] > 0 && !empty($room['occupancy']['ages'])) {
                    foreach ($room['occupancy']['ages'] as $age) {
                        $occupancyCodeByRoom .= 'C' . $age;
                    }
                }

                $extraParamRoom = [];
                if (isset($room['guests']) && !empty($room['guests'])) {
                    $guests = [];
                    foreach ($room['guests'] as $guest) {
                        $guests[] = ucwords($guest['name']) . ' ' . ucwords($guest['surname']);
                    }
                    $extraParamRoom['guests'] = implode(', ', $guests);
                }
                if (isset($room['meta']['meal_plan'])) {
                    $extraParamRoom['meal_plan'] = $room['meta']['meal_plan'];
                }
                if (isset($room['meta']['policies']) && !empty(isset($room['meta']['policies']))) {
                    $extraParamRoom['policies'] = $room['meta']['policies'];
                }
                if (isset($room['meta']['cancel_penalties']) && !empty($room['meta']['cancel_penalties'])) {
                    $extraParamRoom['cancel_penalties'] = $room['meta']['cancel_penalties'];
                }
                if (isset($room['meta']['bed_preferences'])) {
                    $extraParamRoom['bed_preferences'] = $room['meta']['bed_preferences'];
                }
                if (isset($room['meta']['free_text'])) {
                    $extraParamRoom['free_text'] = $room['meta']['free_text'];
                }
                if (isset($room['meta']['payment_instruction'])) {
                    $extraParamRoom['payment_instruction'] = $room['meta']['payment_instruction'];
                }
                if (isset($room['meta']['promotion_code'])) {
                    $extraParamRoom['promotion_code'] = $room['meta']['promotion_code'];
                }
                if (isset($room['meta']['smoking_preferences'])) {
                    $extraParamRoom['smoking_preferences'] = $room['meta']['smoking_preferences'];
                }

                $bookingParam['room'][] = [
                    'room_order_number' => ($roomOrder + 1),
                    'occupancy_code' => $occupancyCodeByRoom,
                    'checkin_date' => $room['checkin_date'],
                    'checkout_date' => $room['checkout_date'],
                    'rate_key' => null,
                    'rate_key_code' => null,
                    'availability_id' => 1,
                    'availability_code' => 'ROM',
                    'room_id' => $channelManagerRoomRate['property_room_rate_mapping']['room_id'],
                    'room_name' => isset($roomTypesSelected['attributes']['title']) ? $roomTypesSelected['attributes']['title'] : null,
                    'room_rate_mapping_id' => $channelManagerRoomRate['property_room_rate_mapping']['id'],
                    'room_rate_name' => isset($ratePlanSelected['attributes']['title']) ? $ratePlanSelected['attributes']['title'] : null,
                    'cancellation_policy' => null,
                    'payment_type_code' => $paymentTypeCode,
                    'daily_amount' => !empty($dailyAmount) ? json_encode($dailyAmount) : null,
                    'extra_param' => !empty($extraParamRoom) ? json_encode($extraParamRoom) : null,
                    'rate_detail' => json_encode($room),
                    'total' => $room['amount'],
                    'currency_code' => $param['attributes']['currency'],
                    'status' => $room['is_cancelled'] ? 0 : 1
                ];

            }

            //Expedia Net Commission
            $paymentAmount = $param['attributes']['amount'];
            if($paymentTypeCode == 'CRD' && $paymentSourceCode == 'OTA' && in_array($param['attributes']['ota_name'],['Expedia'])) {
                if(isset($param['attributes']['guarantee']['meta']) && isset($param['attributes']['guarantee']['meta']['expedia_net_commission'])) {
                    $paymentAmountNet = moneyDoubleFormatDecimal($param['attributes']['guarantee']['meta']['virtual_card_current_balance'] / 100);
                    if($paymentAmount != $paymentAmountNet) {
                        $paymentAmount = $paymentAmountNet;
                    }
                }
            }

            //Booking Payment Data
            $bookingParam['payment'] = [
                'payment_code' => null,
                'payment_type_code' => $paymentTypeCode,
                'payment_source_code' => $paymentSourceCode,
                'extra_param' => json_encode($param),
                'total' => $paymentAmount,
                'currency_code' => $param['attributes']['currency'],
                'status' => 2
            ];

            //Booking Channel Payment Data
            $bookingParam['payment_channel'] = null;
            if (isset($param['attributes']['guarantee']) && !empty($param['attributes']['guarantee']) && in_array($param['attributes']['status'], ['new', 'modified'])) {

                $urlParam = [
                    'api_key' => $this->pciApiKey,
                    'method' => 'get',
                    'url' => $this->pciSecureUrl . 'api/v1/bookings/' . $param['attributes']['booking_id'],
                    'profile' => 'channex_entity'
                ];

                $creditCardCapture = $this->requestPCI('POST', 'capture?' . http_build_query($urlParam), null);

                if ($creditCardCapture['status'] && isset($creditCardCapture['data']['attributes']['guarantee']['token'])) {

                    $bookingParam['payment_channel'] = [
                        'type' => 'ch',
                        'data' => $creditCardCapture['data']['attributes']['guarantee']['token'],
                        'status' => 2
                    ];

                }


            }
            //Booking Channel Payment Data


            //Channel Manager ETC Params
            $bookingParam['channel_manager'] = [
                'property_id' => $channelManagerPropertyId,
                'booking_id' => $param['attributes']['booking_id'],
                'sub_channel' => $param['attributes']['ota_name'],
                'sub_channel_booking_id' => $param['attributes']['ota_reservation_code'],
                'unique_id' => $param['attributes']['unique_id'],
                'revision_id' => $param['attributes']['id'],
            ];

            $statusType = null;
            if ($param['attributes']['status'] == 'new') {
                $statusType = 'createBooking';
            } elseif ($param['attributes']['status'] == 'cancelled') {
                $statusType = 'cancelBooking';
            } elseif ($param['attributes']['status'] == 'modified') {
                $statusType = 'modifiedBooking';
            }

            //$statusType = 'modifiedBooking';

            $response['status'] = true;
            $response['type'] = $statusType;
            $response['data'] = $bookingParam;

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

        return $response;
    }


    public function reservationListParam($propertyId)
    {

        $requestParam = [];

        $requestParam = [
            'pagination[page]' => 1,
            'pagination[limit]' => 10,
            'order[inserted_at]' => 'ASC',
            'filter[property_id]' => $propertyId
        ];


        return $requestParam;
    }

    public function reservationList($param)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $request = $this->request('GET', 'booking_revisions/feed', $param);

            //TODO: Delete
            //$request = $this->request('GET', 'booking_revisions/4cb223d2-0259-41f6-b717-fdd4705c57e6', []);

            //a99e36ec-c83a-4b95-b124-2a0c2ba95c78 Expedia
            //4cb223d2-0259-41f6-b717-fdd4705c57e6 Booking
            //0b7912ef-aeaa-479c-9906-d71b7bc65b64 Booking
            //c3426e46-6d6c-440d-a529-fd860b7b3283 Expedia
            //d50355b7-c538-49f6-a0e4-898bf92812e0

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

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


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

        return $response;
    }

    public function reservationConfirmParam($propertyId, $channelManagerBookingId, $bookingCode, $param = [])
    {

        $requestParam = [];

        $requestParam = [
            'revision_id' => $param['channel_manager']['revision_id']
        ];


        return $requestParam;
    }

    public function reservationConfirm($param)
    {
        $response = ['status' => false, 'message' => ''];

        try {

            $request = $this->request('POST', 'booking_revisions/' . $param['revision_id'] . '/ack', []);

            if (!$request['status']) {
                throw new ApiErrorException($request['message']);
            }

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


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

        return $response;
    }

}
