<?php


namespace App\Http\Controllers\ChannelManager\HyperGuest\v1;


use App\Core\Mail\LogMail;


use App\Core\Service\BookingService;
use App\Core\Service\ChannelManagerLogService;
use App\Core\Service\ChannelManagerPropertyMappingService;
use App\Core\Service\PropertyChannelMappingService;
use App\Core\Service\PropertyChannelService;
use App\Core\Service\PropertyRoomAvailabilityService;
use App\Core\Service\PropertyRoomRateChannelMappingService;
use App\Core\Service\PropertyRoomRatePriceService;
use App\Core\Service\PropertyRoomService;
use App\Exceptions\ApiErrorException;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Mail\Mailer;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Exception;


class HyperGuestController
{

    private $channelManagerId;
    private $channelManagerName;
    private $mailer;
    private $userApiKey;

    public function __construct(
        Request $request,
        Client $restClient,
        Mailer $mailer,
        PropertyChannelService $propertyChannelService,
        PropertyChannelMappingService $propertyChannelMappingService,
        PropertyRoomRateChannelMappingService $propertyRoomRateChannelMappingService,
        PropertyRoomRatePriceService $propertyRoomRatePriceService,
        PropertyRoomAvailabilityService $propertyRoomAvailabilityService,
        PropertyRoomService $propertyRoomService,
        BookingService $bookingService,
        ChannelManagerPropertyMappingService $channelManagerPropertyMappingService,
        ChannelManagerLogService $channelManagerLogService
    )
    {
        $this->request = $request;
        $this->restClient = $restClient;
        $this->mailer = $mailer;
        $this->param = $this->request->all();

        $this->propertyChannelService = $propertyChannelService;
        $this->propertyChannelMappingService = $propertyChannelMappingService;
        $this->propertyRoomRateChannelMappingService = $propertyRoomRateChannelMappingService;
        $this->propertyRoomRatePriceService = $propertyRoomRatePriceService;
        $this->propertyRoomAvailabilityService = $propertyRoomAvailabilityService;
        $this->propertyRoomService = $propertyRoomService;
        $this->bookingService = $bookingService;
        $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService;
        $this->channelManagerLogService = $channelManagerLogService;

        if (App::environment() == 'production') {
            $this->url = 'https://pdm.hyperguest.io/api/';
            $this->urlstatic = 'https://hg-static.hyperguest.com/';
            $this->bearerToken = '63d51938dc3749788ac3e88ea6d58950';
        } else {
            $this->url = 'https://pdm.hyperguest.com/api/';
            $this->urlstatic = 'https://hg-static.hyperguest.com/';
            $this->bearerToken = '63d51938dc3749788ac3e88ea6d58950';
        }


        $this->channelId = 1;
        $this->channelManagerId = 10;
        $this->channelManagerName = 'HyperGuest';

        $getRequestUri = $request->getRequestUri();
        $getRequestUriExplode = explode('/', $getRequestUri);
        $serviceRequestName = last($getRequestUriExplode);

        //channelManagerLogService
        $logArray = ['room-inventory-update'];
        $this->channelManagerLogId = null;
        $this->channelManagerRequestTime = microtime(true);
        if (in_array($serviceRequestName, $logArray)) {

            $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($this->param['propertyId']);

            if ($channelManagerPropertyCheck['status']) {
                $insertDataLog = [
                    'property_id' => $channelManagerPropertyCheck['data']['property_id'],
                    'channel_manager_id' => $this->channelManagerId,
                    'type' => 'C2E',
                    'service' => $serviceRequestName,
                    'request' => json_encode($this->param),
                    'response' => null,
                    'ip_address' => $this->request->ip(),
                    'status' => null
                ];

                $channelManagerLog = $this->channelManagerLogService->create($insertDataLog);
                if ($channelManagerLog['status'] == 'success') {
                    $this->channelManagerLogId = $channelManagerLog['data']['id'];
                }
            }

        }
        //channelManagerLogService

    }

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

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

            if ($type == 'POST') {

                $parameter = [
                    'headers' => [
                        'Content-Type' => 'application/json',
                        'Authorization' => 'Bearer ' . $this->bearerToken
                    ]
                ];

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

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

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

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


                $res = $this->restClient->request('GET', $this->url . $method,
                    [
                        'headers' => [
                            'Content-Type' => 'application/json',
                            'Authorization' => 'Bearer ' . $this->bearerToken
                        ],
                        'query' => $jsonPayload
                    ]
                );

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


            }

            if ($res->getStatusCode() != 200) {
                $errors = singleElementArray($getResponse['errors']);
                $firstError = reset($errors);
                throw new Exception($firstError['code'] . ': ' . $firstError['title']);
            }


            $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, 'data', [])];


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


        return $response;

    }

    public function responseError($errors)
    {

        $response = ['success' => false, 'message' => null];
        $response['message'] = implode(',', $errors);


        //channelManagerLogService
        if (!is_null($this->channelManagerLogId)) {
            $updateDataLog = [
                'response' => json_encode($response),
                'status' => 0
            ];

            if (!is_null($this->channelManagerRequestTime)) {
                $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime;
            }

            $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog);
        }
        //channelManagerLogService


        return response()->json($response);
    }

    public function responseSuccess($data = [])
    {

        $response = ['success' => true, 'message' => null];

        //channelManagerLogService
        if (!is_null($this->channelManagerLogId)) {
            $updateDataLog = [
                'response' => json_encode($response),
                'status' => 1
            ];

            if (!is_null($this->channelManagerRequestTime)) {
                $updateDataLog['response_time'] = microtime(true) - $this->channelManagerRequestTime;
            }

            $channelManagerLog = $this->channelManagerLogService->update($this->channelManagerLogId, $updateDataLog);
        }
        //channelManagerLogService

        return response()->json($response);
    }

    public function propertyChannelMapping($propertyId)
    {

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

        try {

            $propertyChannelMappingCriteria = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'firstRow' => true
            ];


            $propertyChannelMapping = $this->propertyChannelMappingService->select($propertyChannelMappingCriteria);

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

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


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

        return $response;

    }

    public function channelPropertyRoomRate($propertyId)
    {

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

        try {

            $requestParam = [
                'criteria' => [
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $this->channelId],
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'with' => [
                    'propertyRoomRateMapping.propertyRoomRate.propertyRoomRateAccommodation',
                    'propertyRoomRateMapping.propertyRoom.propertyRoomType',
                ]
            ];

            $getChannelPropertyRoomRate = $this->propertyRoomRateChannelMappingService->select($requestParam);

            if ($getChannelPropertyRoomRate['status'] != 'success' || empty($getChannelPropertyRoomRate['data'])) {
                throw new ApiErrorException('Property Room Rate not found');
            }

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


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

        return $response;

    }

    public function channelManagerPropertyCheck($propertyId)
    {

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

        try {

            $requestParam =
                [
                    'criteria' =>
                        [
                            ['field' => 'status', 'condition' => '=', 'value' => 1],
                            ['field' => 'channel_manager_property_id', 'condition' => '=', 'value' => $propertyId],
                            ['field' => 'channel_manager_id', 'condition' => '=', 'value' => $this->channelManagerId],
                        ],
                    'with' => ['channelManagerRoomRate.propertyRoomRateMapping'],
                    'firstRow' => true
                ];

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

            if ($channelManagerPropertyMapping['status'] != 'success' || empty($channelManagerPropertyMapping['data'])) {
                throw new ApiErrorException('Property Room Rate not found');
            }

            $channelManagerPropertyMapping = $channelManagerPropertyMapping['data'];

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


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

        return $response;

    }

    public function availabilityRateUpdate(Request $request)
    {

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

        try {


            $channelManagerPropertyCheck = $this->channelManagerPropertyCheck($this->param['propertyId']);
            if (!$channelManagerPropertyCheck['status']) {
                throw new ApiErrorException($channelManagerPropertyCheck['message']);
            }

            $channelManagerPropertyCheck = $channelManagerPropertyCheck['data'];
            $channelManagerPropertyRoomRateCollect = collect($channelManagerPropertyCheck['channel_manager_room_rate']);

            $propertyId = $channelManagerPropertyCheck['property_id'];

            $channelPropertyRoomRate = $this->channelPropertyRoomRate($propertyId);
            if (!$channelPropertyRoomRate['status']) {
                throw new ApiErrorException($channelPropertyRoomRate['message']);
            }

            $channelPropertyRoomRate = $channelPropertyRoomRate['data'];
            $channelPropertyRoomRateCollect = collect($channelPropertyRoomRate);


            $propertyChannelMapping = $this->propertyChannelMapping($propertyId);
            if (!$propertyChannelMapping['status']) {
                throw new ApiErrorException($propertyChannelMapping['message']);
            }


            $propertyChannelMapping = $propertyChannelMapping['data'];

            $availRateUpdates = $this->param['ARIUpdate'];

            DB::beginTransaction();

            /*$dateRange = [];
            $dateRange['startDate'] = $this->param['start_date'];
            $dateRange['finishDate'] = $this->param['end_date'];

            if (Carbon::parse($dateRange['startDate'])->isBefore(Carbon::now()->toDateString()) || Carbon::parse($dateRange['finishDate'])->isBefore(Carbon::now()->toDateString())) {
                throw new ApiErrorException('Dates to be updated cannot be earlier than today');
            }*/

            foreach ($availRateUpdates as $availRateUpdate) {

                $roomIdCheck = $channelManagerPropertyRoomRateCollect->where('channel_manager_room_id', $availRateUpdate['roomTypeCode'])->first();
                if (empty($roomIdCheck)) {
                    continue;
                }

                $roomId = $roomIdCheck['property_room_rate_mapping']['room_id'];
                $roomCheck = $channelPropertyRoomRateCollect->where('property_room_rate_mapping.room_id', $roomId)->isEmpty();
                if ($roomCheck) {
                    continue;
                }

                $totalInventoryAvailable = null;
                if (isset($availRateUpdate['numberOfAvailableRooms'])) {
                    $totalInventoryAvailable = $availRateUpdate['numberOfAvailableRooms'];
                }


                $currentDate = Carbon::parse($availRateUpdate['date'])->toDateString();

                $requestParamBase = [
                    'property_id' => fillOnUndefined($propertyChannelMapping, 'property_id'),
                    'channel_id' => fillOnUndefined($propertyChannelMapping, 'channel_id'),
                    'availability_type_id' => fillOnUndefined($propertyChannelMapping, 'property_availability_type_id', 1),
                    'start_date' => $currentDate,
                    'end_date' => $currentDate,
                    'include_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
                ];


                if (!is_null($totalInventoryAvailable)) {

                    $requestParams = [];
                    $requestParams = $requestParamBase;
                    $requestParams['update_type'] = 'availability';
                    $requestParams['value'] = $totalInventoryAvailable;
                    $requestParams['room_rates'] = [
                        ['room_id' => $roomId]
                    ];

                    $propertyRoomRateMapping = $this->propertyRoomAvailabilityService->bulkUpdate($requestParams);

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

                }


                foreach ($availRateUpdate['ratePlans'] as $ratePlan) {


                    $roomRateMappingIdCheck = $channelManagerPropertyRoomRateCollect->where('channel_manager_room_id', $availRateUpdate['roomTypeCode'])->where('channel_manager_room_rate_id', $ratePlan['ratePlanCode'])->first();
                    if (empty($roomRateMappingIdCheck)) {
                        continue;
                    }

                    $roomRateMappingId = $roomRateMappingIdCheck['property_room_rate_mapping_id'];

                    $channelRoomRateMappingCheck = $channelPropertyRoomRateCollect->where('room_rate_mapping_id', $roomRateMappingId)->isEmpty();
                    if ($channelRoomRateMappingCheck) {
                        continue;
                    }


                    $includedOccupancy = (int)$roomRateMappingIdCheck['included_occupancy'];
                    $ratePlanPrice = collect($ratePlan['prices'])->where('numberOfGuests.adults', $includedOccupancy)
                        ->where('numberOfGuests.adults', $includedOccupancy)
                        ->where('numberOfGuests.children', 0)
                        ->where('numberOfGuests.infants', 0)
                        ->first();


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

                    $ratePlanPrice = $ratePlanPrice['price'];

                    $roomRates = [];
                    $roomRates[] = [
                        'room_id' => $roomId,
                        'room_rate_mapping_id' => [
                            $roomRateMappingId
                        ]
                    ];


                    $paramsListChannel = [];
                    $paramsListChannel[] = $this->channelId;

                    //CONNECTED CHANNEL RATE UPDATE
                    $propertyChannelMappingConnectedCriteria = [
                        'criteria' => [
                            ['field' => 'property_id', 'condition' => '=', 'value' => $requestParamBase['property_id']],
                            ['field' => 'connected_channel_id', 'condition' => '=', 'value' => $requestParamBase['channel_id']],
                            ['field' => 'channel_id', 'condition' => '=', 'value' => 5],//JUST CM
                            ['field' => 'status', 'condition' => '=', 'value' => 1],
                        ],
                        'with' => ['channel']
                    ];

                    $propertyChannelMappingConnected = $this->propertyChannelMappingService->select($propertyChannelMappingConnectedCriteria);
                    if ($propertyChannelMappingConnected['status'] == 'success') {

                        foreach ($propertyChannelMappingConnected['data'] as $channel) {

                            if (!is_null($channel['channel']['parent_id'])) {
                                continue;
                            }

                            $paramsListChannel[] = $channel['channel_id'];

                        }

                    }
                    //CONNECTED CHANNEL RATE UPDATE


                    foreach ($paramsListChannel as $paramChannelId) {

                        //Eğer Rate var ise currency check yapılmalı ve PerDay var mı check edilmeli, burada sonra günceleme yaptırılabilri
                        if (isset($ratePlanPrice)) {

                            $requestParams = [];
                            $requestParams = $requestParamBase;
                            $requestParams['update_type'] = 'rate';
                            $requestParams['value'] = $ratePlanPrice;
                            $requestParams['room_rates'] = $roomRates;

                            $requestParams['channel_id'] = $paramChannelId;

                            $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams);

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

                        }


                        //Room Rate Stop Sale
                        $isCloseRoomRateMappingSale = null;
                        if (isset($ratePlan['isOpen'])) {
                            $isCloseRoomRateMappingSale = $ratePlan['isOpen'] == 1 ? false : true;
                        }

                        if (!is_null($isCloseRoomRateMappingSale)) {
                            $requestParams = [];
                            $requestParams = $requestParamBase;
                            $requestParams['update_type'] = 'rate_stop_sell';
                            $requestParams['value'] = $isCloseRoomRateMappingSale ? 1 : 0;
                            $requestParams['room_rates'] = $roomRates;

                            $requestParams['channel_id'] = $paramChannelId;

                            $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams);

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


                        //Minimum Konaklama Gün Sayısı
                        if (isset($ratePlan['minLOS'])) {

                            $minStay = $ratePlan['minLOS'];

                            $requestParams = [];
                            $requestParams = $requestParamBase;
                            $requestParams['update_type'] = 'min_stay';
                            $requestParams['value'] = $minStay;
                            $requestParams['room_rates'] = $roomRates;

                            $requestParams['channel_id'] = $paramChannelId;

                            $propertyRoomRateMapping = $this->propertyRoomRatePriceService->bulkUpdate($requestParams);

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


                        }


                    }


                }


            }

            DB::commit();


            //$this->channelManagerLogId

            return $this->responseSuccess();

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

        DB::rollBack();

        if (!$response['status']) {
            $errors[] = $response['message'];

            return $this->responseError($errors);

        }

    }


}
