<?php

namespace App\Core\Service;

use App\Core\Repository\PropertyRoomRatePrice\PropertyRoomRatePriceRepository;
use App\Core\Repository\PropertyChannel\PropertyChannelRepository;
use App\Core\Repository\PropertyRoomRateMapping\PropertyRoomRateMappingRepository;
use App\Core\Repository\Property\PropertyRepository;
use App\Core\Repository\PropertyChannelMapping\PropertyChannelMappingRepository;
use App\Core\Repository\PropertyRoomConnected\PropertyRoomConnectedRepository;
use App\Core\Repository\PropertyRoomRateChannelMapping\PropertyRoomRateChannelMappingRepository;


use App\Core\Validator\PropertyRoomRatePrice\PropertyRoomRatePriceAddValidator;
use App\Core\Validator\PropertyRoomRatePrice\PropertyRoomRatePriceUpdateValidator;
use App\Core\Validator\PropertyRoomRatePrice\PropertyRoomRatePriceDeleteValidator;
use App\Core\Validator\PropertyRoomRatePrice\PropertyRoomRatePriceBulkInsertValidator;
use App\Core\Validator\PropertyRoomRatePrice\PropertyRoomRatePriceBulkUpdateValidator;


use App;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;
use Exception;
use App\Exceptions\ApiErrorException;
use Illuminate\Support\Carbon;

class PropertyRoomRatePriceService
{

    private $propertyRoomRatePriceRepository;
    private $propertyRoomRatePriceAddValidator;
    private $propertyRoomRatePriceUpdateValidator;
    private $propertyRoomRatePriceDeleteValidator;
    private $propertyRoomRatePriceBulkInsertValidator;
    private $propertyRoomRatePriceBulkUpdateValidator;
    private $propertyChannelRepository;
    private $propertyRoomRateMappingRepository;
    private $propertyRepository;
    private $propertyChannelMappingRepository;
    private $propertyQuickPricingService;
    private $propertyRoomConnectedRepository;
    private $propertyRoomRateChannelMappingRepository;


    public function __construct(

        PropertyRoomRatePriceRepository $propertyRoomRatePriceRepository,
        PropertyRoomRatePriceUpdateValidator $propertyRoomRatePriceUpdateValidator,
        PropertyRoomRatePriceDeleteValidator $propertyRoomRatePriceDeleteValidator,
        PropertyRoomRatePriceBulkInsertValidator $propertyRoomRatePriceBulkInsertValidator,
        PropertyRoomRatePriceBulkUpdateValidator $propertyRoomRatePriceBulkUpdateValidator,
        PropertyChannelRepository $propertyChannelRepository,
        PropertyRoomRateMappingRepository $roomRateMappingRepository,
        PropertyRepository $propertyRepository,
        PropertyChannelMappingRepository $propertyChannelMappingRepository,
        PropertyRoomConnectedRepository $propertyRoomConnectedRepository,
        PropertyRoomRatePriceAddValidator $propertyRoomRatePriceAddValidator,
        PropertyQuickPricingService $propertyQuickPricingService,
        PropertyRoomRateChannelMappingRepository $propertyRoomRateChannelMappingRepository
    )
    {

        $this->propertyRoomRatePriceRepository = $propertyRoomRatePriceRepository;
        $this->propertyRoomRatePriceAddValidator = $propertyRoomRatePriceAddValidator;
        $this->propertyRoomRatePriceUpdateValidator = $propertyRoomRatePriceUpdateValidator;
        $this->propertyRoomRatePriceDeleteValidator = $propertyRoomRatePriceDeleteValidator;
        $this->propertyRoomRatePriceBulkInsertValidator = $propertyRoomRatePriceBulkInsertValidator;
        $this->propertyRoomRatePriceBulkUpdateValidator = $propertyRoomRatePriceBulkUpdateValidator;
        $this->propertyChannelRepository = $propertyChannelRepository;
        $this->propertyRoomRateMappingRepository = $roomRateMappingRepository;
        $this->propertyRepository = $propertyRepository;
        $this->propertyChannelMappingRepository = $propertyChannelMappingRepository;
        $this->propertyRoomConnectedRepository = $propertyRoomConnectedRepository;
        $this->propertyQuickPricingService = $propertyQuickPricingService;
        $this->propertyRoomRateChannelMappingRepository = $propertyRoomRateChannelMappingRepository;


    }

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

            $validationResult = $this->propertyRoomRatePriceAddValidator->validate($params);

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

            $insertData =
                [
                    'property_id' => fillOnUndefined($params, 'property_id'),
                    'name' => fillOnUndefined($params, 'name'),
                    'description' => fillOnUndefined($params, 'description'),
                    'min_stay' => fillOnUndefined($params, 'min_stay'),
                    "status" => fillOnUndefined($params, "status", 1),
                    "created_by" => fillOnUndefined($params, "created_by"),
                    "updated_by" => fillOnUndefined($params, "updated_by"),
                ];

            $userCreateResult = $this->propertyRoomRatePriceRepository->create($insertData);

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

        } 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->propertyRoomRatePriceRepository->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->propertyRoomRatePriceRepository->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 updateOrCreate($criteria = [], $saveData = [])
    {


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

        try {

            $data = $this->propertyRoomRatePriceRepository->updateOrCreate($criteria, $saveData);
            if ($data['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $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 addPropertyRoomRatePrice($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $validationResult = $this->propertyRoomRatePriceAddValidator->validate($params);
            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }

            $insertData =
                [
                    'property_id' => fillOnUndefined($params, 'property_id', 0),
                    'property_room_id' => fillOnUndefined($params, 'property_room_id', 0),
                    'room_rate_mapping_id' => fillOnUndefined($params, 'room_rate_mapping_id', 0),
                    'offer_id' => fillOnUndefined($params, 'offer_id', 0),
                    'channel_id' => fillOnUndefined($params, 'channel_id', 0),
                    'min_stay' => fillOnUndefined($params, 'min_stay', 0),
                    'max_stay' => fillOnUndefined($params, 'max_stay', 0),
                    'stop_sell' => fillOnUndefined($params, 'stop_sell', 0),
                    'booking_on_request' => fillOnUndefined($params, 'booking_on_request', 0),
                    'date' => fillOnUndefined($params, 'date', null),
                    'amount' => fillOnUndefined($params, 'amount', 0),
                    'currency' => fillOnUndefined($params, 'currency', null),
                    "status" => fillOnUndefined($params, "status", 1),
                    "created_by" => fillOnUndefined($params, "user_id", 0),
                    "updated_by" => fillOnUndefined($params, "user_id", 0),
                ];


            $userCreateResult = $this->propertyRoomRatePriceRepository->create($insertData);
            if ($userCreateResult['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }
            $userData = $userCreateResult["data"];
            $response = [
                'status' => true,
                'data' => $userData,
            ];

        } 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 getPropertyRoomRatePrices($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $criteria = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1]
                ]
            ];

            $data = $this->propertyRoomRatePriceRepository->findByCriteria($criteria, ['id', 'property_id', 'name', 'description', 'min_stay']);

            $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 updatePropertyRoomRatePrice($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $validationResult = $this->propertyRoomRatePriceUpdateValidator->validate($params);

            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }
            $updateData =
                [
                    'property_id' => fillOnUndefined($params, 'property_id'),
                    'name' => fillOnUndefined($params, 'name'),
                    'description' => fillOnUndefined($params, 'description'),
                    'min_stay' => fillOnUndefined($params, 'min_stay'),
                    "updated_by" => fillOnUndefined($params, "user_id"),
                ];

            if (isset($params['status'])) {
                $updateData['status'] = fillOnUndefined($params, "status", 0);
            }


            $updateResult = $this->update($params['id'], $updateData);
            if ($updateResult['status'] != 'success') {
                throw new ApiErrorException($updateResult['message']);
            }
            $userData = $updateResult["data"];
            $response = [
                'status' => true,
                'data' => $userData,
            ];

        } 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 deletePropertyRoomRatePrice($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $validationResult = $this->propertyRoomRatePriceDeleteValidator->validate($params);

            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }
            $updateData =
                [
                    'status' => 0,
                    "updated_by" => fillOnUndefined($params, "user_id")
                ];

            $updateResult = $this->update($params['id'], $updateData);
            if ($updateResult['status'] != 'success') {
                throw new ApiErrorException($updateResult['message']);
            }
            $userData = $updateResult["data"];
            $response = [
                'status' => true,
                'data' => $userData,
            ];

        } 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 bulkUpdate($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $startDate = Carbon::parse($params['start_date']);
            $endDate = Carbon::parse($params['end_date']);
            $diffInDays = $startDate->diffInDays($endDate);
            $includeDays = fillOnUndefined($params, 'include_days', []);
            $insertData = [];

            $validationResult = $this->propertyRoomRatePriceBulkInsertValidator->validate($params);
            if ($validationResult->errors()->first()) {
                $errors = $validationResult->errors()->all();
                throw new ApiErrorException($errors);
            }
            // Get Channel Mapping Information
            $criteria = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')],
                ],
                'with' => ['channel'],
                'firstRow' => 1
            ];

            $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'connected_channel_id', 'currency_code', 'property_availability_type_id']);
            if (!$channelData) {
                throw new ApiErrorException('Channel mapping not found');
            }

            $updateData = [];
            switch ($params['update_type']) {
                case 'rate':
                case 'quick_pricing':
                    $updateData = ['amount' => $params['value']];
                    break;
                case 'min_stay':
                    $updateData = ['min_stay' => $params['value']];
                    break;
                case 'maxstay':
                    $updateData = ['max_stay' => $params['value']];
                    break;
                case 'stopsell':
                    $updateData = ['stop_sell' => $params['value']];
                    break;
                case 'bookingonrequest':
                    $updateData = ['booking_on_request' => $params['value']];
                    break;
                case 'rate_stop_sell':
                    $updateData = ['stop_sell' => $params['value']];
                    break;

            }

            $updateData = array_merge($updateData, ['updated_by' => fillOnUndefined($params, "user_id", 0), 'updated_at' => Carbon::now()->timestamp,]);

            //min_stay min 1
            if(isset($updateData['min_stay']) && empty($updateData['min_stay'])) {
                $updateData['min_stay'] = 1;
            }

            $selectIds = $this->selectedRoomRateIds($params);
            if ($selectIds['status'] != 'success') {
                throw new ApiErrorException('array error');
            }
            $selectIds = $selectIds['data'];

            $propertyRequest = [
                'criteria' => [
                    ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => 1

            ];
            $property = $this->propertyRepository->findByCriteria($propertyRequest);

            if (!$property) {
                throw new ApiErrorException(lang('Property not found'));
            }

            //Quick Pricing
            $propertyQuickPricingMappingCollect = [];
            if ($params['update_type'] == 'quick_pricing') {
                $requestSelectCriteria = [
                    'criteria' => [
                        ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                    ],
                    'with' => ['propertyRoomRateChannelMapping.propertyRoomRateMapping', 'propertyRoomRateChannelMapping.propertyRoomRateMapping']
                ];

                //Connected Channel Quick Pricing Settings
                if (!empty($channelData['connected_channel_id'])) {
                    $requestSelectCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $channelData['connected_channel_id']];
                } else {
                    $requestSelectCriteria['criteria'][] = ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']];
                }

                $columns = ['id', 'property_id', 'channel_id', 'room_rate_channel_mapping_id', 'action_type', 'price_type', 'price_value'];
                $requestSelectResult = $this->propertyQuickPricingService->selectPropertyQuickPricingMapping($requestSelectCriteria, $columns);

                if ($requestSelectResult['status'] == 'success') {
                    $propertyQuickPricingMappingCollect = collect($requestSelectResult['data']);
                }
            }


            $channelDataRequest = [
                'criteria' => [
                    ['field' => 'id', 'condition' => '=', 'value' => $params['channel_id']],
                ],
                'firstRow' => 1
            ];
            $selectedChannel = $this->propertyChannelRepository->findByCriteria($channelDataRequest, ['id', 'name', 'currency_code']);
            $selectedChannel = $selectedChannel ? $selectedChannel : [];
            if (!$selectedChannel) {
                throw new ApiErrorException('channel error');
            }

            $roomRateMappingRequest = [
                'whereIn' => [
                    ['field' => 'id', 'value' => $selectIds['room_rate_mapping_ids']],
                ],
                'with' => ['propertyRoomRate']
            ];
            $selectedRoomRateMapping = $this->propertyRoomRateMappingRepository->findByCriteria($roomRateMappingRequest);
            $selectedRoomRateMapping = $selectedRoomRateMapping ? $selectedRoomRateMapping : [];
            $selectedRoomRateMapping = collect($selectedRoomRateMapping);

            $oldRoomRatesCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                    ['field' => 'date', 'condition' => '>=', 'value' => $params['start_date']],
                    ['field' => 'date', 'condition' => '<=', 'value' => $params['end_date']],
                ]
            ];

            $oldRoomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria);
            $oldRoomRatePrice = $oldRoomRatePrice ? $oldRoomRatePrice : [];
            $updateThis = [];

            $oldRoomRatePriceKeys = [];
            foreach ($oldRoomRatePrice as $oldPrice) {
                $oldPriceKey = $oldPrice['property_room_id'] . "|" . $oldPrice['room_rate_mapping_id'] . "|" . $oldPrice['date'] . "|" . $oldPrice['channel_id'] . "|" . $oldPrice['availability_type_id'];
                $oldRoomRatePriceKeys[$oldPriceKey] = $oldPrice['id'];
            }


            foreach ($params['room_rates'] as $roomRate) {
                foreach ($roomRate['room_rate_mapping_id'] as $roomRateMappingId) {

                    if ($params['update_type'] == 'quick_pricing') {

                        //Connected Channel Quick Pricing Settings
                        if ( !empty($channelData['connected_channel_id'])) {
                            $quickPricing = $propertyQuickPricingMappingCollect->where('property_id', $params['property_id'])
                                ->where('channel_id', $channelData['connected_channel_id'])->where('property_room_rate_channel_mapping.room_rate_mapping_id', $roomRateMappingId)
                                ->first();

                        } else {
                            $quickPricing = $propertyQuickPricingMappingCollect->where('property_id', $params['property_id'])
                                ->where('channel_id', $params['channel_id'])->where('property_room_rate_channel_mapping.room_rate_mapping_id', $roomRateMappingId)
                                ->first();
                        }

                        if (empty($quickPricing)) {
                            continue;
                        }
                    }

                    $startDate = Carbon::parse($params['start_date']);
                    for ($i = 0; $i <= $diffInDays; $i++) {
                        if (!in_array($startDate->shortEnglishDayOfWeek, $includeDays)) {
                            $startDate = $startDate->addDay();
                            continue;
                        }

                        $currentRoomRateMapping = $selectedRoomRateMapping->where('id', '=', $roomRateMappingId)->first();
                        $currentRoomRate = $currentRoomRateMapping['property_room_rate'];

                        $insertDataStopSell = 0;
                        if ($params['update_type'] == 'rate_stop_sell') {
                            $insertDataStopSell = fillOnUndefined($params, 'value', 0);
                        }

                        $insertDataMinStay = 0;
                        if ($params['update_type'] == 'min_stay') {
                            $insertDataMinStay = fillOnUndefined($params, 'value', 1);
                        }

                        $insertDataAmount = 0;
                        if ($params['update_type'] == 'rate') {
                            $insertDataAmount = fillOnUndefined($params, 'value', 0);
                        }

                        if ($params['update_type'] == 'quick_pricing') {

                            $amount = fillOnUndefined($params, 'value', 0);

                            $amountCalculated = $amount;

                            $amountAffected = 0;
                            if ($quickPricing['price_type'] == 'PER') {
                                $amountAffected = ($amount * $quickPricing['price_value']) / 100;
                            } elseif ($quickPricing['price_type'] == 'FIX') {
                                $amountAffected = $quickPricing['price_value'];
                            }

                            if ($quickPricing['action_type'] == 'INC') {
                                $amountCalculated = $amountCalculated + $amountAffected;
                            }

                            if ($quickPricing['action_type'] == 'DEC') {
                                $amountCalculated = $amountCalculated - $amountAffected;
                            }

                            $insertDataAmount = $amountCalculated;
                        }

                        $insertDataItem = [
                            'property_id' => fillOnUndefined($params, 'property_id', 0),
                            'property_room_id' => fillOnUndefined($roomRate, 'room_id', 0),
                            'room_rate_mapping_id' => $roomRateMappingId,
                            'channel_id' => fillOnUndefined($params, 'channel_id', 0),
                            'availability_type_id' => fillOnUndefined($params, 'availability_type_id', 1),
                            'min_stay' => $insertDataMinStay,
                            'max_stay' => 0,
                            'stop_sell' => $insertDataStopSell,
                            'booking_on_request' => 0,
                            'date' => $startDate->format('Y-m-d'),
                            'amount' => $insertDataAmount,
                            'currency' => $channelData['currency_code'],
                            'status' => 1,
                            'created_by' => fillOnUndefined($params, "user_id", 0),
                            'updated_by' => fillOnUndefined($params, "user_id", 0),
                            'created_at' => Carbon::now()->timestamp,
                            'updated_at' => Carbon::now()->timestamp,
                        ];


                        $checkKey = $roomRate['room_id'] . "|" . $roomRateMappingId . "|" . $startDate->format('Y-m-d') . "|" . $params['channel_id'] . "|" . $params['availability_type_id'];
                        if (isset($oldRoomRatePriceKeys[$checkKey])) {
                            if ($params['update_type'] == 'quick_pricing') {
                                $amountKey = (string)$insertDataAmount;
                                $updateThis[$amountKey][] = $oldRoomRatePriceKeys[$checkKey];
                            } else {
                                $updateThis[] = $oldRoomRatePriceKeys[$checkKey];
                            }

                        } else {
                            $insertData[] = $insertDataItem;
                        }

                        $startDate = $startDate->addDay();
                    }

                }
            }

            if ($updateThis) {

                if ($params['update_type'] == 'quick_pricing') {

                    foreach ($updateThis as $updateDataQuickPricingAmount => $updateIds) {
                        $updateDataCalculated = $updateData;
                        $updateDataCalculated['amount'] = $updateDataQuickPricingAmount;
                        $data = $this->propertyRoomRatePriceRepository->updateWhereIn($updateIds, $updateDataCalculated);
                        if ($data['status'] != 'success') {
                            throw new Exception('api-unknown_error');
                        }
                    }

                } else {

                    $thisUpdateArrayChunk = array_chunk($updateThis, 1000);
                    foreach ($thisUpdateArrayChunk as $updateIds) {
                        $data = $this->propertyRoomRatePriceRepository->updateWhereIn($updateIds, $updateData);
                        if ($data['status'] != 'success') {
                            throw new Exception('api-unknown_error');
                        }
                    }

                }

            }

            if (in_array($params['update_type'], ['rate', 'rate_stop_sell', 'min_stay', 'quick_pricing'])) {
                $thisInsertArrayChunk = array_chunk($insertData, 1000);
                foreach ($thisInsertArrayChunk as $insertThis) {
                    $data = $this->propertyRoomRatePriceRepository->insert($insertThis);
                    if ($data['status'] != 'success') {
                        throw new Exception('api-unknown_error');
                    }
                }
            }

            //Connected Room Case
            $roomRateUpdateForConnectedRoomParams = [
                'quickPricing' => fillOnUndefined($params, 'quickPricing', false),
                'property_id' => $params['property_id'],
                'channel_id' => $params['channel_id'],
                'currency_code' => $channelData['currency_code'],
                'channel_availability_type_id' => $channelData['property_availability_type_id'],
                'startDate' => $params['start_date'],
                'endDate' => $params['end_date'],
                'user_id' => fillOnUndefined($params, "user_id", 0)
            ];

            $this->roomRateUpdateForConnectedRooms($roomRateUpdateForConnectedRoomParams);
            //Connected Room Case

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

        } 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 selectedRoomRateIds($params = [])
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];
        try {

            $roomIds = [];
            $roomRateIds = [];
            $channelIds = [];
            foreach ($params['room_rates'] as $roomRate) {
                $roomIds[] = $roomRate['room_id'];
                foreach ($roomRate['room_rate_mapping_id'] as $roomRateMapping) {
                    $roomRateIds[] = $roomRateMapping;
                }
            }
            $roomIds = array_unique($roomIds);
            $roomRateIds = array_unique($roomRateIds);
            $response = [
                'status' => true,
                'data' => [
                    'room_ids' => $roomIds,
                    'room_rate_mapping_ids' => $roomRateIds
                ],
            ];

        } 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 formElementsToArray($params = [])
    {

        $newMappingArray = [];
        $availabilityArray = [];

        foreach ($params['rates'] as $key => $value) {
            $keyExportArray = explode('_', $key);
            $theDate = Carbon::parse($keyExportArray[4])->format('Y-m-d');
            $keyExportArray[3] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null;
            $channelId = $keyExportArray[3] != 0 ? $params['channel_id'] : null;
            if ($keyExportArray[0] == 'AVA') {
                $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['setup_type_id'] = $keyExportArray[1];
                $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_id'] = $keyExportArray[2];
                $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null;
                $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['date'] = $theDate;
                $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['availability'] = $value;
            } elseif ($keyExportArray[0] == 'PRC') {
                $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1];
                $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2];
                $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3];
                $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate;
                $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['amount'] = $value;
            } elseif ($keyExportArray[0] == 'STS') {
                if ($keyExportArray[3] != null) {
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate;
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['stop_sell'] = $value;
                }

                if ($keyExportArray[3] == null) {
                    $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['setup_type_id'] = $keyExportArray[1];
                    $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_id'] = $keyExportArray[2];
                    $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3] != 0 ? $keyExportArray[3] : null;
                    $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['date'] = $theDate;
                    $availabilityArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $channelId . '|' . $theDate]['stop_sell'] = $value;
                }

            } elseif ($keyExportArray[0] == 'MNS') {
                if ($keyExportArray[3] != null) {
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['setup_type_id'] = $keyExportArray[1];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_id'] = $keyExportArray[2];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['room_rate_mapping_id'] = $keyExportArray[3];
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['date'] = $theDate;
                    $newMappingArray[$keyExportArray[1] . '|' . $keyExportArray[2] . '|' . $keyExportArray[3] . '|' . $theDate]['min_stay'] = $value;
                }
            }

        }

        return ['availability' => $availabilityArray, 'rates' => $newMappingArray];

    }

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

            $newMappingArray = $params ? $params : [];
            $validationResult = $this->propertyRoomRatePriceBulkUpdateValidator->validate($newMappingArray);

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

            $newMappingArray = $newMappingArray['rates'];

            $newMappingCollect = collect($newMappingArray);
            $startDate = $newMappingCollect->min('date');
            $endDate = $newMappingCollect->max('date');

            $propertyRequest = [
                'criteria' => [
                    ['field' => 'id', 'condition' => '=', 'value' => $params['property_id']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => 1

            ];
            $property = $this->propertyRepository->findByCriteria($propertyRequest);

            if (!$property) {
                throw new ApiErrorException(lang('Property not found'));
            }

            // Get Channel Mapping Information
            $criteria = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')],
                ],
                'with' => ['channel'],
                'firstRow' => 1
            ];

            $channelData = $this->propertyChannelMappingRepository->findByCriteria($criteria, ['id', 'property_id', 'channel_id', 'currency_code', 'property_availability_type_id']);
            if (!$channelData) {
                throw new ApiErrorException('Channel mapping not found');
            }

            $oldRoomRatePrice = [];
            if (!empty($newMappingArray)) {
                $oldRoomRatesCriteria = [
                    'criteria' => [
                        ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                        ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']],
                        ['field' => 'date', 'condition' => '>=', 'value' => $startDate],
                        ['field' => 'date', 'condition' => '<=', 'value' => $endDate],
                    ]
                ];

                $oldRoomRatePrice = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria);
            }

            $oldMappingArray = [];
            foreach ($oldRoomRatePrice as $oldPrice) {
                $oldMappingArray[$oldPrice['availability_type_id'] . '|' . $oldPrice['property_room_id'] . '|' . $oldPrice['room_rate_mapping_id'] . '|' . $oldPrice['date']] = [
                    'id' => $oldPrice['id'],
                    'room_id' => $oldPrice['property_room_id'],
                    'room_rate_mapping_id' => $oldPrice['room_rate_mapping_id'],
                    'availability_type_id' => $oldPrice['availability_type_id'],
                    'channel_id' => $oldPrice['channel_id'],
                    'date' => $oldPrice['date'],
                    'amount' => $oldPrice['amount'],
                    'stop_sell' => $oldPrice['stop_sell'],
                    'min_stay' => $oldPrice['min_stay']
                ];
            }

            $deleteThis = [];
            $insertThis = [];


            foreach ($newMappingArray as $key => $newMapping) {

                if (isset($newMapping['amount']) && $newMapping['amount'] === "") {
                    $newMapping['amount'] = 0;
                }

                if (isset($newMapping['amount']) && $newMapping['amount'] === "") {
                    if (isset($oldMappingArray[$key])) {
                        $deleteThis[] = $oldMappingArray[$key]['id'];
                    }
                } else {

                    if (isset($newMappingArray[$key]['amount'])) {
                        $insertAmountData = fillOnUndefined($newMappingArray[$key], 'amount', 0);

                        if ($newMappingArray[$key]['amount'] == '') {
                            $newMappingArray[$key]['amount'] = 0;
                            $insertAmountData = 0;
                        }
                    }


                    if (!isset($newMapping['amount'])) {
                        if (!isset($oldMappingArray[$key])) {
                            $insertAmountData = 0;
                        } else {
                            $insertAmountData = $oldMappingArray[$key]['amount'];
                        }

                    }

                    $insertStopSellData = fillOnUndefined($newMappingArray[$key], 'stop_sell', 0);
                    if (!isset($newMapping['stop_sell'])) {
                        if (isset($oldMappingArray[$key])) {
                            $insertStopSellData = $oldMappingArray[$key]['stop_sell'];
                        } else {
                            $insertStopSellData = 0;
                        }
                    }

                    $insertMinStayData = fillOnUndefined($newMappingArray[$key], 'min_stay', 1);
                    $insertMinStayData = empty($insertMinStayData) ? 1 : $insertMinStayData;
                    if (!isset($newMapping['min_stay'])) {
                        if (isset($oldMappingArray[$key])) {
                            $insertMinStayData = $oldMappingArray[$key]['min_stay'];
                        } else {
                            $insertMinStayData = 1;
                        }
                    }


                    $insertItem = [
                        'property_id' => fillOnUndefined($params, 'property_id', 0),
                        'property_room_id' => fillOnUndefined($newMappingArray[$key], 'room_id', 0),
                        'room_rate_mapping_id' => fillOnUndefinedAndEmpty($newMappingArray[$key], 'room_rate_mapping_id', 0),
                        'channel_id' => fillOnUndefined($params, 'channel_id'),
                        'availability_type_id' => fillOnUndefined($newMappingArray[$key], 'setup_type_id', null),
                        'min_stay' => $insertMinStayData,
                        'max_stay' => fillOnUndefined($newMappingArray[$key], 'max_stay', 0),
                        'stop_sell' => $insertStopSellData,
                        'booking_on_request' => fillOnUndefined($newMappingArray[$key], 'booking_on_request', 0),
                        'date' => fillOnUndefined($newMappingArray[$key], 'date', 0),
                        'amount' => $insertAmountData,
                        'currency' => $channelData['currency_code'],
                        'status' => fillOnUndefined($params, "status", 1),
                        'created_by' => fillOnUndefined($params, "user_id", 0),
                        'updated_by' => fillOnUndefined($params, "user_id", 0),
                        'created_at' => Carbon::now()->timestamp,
                        'updated_at' => Carbon::now()->timestamp,
                    ];

                    if (isset($oldMappingArray[$key])) {
                        if (
                            $oldMappingArray[$key]['amount'] != $insertItem['amount'] || $oldMappingArray[$key]['stop_sell'] != $insertItem['stop_sell'] || $oldMappingArray[$key]['min_stay'] != $insertItem['min_stay']
                        ) {
                            $deleteThis[] = $oldMappingArray[$key]['id'];
                            if ($insertItem['amount'] !== 0) {
                                $insertThis[] = $insertItem;
                            }
                        }
                    } else {
                        $insertThis[] = $insertItem;
                    }

                }

            }

            sort($deleteThis);

            if ($deleteThis) {
                $deleteRoomRatePrices = $this->propertyRoomRatePriceRepository->destroy($deleteThis);
                if ($deleteRoomRatePrices['status'] != 'success') {
                    throw new Exception('api-unknown_error');
                }
            }

            $insertRoomRatePrices = $this->propertyRoomRatePriceRepository->insert($insertThis);
            if ($insertRoomRatePrices['status'] != 'success') {
                throw new Exception('api-unknown_error');
            }


            //Connected Room Case
            $roomRateUpdateForConnectedRoomParams = [
                'quickPricing' => fillOnUndefined($params, 'quickPricing', false),
                'property_id' => $params['property_id'],
                'channel_id' => $params['channel_id'],
                'currency_code' => $channelData['currency_code'],
                'channel_availability_type_id' => $channelData['property_availability_type_id'],
                'startDate' => $startDate,
                'endDate' => $endDate,
                'user_id' => fillOnUndefined($params, "user_id", 0)
            ];


            $this->roomRateUpdateForConnectedRooms($roomRateUpdateForConnectedRoomParams);
            //Connected Room Case

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

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

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

        try {

            if (is_null($params['startDate']) || is_null($params['endDate'])) {
                $response['status'] = true;
                return $response;
            }

            $diffInDays = Carbon::parse($params['startDate'])->diffInDays(Carbon::parse($params['endDate']));

            //Connected Room Case
            $propertyRoomConnectedCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                ],
                'with' => ['roomDetail']
            ];

            $propertyRoomConnected = $this->propertyRoomConnectedRepository->findbyCriteria($propertyRoomConnectedCriteria);
            $propertyRoomConnected = $propertyRoomConnected ? $propertyRoomConnected : [];

            $propertyRoomConnectedGrouped = collect($propertyRoomConnected)->groupBy('room_id')->toArray();


            $propertyRoomRateChannelMappingCriteria = [
                'criteria' => [
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                    ['field' => 'property_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'property_id')],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => fillOnUndefined($params, 'channel_id')],
                ],
                'with' => ['propertyRoomRateMapping', 'propertyRoomRateQuickPricingMapping']
            ];

            $propertyRoomRateChannelMapping = $this->propertyRoomRateChannelMappingRepository->findByCriteria($propertyRoomRateChannelMappingCriteria, ['*']);
            $propertyRoomRateChannelMappingCollect = collect($propertyRoomRateChannelMapping);

            $oldRoomRatesCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $params['property_id']],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $params['channel_id']],
                    ['field' => 'date', 'condition' => '>=', 'value' => $params['startDate']],
                    ['field' => 'date', 'condition' => '<=', 'value' => $params['endDate']],
                ],
                'with' => ['roomRateMapping']
            ];

            $oldRoomRatePriceUpdated = $this->propertyRoomRatePriceRepository->findbyCriteria($oldRoomRatesCriteria);
            $oldRoomRatePriceUpdated = $oldRoomRatePriceUpdated ? collect($oldRoomRatePriceUpdated) : [];


            foreach ($propertyRoomConnectedGrouped as $connectedRoomId => $connectedRooms) {

                $roomDetail = reset($connectedRooms)['room_detail'];

                if ($roomDetail['is_connected_room_price'] != 1) {
                    continue;
                }

                //güncellenmesi gereken id bulalım
                $connectedRoomIds = pickItemFromArray('connected_room_id', $connectedRooms);

                $availableRoomRates = $propertyRoomRateChannelMappingCollect->where('property_room_rate_mapping.room_id', $connectedRoomId);
                $availableRoomRates = $availableRoomRates ? $availableRoomRates->toArray() : [];

                foreach ($availableRoomRates as $availableRoomRate) {


                    for ($i = 0; $i <= $diffInDays; $i++) {

                        $date = Carbon::parse($params['startDate'])->addDays($i)->toDateString();

                        $issetCurrentRoomRatePrice = $oldRoomRatePriceUpdated->where('availability_type_id', $params['channel_availability_type_id'])
                            ->where('property_room_id', $connectedRoomId)
                            ->where('room_rate_mapping_id', $availableRoomRate['room_rate_mapping_id'])
                            ->where('date', $date)->first();

                        $roomPriceFromConnectedRooms = $oldRoomRatePriceUpdated
                            ->where('room_rate_mapping.room_rate_id', $availableRoomRate['property_room_rate_mapping']['room_rate_id'])
                            ->whereIn('property_room_id', $connectedRoomIds)
                            ->where('date', $date)
                            ->sum('amount');


                        if (isset($params['quickPricing']) && $params['quickPricing']) {

                            $amount = $roomPriceFromConnectedRooms;
                            $amountCalculated = $roomPriceFromConnectedRooms;

                            $quickPricing = $availableRoomRate['property_room_rate_quick_pricing_mapping'];

                            $amountAffected = 0;
                            if ($quickPricing['price_type'] == 'PER') {
                                $amountAffected = ($amount * $quickPricing['price_value']) / 100;
                            } elseif ($quickPricing['price_type'] == 'FIX') {
                                $amountAffected = $quickPricing['price_value'];
                            }

                            if ($quickPricing['action_type'] == 'INC') {
                                $amountCalculated = $amountCalculated + $amountAffected;
                            }

                            if ($quickPricing['action_type'] == 'DEC') {
                                $amountCalculated = $amountCalculated - $amountAffected;
                            }

                            $roomPriceFromConnectedRooms = $amountCalculated;

                        }


                        //update
                        if (!empty($issetCurrentRoomRatePrice)) {

                            $this->propertyRoomRatePriceRepository->update($issetCurrentRoomRatePrice['id'], ['amount' => $roomPriceFromConnectedRooms]);

                        } else {

                            $insertItem = [
                                'property_id' => $params['property_id'],
                                'property_room_id' => $availableRoomRate['property_room_rate_mapping']['room_id'],
                                'room_rate_mapping_id' => $availableRoomRate['property_room_rate_mapping']['id'],
                                'channel_id' => $params['channel_id'],
                                'availability_type_id' => $params['channel_availability_type_id'],
                                'min_stay' => 0,
                                'max_stay' => 0,
                                'stop_sell' => 0,
                                'booking_on_request' => 0,
                                'date' => $date,
                                'amount' => $roomPriceFromConnectedRooms,
                                'currency' => $params['currency_code'],
                                'status' => 1,
                                'created_by' => fillOnUndefined($params, "user_id", 0),
                                'updated_by' => fillOnUndefined($params, "user_id", 0),
                                'created_at' => Carbon::now()->timestamp,
                                'updated_at' => Carbon::now()->timestamp,
                            ];

                            $this->propertyRoomRatePriceRepository->insert($insertItem);

                        }


                    }


                }


            }
            //Connected Room Case


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


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

}
