<?php

namespace App\Core\Service;

use App\Core\Repository\Booking\BookingRepository;
use App\Core\Repository\Booking\BookingRoomRepository;
use App\Core\Repository\Booking\BookingRoomPaxRepository;
use App\Core\Repository\Booking\BookingContactRepository;
use App\Core\Repository\Booking\BookingPaymentRepository;
use App\Core\Repository\Booking\BookingStatusRepository;
use App\Core\Repository\PaymentTransaction\PaymentTransactionRepository;
use App\Core\Repository\PropertyBookingEngine\PropertyBookingEngineRepository;
use App\Core\Repository\Booking\BookingPaymentDataCheckRepository;
use App\Core\Repository\PropertyChannel\PropertyChannelRepository;
use App\Core\Repository\PropertyBookingPaymentType\PropertyBookingPaymentTypeRepository;
use App\Core\Mail\BookingPaymentDataCodeMail;

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

class BookingService
{

    private $bookingRepository;
    private $bookingRoomRepository;
    private $bookingRoomPaxRepository;
    private $bookingContactRepository;
    private $bookingPaymentRepository;
    private $paymentTransactionRepository;
    private $bookingStatusRepository;


    public function __construct(

        BookingRepository $bookingRepository,
        BookingRoomRepository $bookingRoomRepository,
        BookingRoomPaxRepository $bookingRoomPaxRepository,
        BookingContactRepository $bookingContactRepository,
        PaymentTransactionRepository $paymentTransactionRepository,
        BookingPaymentRepository $bookingPaymentRepository,
        BookingPaymentDataCheckRepository $bookingPaymentDataCheckRepository,
        BookingStatusRepository $bookingStatusRepository,
        PropertyBookingEngineRepository $propertyBookingEngineRepository,
        PropertyChannelRepository $propertyChannelRepository,
        PropertyBookingPaymentTypeRepository $propertyBookingPaymentTypeRepository,
        Mailer $mailer
    )
    {

        $this->bookingRepository = $bookingRepository;
        $this->bookingRoomRepository = $bookingRoomRepository;
        $this->bookingRoomPaxRepository = $bookingRoomPaxRepository;
        $this->bookingContactRepository = $bookingContactRepository;
        $this->bookingPaymentRepository = $bookingPaymentRepository;
        $this->bookingPaymentDataCheckRepository = $bookingPaymentDataCheckRepository;
        $this->paymentTransactionRepository = $paymentTransactionRepository;
        $this->bookingStatusRepository = $bookingStatusRepository;
        $this->propertyBookingEngineRepository = $propertyBookingEngineRepository;
        $this->propertyChannelRepository = $propertyChannelRepository;
        $this->propertyBookingPaymentTypeRepository = $propertyBookingPaymentTypeRepository;
        $this->mailer = $mailer;


    }

    public function create($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            /*$validationResult = $this->propertyChannelAddValidator->validate($params);

            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }*/

            $insertData = [
                'property_id' => fillOnUndefined($params, 'property_id'),
                'channel_id' => fillOnUndefined($params, 'channel_id'),
                'channel_manager_id' => fillOnUndefined($params, 'channel_manager_id'),
                'booking_code' => fillOnUndefined($params, 'booking_code'),
                'channel_booking_code' => fillOnUndefined($params, 'channel_booking_code'),
                'search_key' => fillOnUndefined($params, 'search_key'),
                'checkin_date' => fillOnUndefined($params, 'checkin_date'),
                'checkout_date' => fillOnUndefined($params, 'checkout_date'),
                'rooms' => fillOnUndefined($params, 'rooms'),
                'payment_type_code' => fillOnUndefined($params, 'payment_type_code'),
                'room_amount' => fillOnUndefined($params, 'room_amount'),
                'addon_amount' => fillOnUndefined($params, 'addon_amount', 0),
                'discount_amount' => fillOnUndefined($params, 'discount_amount', 0),
                'total' => fillOnUndefined($params, 'total'),
                'currency_code' => fillOnUndefined($params, 'currency_code'),
                'channel_discount' => fillOnUndefined($params, 'channel_discount'),
                'channel_markup' => fillOnUndefined($params, 'channel_markup'),
                'channel_currency_code' => fillOnUndefined($params, 'channel_currency_code'),
                'channel_currency_exchange' => fillOnUndefined($params, 'channel_currency_exchange'),
                'channel_token' => fillOnUndefined($params, 'channel_token'),
                'booking_engine_token' => fillOnUndefined($params, 'booking_engine_token'),
                'extra_param' => fillOnUndefined($params, 'extra_param'),
                'status' => fillOnUndefined($params, 'status', 1),
                'reservation_time' => fillOnUndefined($params, 'reservation_time', Carbon::now()->timestamp),
                'coupon_code' => fillOnUndefined($params,'coupon_code')
            ];

            $createResult = $this->bookingRepository->create($insertData);

            if ($createResult['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $createData = $createResult['data'];
            $response = [
                'status' => true,
                'data' => $createData,
            ];

        } 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 output($response);
    }

    public function select($param = [], $column = ['*'])
    {

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

        try {

            $data = $this->bookingRepository->findByCriteria($param, $column);

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

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

        return output($response);
    }

    public function update($id, $param = [])
    {


        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {


            $updateResult = $this->bookingRepository->update($id, $param);
            if ($updateResult['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $updateData = $updateResult['data'];
            $response = [
                'status' => true,
                'data' => $updateData,
            ];

        } 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 output($response);
    }

    public function getBookingList($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $getListCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')],
                ],
                'orderBy' => [/*['field' => 'is_viewed', 'value' => 'ASC'],*/
                    ['field' => 'id', 'value' => 'DESC']],
                'with' => ['bookingContact', 'bookingChannel', 'bookingPayment', 'bookingPaymentType', 'bookingStatus', 'bookingActiveMessageCount'],
            ];

            if (isset($param['filter']) && !empty($param['filter'])) {

                $filterParams = [
                    'channel_id' => [
                        'criteriaType' => 'EQUAL'
                    ],
                    'booking_code' => [
                        'criteriaType' => 'LIKE'
                    ],
                    'payment_type_code' => [
                        'criteriaType' => 'LIKE'
                    ],
                    'status' => [
                        'criteriaType' => 'EQUAL'
                    ],
                ];

                foreach ($param['filter'] as $inputName => $inputValue) {
                    if (!is_null($inputValue) && key_exists($inputName, $filterParams)) {
                        if ($filterParams[$inputName]['criteriaType'] == 'EQUAL') {
                            $getListCriteria['criteria'][] = ['field' => $inputName, 'condition' => '=', 'value' => $inputValue];
                        } elseif ($filterParams[$inputName]['criteriaType'] == 'LIKE') {
                            $getListCriteria['criteria'][] = ['field' => $inputName, 'condition' => 'LIKE', 'value' => '%' . $inputValue . '%'];
                        }
                    }
                }


                if (isset($param['filter']['date_type']) && isset($param['filter']['date_range'])) {
                    if (in_array($param['filter']['date_type'], ['checkin_date', 'checkout_date'])) {
                        $dateRange = explode(' ', $param['filter']['date_range']);
                        $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->toDateString()];
                        $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->toDateString()];
                    }

                    if (in_array($param['filter']['date_type'], ['reservation_time'])) {
                        $dateRange = explode(' ', $param['filter']['date_range']);
                        $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->unix()];
                        $getListCriteria['criteria'][] = ['field' => $param['filter']['date_type'], 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->unix()];
                    }
                }

                $getListCriteria['take'] = 10000;

            } else {
                $getListCriteria['take'] = 200;
            }

            $booking = $this->select($getListCriteria, ['id', 'channel_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'reservation_time', 'created_at', 'updated_at', 'status', 'is_viewed', 'channel_booking_code']);

            if ($booking['status'] != 'success') {
                throw new ApiErrorException('Property Booking Data not found');
            }

            $bookingStatusColor = [
                0 => '#CC0000',
                1 => '#007E33',
                2 => '#FF8800',
                3 => '#9933CC',
            ];

            if (!empty($booking['data'])) {
                foreach ($booking['data'] as $dataKey => $data) {
                    $booking['data'][$dataKey]['booking_active_message_count'] = count($data['booking_active_message_count']);
                    $booking['data'][$dataKey]['booking_status']['bg-color'] = isset($bookingStatusColor[$booking['data'][$dataKey]['status']]) ? $bookingStatusColor[$booking['data'][$dataKey]['status']] : '#E0E0E0';
                    $booking['data'][$dataKey]['is_viewed'] = empty($data['is_viewed']) ? false : true;
                }
            }

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

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

        return output($response);
    }


    public function getPropertyBookingListFilter($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $filter = [];

            $propertyChannelParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]];
            $propertyChannel = $this->propertyChannelRepository->findByCriteria($propertyChannelParam, ['id', 'name', 'restriction']);

            $propertyChannel = array_filter($propertyChannel, function ($channel) use ($param) {
                if (!empty($channel['restriction'])) {
                    $channelRestriction = json_decode($channel['restriction'], 1);
                    if (in_array($param['property_id'], $channelRestriction)) {
                        return $channel;
                    }
                } else {
                    return $channel;
                }
            });

            $filter['channel'] = array_values($propertyChannel);

            $propertyBookingPaymentTypeParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]];
            $propertyBookingPaymentType = $this->propertyBookingPaymentTypeRepository->findByCriteria($propertyBookingPaymentTypeParam, ['code', 'name', 'language_key']);
            $filter['payment_type'] = $propertyBookingPaymentType;

            $bookingStatusParam = ['criteria' => [['field' => 'status', 'condition' => '=', 'value' => 1]]];
            $bookingStatus = $this->bookingStatusRepository->findByCriteria($bookingStatusParam, ['id', 'name', 'language_key']);
            $filter['status'] = $bookingStatus;

            $filter['date_type'] = [];
            $filter['date_type'][] = ['code' => 'checkin_date', 'name' => 'Checkin Date', 'language_key' => 'enw-filter-checkin_date'];
            $filter['date_type'][] = ['code' => 'checkout_date', 'name' => 'Checkout Date', 'language_key' => 'enw-filter-checkout_date'];
            $filter['date_type'][] = ['code' => 'reservation_time', 'name' => 'Booking Date', 'language_key' => 'enw-filter-booking_date'];

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

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

        return output($response);
    }

    public function getBookingDetail($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $requestData = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')],
                    ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'booking_id')]
                ],
                'with' => [
                    'bookingContact', 'bookingChannel', 'bookingAddon.propertyChannelAddon.propertyAddon.fact',
                    'bookingRoom.roomRateMapping.propertyRoom.propertyRoomType', 'bookingRoom.roomRateMapping.propertyRoomRate.propertyRoomRateAccommodation',
                    'bookingRoom.roomPax.paxCountry', 'bookingPayment', 'bookingPaymentType', 'bookingPaymentTransaction.paymentTypeMapping.paymentType',
                    'bookingPaymentData',
                    'bookingRoom.roomRateMapping.propertyRoom.propertyRoomBedGroup.propertyRoomBedType', 'bookingRoom.roomRateMapping.propertyRoom.smokingPreference.propertyFact',
                    'bookingRoom.smokingFact','propertyBookingEngineSearch'
                ],
                'firstRow' => true
            ];

            $booking = $this->select($requestData, ['id', 'property_id', 'channel_id', 'booking_code', 'search_key','channel_booking_code', 'coupon_code', 'reservation_time', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status', 'is_viewed']);

            if ($booking['status'] != 'success') {
                throw new ApiErrorException('Property Booking Data not found');
            }

            $booking['data']['voucher'] = null;
            $propertyBookingEngineParam = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $booking['data']['property_id']],
                    //['field' => 'channel_id', 'condition' => '=', 'value' => $booking['data']['channel_id']],
                ],
                'firstRow' => true,
            ];

            $propertyBookingEngine = $this->propertyBookingEngineRepository->findByCriteria($propertyBookingEngineParam);
            if (!empty($propertyBookingEngine)) {
                $booking['data']['voucher'] = Config::get('app.bookingEngineUrl') . '/' . $propertyBookingEngine['token'] . '/' . fillOnUndefined($param, 'locale', 'en') . '/booking-detail/' . $booking['data']['booking_code'];
            }


            if (!empty($booking['data']) && !empty($booking['data']['booking_room'])) {
                $bookingRooms = &$booking['data']['booking_room'];

                foreach ($bookingRooms as &$bookingRoom) {
                    $bookingRoom['cancellation_policy'] = json_decode($bookingRoom['cancellation_policy'], true);
                    $bookingRoom['rate_detail'] = json_decode($bookingRoom['rate_detail'], true);


                    //80678

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

                    $bookingRoom['property_room_bed_group'] = null;
                    if(isset($propertyRoomBedGroup[$bookingRoom['property_room_bed_group_id']])) {
                        $bookingRoom['property_room_bed_group'] = $propertyRoomBedGroup[$bookingRoom['property_room_bed_group_id']];
                    }

                }
            }

            $booking = $booking['data'];
            $booking['booking_payment_transaction'] = collect($booking['booking_payment_transaction'])->map(function ($value) {
                $return = $value;
                unset($return['params']);
                unset($return['extra_params']);
                unset($return['response']);
                unset($return['extraParamsArray']);
                unset($return['paramsArray']);
                unset($return['responseArray']);
                unset($return['payment_type_mapping']);

                $return['credit_card_number'] = isset($value['paramsArray']['creditCard']['number']) ? $value['paramsArray']['creditCard']['number'] : null;
                $return['bank_name'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['name'] : null;
                $return['bank_icon'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['icon'] : null;
                $return['created_at'] = date('Y-m-d H:i:s', $value['created_at']);
                $return['updated_at'] = date('Y-m-d H:i:s', $value['updated_at']);
                return $return;

            });

            $booking['isThereBookingPaymentData'] = !empty($booking['booking_payment_data']) ? true : false;
            unset($booking['booking_payment_data']);

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

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

        return output($response);
    }

    public function getBookingListCount($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $requestData = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')],
                    ['field' => 'status', 'condition' => '=', 'value' => 1] // status 1 = booking
                ],
                'count' => true

            ];

            if (fillOnUndefined($param, 'channel_id')) {
                $requestData['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'channel_id')];
            }

            $booking = $this->select($requestData, ['id']);

            if ($booking['status'] != 'success') {
                throw new ApiErrorException('Property Booking Count Data not found');
            }
            $response = [
                'status' => true,
                'data' => $booking['data']
            ];

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

        return output($response);
    }

    public function getTransactionList($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $requestData = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')]
                ],
                'orderBy' => [['field' => 'id', 'value' => 'DESC']],
                'with' => ['bookingDetail.bookingContact', 'paymentTypeMapping.paymentType', 'paymentTransactionStatus', 'parentTransaction']
            ];


            if (isset($param['filter']) && !empty($param['filter'])) {

                $filterParams = [
                    'code' => [
                        'criteriaType' => 'LIKE'
                    ],
                    'order_id' => [
                        'criteriaType' => 'LIKE'
                    ],
                    'status' => [
                        'criteriaType' => 'EQUAL'
                    ],
                ];

                foreach ($param['filter'] as $inputName => $inputValue) {
                    if (!is_null($inputValue) && key_exists($inputName, $filterParams)) {
                        if ($filterParams[$inputName]['criteriaType'] == 'EQUAL') {
                            $requestData['criteria'][] = ['field' => $inputName, 'condition' => '=', 'value' => $inputValue];
                        } elseif ($filterParams[$inputName]['criteriaType'] == 'LIKE') {
                            $requestData['criteria'][] = ['field' => $inputName, 'condition' => 'LIKE', 'value' => '%' . $inputValue . '%'];
                        }
                    }
                }

                if (isset($param['filter']['date_range'])) {
                    $dateRange = explode(' ', $param['filter']['date_range']);
                    $requestData['criteria'][] = ['field' => 'created_at', 'condition' => '>=', 'value' => Carbon::parse($dateRange[0])->timestamp];
                    $requestData['criteria'][] = ['field' => 'created_at', 'condition' => '<', 'value' => Carbon::parse($dateRange[1])->addDay()->timestamp];
                }

                $requestData['take'] = 10000;

            } else {
                $requestData['take'] = 200;
            }

            //dd($requestData);


            $transactions = $this->paymentTransactionRepository->findByCriteria($requestData);

            $transactions = collect($transactions)->map(function ($value) {

                $return = $value;
                unset($return['params']);
                unset($return['extra_params']);
                unset($return['response']);
                unset($return['extraParamsArray']);
                unset($return['paramsArray']);
                unset($return['responseArray']);
                unset($return['payment_type_mapping']);

                $return['credit_card_number'] = isset($value['paramsArray']['creditCard']['number']) ? $value['paramsArray']['creditCard']['number'] : null;
                $return['bank_name'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['name'] : null;
                $return['bank_icon'] = isset($value['payment_type_mapping']['payment_type']) ? $value['payment_type_mapping']['payment_type']['icon'] : null;
                $return['created_at'] = date('Y-m-d H:i:s', $value['created_at']);
                $return['updated_at'] = date('Y-m-d H:i:s', $value['updated_at']);
                $return['payment_link'] = $value['status'] == 5 && $value['transaction_type'] != 'BKG' ? Config::get('app.paymentFormLink') . $value['order_id'] : null;
                $return['status_language_key'] = isset($value['payment_transaction_status']['language_key']) ? $value['payment_transaction_status']['language_key'] : null;
                if ($value['parent_transaction']) {
                    if ($value['parent_transaction']['status'] == 5) {
                        $return['id'] = $value['parent_transaction']['id'];
                    }
                }
                return $return;

            });

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

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

        return output($response);
    }

    public function getBookingDetailedList($param)
    {

        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $requestData = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')]
                ],
                'orderBy' => [['field' => 'id', 'value' => 'DESC']],
                'with' => ['bookingContact', 'bookingChannel', 'bookingRoom', 'bookingRoom.roomPax.paxCountry', 'bookingPayment', 'bookingPaymentType', 'bookingPaymentTransaction.paymentTypeMapping.paymentType'],
            ];

            if (fillOnUndefined($param, 'channel_id')) {
                $requestData['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'channel_id')];
            }

            $booking = $this->select($requestData, ['id', 'channel_id', 'booking_code', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']);

            if ($booking['status'] != 'success') {
                throw new ApiErrorException('Property Booking Data not found');
            }

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

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

        return output($response);
    }

    public function getBookingstatusList($param = [], $column = ['*'])
    {

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

        try {

            $data = $this->bookingStatusRepository->findByCriteria($param, $column);

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

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

        return output($response);
    }

    public function getBookingPayment($param)
    {

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

        DB::beginTransaction();

        try {

            //TODO: Validator

            $confirmCode = fillOnUndefined($param, 'confirmCode');

            $requestData = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($param, 'property_id')],
                    ['field' => 'id', 'condition' => '=', 'value' => fillOnUndefined($param, 'booking_id')]
                ],
                'with' => ['bookingPaymentData', 'bookingPaymentDataCheck'],
                'firstRow' => true
            ];

            $bookingDetail = $this->select($requestData, ['id', 'property_id', 'channel_id', 'channel_manager_id', 'search_key', 'booking_code', 'channel_booking_code', 'reservation_time', 'checkin_date', 'checkout_date', 'payment_type_code', 'total', 'currency_code', 'created_at', 'updated_at', 'status']);

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

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

            $bookingDetail = $bookingDetail['data'];

            if (in_array($bookingDetail['channel_manager_id'], [null])) {
                if (!in_array($bookingDetail['payment_type_code'], ['HTL', 'CHN'])) {
                    throw new ApiErrorException('Only available for pay at hotel');
                }
            } elseif (in_array($bookingDetail['channel_manager_id'], [1, 2])) {
                if (!in_array($bookingDetail['payment_type_code'], ['CRD', 'CHN'])) {
                    throw new ApiErrorException('Only available for pay at credit card');
                }
            }

            if (empty($bookingDetail['booking_payment_data'])) {
                throw new ApiErrorException('Payment information not available');
            }

            $bookingPaymentDataCheck = $bookingDetail['booking_payment_data_check'];


            switch ($bookingDetail['channel_manager_id']) {
                case '1':

                    $bookingPaymentData = $bookingDetail['booking_payment_data'];

                    $getBookingPaymentDataCollect = collect($bookingPaymentData);
                    $isChannelManagerPaymentData = $getBookingPaymentDataCollect->where('type', 'ch')->first();

                    if ($isChannelManagerPaymentData) {
                        $channelPaymentDataUrl = 'https://www.reseliva.com/siteBase/REST/tsr/?lang=tr&res_id=' . $bookingDetail['search_key'] . '&hash=' . $isChannelManagerPaymentData['data'];

                        $response = [
                            'status' => true,
                            'data' => [
                                'type' => 'redirect',
                                'redirectUrl' => $channelPaymentDataUrl,
                                'channel_manager_id' => $bookingDetail['channel_manager_id']
                            ],
                        ];

                    } else {
                        throw new ApiErrorException('Payment data not found.');
                    }

                    break;
                case '2':

                    $isConfirmCodeRequire = true;

                    if (empty($confirmCode) && $isConfirmCodeRequire) {

                        if (count($bookingPaymentDataCheck) > 2) {
                            throw new ApiErrorException('Payment information can only be viewed 3 times');
                        }

                        $unlockCode = rand(1000, 9999);
                        $paymentDataCheckParam = [
                            'booking_id' => $bookingDetail['id'],
                            'code' => md5($unlockCode),
                            'status' => 2,
                            'expiry_time' => Carbon::now()->addMinutes(30)->timestamp,
                            'created_by' => fillOnUndefined($param, 'user_id'),
                            'updated_by' => fillOnUndefined($param, 'user_id'),
                        ];

                        $createPaymentDataCheck = $this->bookingPaymentDataCheckRepository->create($paymentDataCheckParam);

                        if ($createPaymentDataCheck['status'] != 'success') {
                            throw new Exception('api-unknown_error');
                        }

                        $response = [
                            'status' => true,
                            'data' => [
                                'type' => 'code',
                                'expiry_time' => Carbon::createFromTimestamp($paymentDataCheckParam['expiry_time'])->toDateTimeString(),
                                'channel_manager_id' => $bookingDetail['channel_manager_id']
                                //'code' => $unlockCode
                            ]

                        ];

                        //BookingPaymentDataCode
                        $mailParams = [
                            'user_id' => $param['user_id'],
                            'booking_id' => $bookingDetail['id'],
                            'unlock_code' => $unlockCode
                        ];

                        $this->mailer->send(new BookingPaymentDataCodeMail($mailParams));
                        //BookingPaymentDataCode

                    } else {

                        if ($isConfirmCodeRequire) {

                            $bookingPaymentDataCheckCollect = collect($bookingPaymentDataCheck);

                            $bookingPaymentDataCheckRow = $bookingPaymentDataCheckCollect
                                ->where('code', md5($confirmCode))
                                ->where('status', 2)
                                ->where('expiry_time', '>', Carbon::now()->timestamp)
                                ->first();

                            if (empty($bookingPaymentDataCheckRow)) {
                                throw new ApiErrorException('Code could not be verified, please check again');
                            }

                        }

                        $bookingPaymentData = $bookingDetail['booking_payment_data'];

                        $getBookingPaymentDataCollect = collect($bookingPaymentData);
                        $isChannelManagerPaymentData = $getBookingPaymentDataCollect->where('type', 'ch')->sortByDesc('id')->first();

                        $channelService = App::make("App\Core\Service\ChannelManager\Channex");


                        $sessionTokenParam = [
                            'session_token' => [
                                //'scope' => 'card'
                                'scope' => 'show_card'
                            ]
                        ];

                        $sessionToken = $channelService->getSessionToken($sessionTokenParam);
                        $sessionToken = $sessionToken['status'] ? $sessionToken['data']['id'] : null;

                        $serviceCodeTokenParam = [
                            'session_token' => [
                                //'scope' => 'service_code'
                                'scope' => 'show_service_code'
                            ]
                        ];

                        $serviceCodeToken = $channelService->getSessionToken($serviceCodeTokenParam);
                        $serviceCodeToken = $serviceCodeToken['status'] ? $serviceCodeToken['data']['id'] : null;


                        $channelPaymentDataUrl = 'https://pci.channex.io/api/v1/show_card?card_token=' . $isChannelManagerPaymentData['data'] . '&session_token=' . $sessionToken . '&service_code_token=' . $serviceCodeToken;

                        $response = [
                            'status' => true,
                            'data' => [
                                'type' => 'redirect',
                                'redirectUrl' => $channelPaymentDataUrl,
                                'channel_manager_id' => $bookingDetail['channel_manager_id']
                            ],
                        ];

                    }

                    break;
                default :

                    if (empty($confirmCode)) {

                        if (count($bookingPaymentDataCheck) > 2) {
                            throw new ApiErrorException('Payment information can only be viewed 3 times');
                        }

                        $unlockCode = rand(1000, 9999);
                        $paymentDataCheckParam = [
                            'booking_id' => $bookingDetail['id'],
                            'code' => md5($unlockCode),
                            'status' => 2,
                            'expiry_time' => Carbon::now()->addMinutes(30)->timestamp,
                            'created_by' => fillOnUndefined($param, 'user_id'),
                            'updated_by' => fillOnUndefined($param, 'user_id'),
                        ];

                        $createPaymentDataCheck = $this->bookingPaymentDataCheckRepository->create($paymentDataCheckParam);

                        if ($createPaymentDataCheck['status'] != 'success') {
                            throw new Exception('api-unknown_error');
                        }

                        $response = [
                            'status' => true,
                            'data' => [
                                'type' => 'code',
                                'expiry_time' => Carbon::createFromTimestamp($paymentDataCheckParam['expiry_time'])->toDateTimeString(),
                                'channel_manager_id' => $bookingDetail['channel_manager_id']
                                //'code' => $unlockCode
                            ]

                        ];

                        //BookingPaymentDataCode
                        $mailParams = [
                            'user_id' => $param['user_id'],
                            'booking_id' => $bookingDetail['id'],
                            'unlock_code' => $unlockCode
                        ];

                        $this->mailer->send(new BookingPaymentDataCodeMail($mailParams));
                        //$this->mailer->onQueue('bookingPaymentDataCode', new BookingPaymentDataCodeMail($mailParams));
                        //BookingPaymentDataCode

                    } else {

                        if (empty($bookingPaymentDataCheck)) {
                            throw new ApiErrorException('No request to view payment information yet');
                        }

                        $bookingPaymentDataCheckCollect = collect($bookingPaymentDataCheck);

                        $bookingPaymentDataCheckRow = $bookingPaymentDataCheckCollect
                            ->where('code', md5($confirmCode))
                            ->where('status', 2)
                            ->where('expiry_time', '>', Carbon::now()->timestamp)
                            ->first();

                        if (empty($bookingPaymentDataCheckRow)) {
                            throw new ApiErrorException('Code could not be verified, please check again');
                        }

                        $bookingPaymentDataEncrypted = collect($bookingDetail['booking_payment_data'])->groupBy('type')->toArray();


                        $bookingPaymentData = [];
                        foreach ($bookingPaymentDataEncrypted as $type => $value) {

                            if ($type == 'cc') {
                                $value = collect($value)->sortBy('id')->toArray();
                                foreach ($value as $cardValue) {
                                    $bookingPaymentData[$type][] = Crypt::decrypt($cardValue['data']);
                                }
                                $bookingPaymentData[$type] = implode(' ', $bookingPaymentData[$type]);
                            } else {
                                $cardValue = reset($value);
                                $bookingPaymentData[$type] = Crypt::decrypt($cardValue['data']);
                            }

                        }

                        $this->bookingPaymentDataCheckRepository->update($bookingPaymentDataCheckRow['id'], ['status' => 1, 'confirm_time' => Carbon::now()->timestamp]);

                        $bookingPaymentData['bookingCode'] = $bookingDetail['booking_code'];

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

                    }

                    break;

            }


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

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

        return output($response);
    }

}
