<?php


namespace App\Core\Service\ChannelManager;


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

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


class _Mirai
{

    private $channelManagerId;
    private $channelManagerName;
    private $mailer;
    private $login;
    private $password;
    private $url;

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

        if (App::environment() == 'production') {
            $this->login = 'ExtranetWorktest-xml-100378347';
            $this->password = 'ifXHs3EJc-$>';
            $this->url = 'https://api.mirai.com/XMLIntegrationx/';
        } else {
            $this->login = 'ExtranetWorktest-xml-100378347';
            $this->password = 'ifXHs3EJc-$>';
            $this->url = 'https://api.mirai.com/XMLIntegrationx/';
        }

        $this->channelManagerId = 7;
        $this->channelManagerName = 'Mirai';
        $this->channelManagerPropertyMappingService = $channelManagerPropertyMappingService;
        $this->channelManagerMappingService = $channelManagerMappingService;

    }

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

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

            $formAuthParam = [
                'login' => $this->login,
                'password' => $this->password,
            ];

            $parameter = [
                'headers' => [],
                'query' => $formAuthParam,
            ];

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

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

            $getResponseBody = $request->getBody();
            $getResponse = $getResponseBody->getContents();
            $getResponseXml = new \SimpleXMLElement($getResponse);
            $getResponse = json_decode(json_encode($getResponseXml), 1);

            if (isset($getResponse['error'])) {
                $errorMessage = is_array($getResponse['error']['comment']) ? implode(',', $getResponse['error']['comment']) : $getResponse['error']['comment'];
                throw new ApiErrorException($errorMessage);
            }


            if ($request->getStatusCode() != 200) {
                $firstError = is_array($getResponse['error']['comment']) ? implode(',', $getResponse['error']['comment']) : $getResponse['error']['comment'];

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

                throw new ApiErrorException($firstError);
            }

            $response = ["status" => true, 'message' => '', "data" => fillOnUndefined($getResponse, '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 $response;

    }


    public function inventoryRoomRateUpdate($method, $xmlPayload)
    {
        $response = ['status' => false, 'message' => ''];

        try {


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

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

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

    /** Pattern Functions */

    public function availabilityUpdateRequestMultiParam($propertyId, $propertyRoomRateMapping, $params)
    {

        $idList = [];

        $requestParam['values'] = [];

        ksort($params);

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

                $roomIdManipulate = $roomId;
                $roomIdManipulateCheck = $propertyRoomRateMappingCollect->where('channel_manager_room_id', $roomId)->where('property_room_rate_mapping.status', 1)->first();
                if (!empty($roomIdManipulateCheck)) {
                    $roomIdManipulate = $roomIdManipulateCheck['channel_manager_room_rate_id'];
                }

                $dataByRoomDate[$roomIdManipulate][$uniqueKey][$currentDate] = $value;
            }
        }

        $requestParam = [];

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

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

                $uniqueValueDate = array_keys($uniqueValues);

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

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

                $currentValue = reset($uniqueValues);


                foreach ($dateGroup as $dates) {

                    if (count($dates) > 1) {

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

                    } else {

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

                    }

                    $valueKey++;

                }


            }
        }


        $xmlResponse = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><inventoryUpdate></inventoryUpdate>');

        $xmlResponse->addAttribute('hotelId', $propertyId);

        foreach ($requestParam as $roomId => $roomData) {

            $room = $xmlResponse->addChild('room');
            $room->addAttribute('id', $roomId);

            $roomInventory = $room->addChild('inventory');

            foreach ($roomData['data'] as $roomRate) {
                $roomInventoryAvailability = $roomInventory->addChild('availability');

                $roomInventoryAvailability->addAttribute('from', $roomRate['date_from']);
                $roomInventoryAvailability->addAttribute('to', $roomRate['date_to']);
                $roomInventoryAvailability->addAttribute('quantity', $roomRate['availability']);

            }

        }

        return [
            'idList' => $idList,
            'payload' => $xmlResponse->asXML()
        ];
    }

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


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

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

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

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

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


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


            try {

                foreach ($roomAvailabilityQueue as $roomAvailability) {


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

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

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

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

                        continue;

                    }

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

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

                        continue;
                    }


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


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

                }

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

                if ($request['status']) {

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

                } else {

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

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

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


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


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


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

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

        return $response;
    }


    public function roomRateUpdateRequestMultiParam($propertyId, $params)
    {

        $idList = [];
        ksort($params);

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

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

        $requestParam = [];

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

            $valueKey = 0;
            foreach ($rates as $uniqueKey => $uniqueValues) {


                $uniqueValueDate = array_keys($uniqueValues);

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

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

                $currentValue = reset($uniqueValues);

                foreach ($dateGroup as $dates) {

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

                    if ($requestParam[$rateId][$valueKey]['amount'] == 0) {
                        $requestParam[$rateId][$valueKey]['stop_sell'] = 1;
                    }

                    $valueKey++;

                }

            }
        }


        $xmlResponse = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><inventoryUpdate></inventoryUpdate>');

        $xmlResponse->addAttribute('hotelId', $propertyId);

        foreach ($requestParam as $roomId => $roomData) {

            $room = $xmlResponse->addChild('room');
            $room->addAttribute('id', $roomId);

            $currencyCode = reset($roomData)['currency'];

            $roomRate = $room->addChild('rate');
            $roomRate->addAttribute('currency', $currencyCode);

            foreach ($roomData as $roomRateData) {

                $roomRatePlaning = $roomRate->addChild('planning');

                $roomRatePlaning->addAttribute('from', $roomRateData['date_from']);
                $roomRatePlaning->addAttribute('to', $roomRateData['date_to']);
                $roomRatePlaning->addAttribute('unitPrice', $roomRateData['stop_sell'] == 1 ? 0 : $roomRateData['amount']);
                $roomRatePlaning->addAttribute('minimumStay', $roomRateData['min_stay']);

            }

        }

        return [
            'idList' => $idList,
            'payload' => $xmlResponse->asXML()
        ];

    }


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

        try {

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


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

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

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

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


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


                foreach ($roomRatePriceQueue as $roomRatePrice) {


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

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

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

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

                        continue;

                    }

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

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

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

                        continue;
                    }


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


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

                }


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


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


                    $roomRateUpdateRequestMultiParam = $this->roomRateUpdateRequestMultiParam($channelManagerPropertyId, $channelManagerProperty);
                    $request = $this->inventoryRoomRateUpdate('webservice_updater.apro', $roomRateUpdateRequestMultiParam['payload']);

                    if ($request['status']) {

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

                    } else {

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

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

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

                }

            }

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

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

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

        return $response;
    }


}
