<?php

namespace App\Console\Commands\ChannelManager;

use App\Core\Mail\LogMail;
use App\Core\Service\ChannelManager\Mirai;
use App\Exceptions\ApiErrorException;
use App\Models\ChannelManagerPropertyMapping;
use App\Models\PropertyMeta;
use App\Models\PropertyMetaRoomRate;
use App\Models\PropertyMetaRoomRateMapping;
use App\Models\PropertyMetaRoomRatePrice;
use App\Models\PropertyRoomAvailability;
use App\Models\PropertyRoomRatePrice;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
use Illuminate\Mail\Mailer;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class PropertyMetaRoomRatePushService extends Command
{
    protected $signature = 'cron:propertymeta-roomratepush-service {--property_id=}';

    protected $description = '';
    protected $mailer;

    public function __construct(
        Mailer $mailer,
        Mirai $miraiService
    )
    {
        parent::__construct();
        $this->mailer = $mailer;
        $this->miraiService = $miraiService;
    }

    function getMonthlyPeriod($endDate)
    {
        $diffInMonths = Carbon::parse()->floorMonth()->diffInMonths(Carbon::parse($endDate)->floorMonth());
        $diffInMonths++;

        $monthlyPeriod = [];
        $startDate = null;
        $finishDate = null;
        for ($i = 0; $i < $diffInMonths; $i++) {

            if (empty($startDate)) {
                $startDate = Carbon::now()->toDateString();
                $finishDate = Carbon::parse($startDate)->endOfMonth()->toDateString();
            } else {
                $startDate = Carbon::now()->addMonths($i)->startOfMonth()->toDateString();
                $finishDate = Carbon::parse($startDate)->endOfMonth()->toDateString();
            }

            if ($finishDate > $endDate) {
                $finishDate = $endDate;
            }

            $monthlyPeriod[] = [
                'startDate' => $startDate,
                'finishDate' => $finishDate,
            ];

        }

        return $monthlyPeriod;

    }

    public function handle()
    {

        $this->info(date('Y-m-d H:i:s') . ' : Start');


        if (!is_null($this->option('property_id'))) {
            $propertyMeta = PropertyMeta::where('status', 1)->where('property_id', $this->option('property_id'))->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'param', 'status'])->toArray();
        } else {
            $propertyMeta = PropertyMeta::where('status', 1)->with('property')->orderBy('id', 'ASC')->get(['id', 'property_id', 'cancellation_policy', 'adult_policy', 'child_policy', 'param', 'status'])->toArray();
        }


        foreach ($propertyMeta as $property) {


            $propertyId = $property['property_id'];

            //Mirai User Setup
            $this->miraiService->setupUser($property['paramsArray']['login'], $property['paramsArray']['password']);

            $this->info(date('Y-m-d H:i:s') . ' : Property Room Rate Push Start: ' . $property['property']['name']);

            //$propertyRoomRatePriceEndDate = PropertyRoomRatePrice::where('property_id', $propertyId)->orderBy('date', 'DESC')->first();
            //$propertyRoomRatePriceEndDate = $propertyRoomRatePriceEndDate->toArray() ? $propertyRoomRatePriceEndDate['date'] : null;
            //$propertyRoomRatePriceEndDate = '2023-10-31';

            //$getMonthlyPeriod = $this->getMonthlyPeriod($propertyRoomRatePriceEndDate);
            $getMonthlyPeriod = [];
            $getMonthlyPeriod[] = [
                'startDate' => Carbon::now()->toDateString(),
                //'finishDate' => Carbon::now()->addYear()->toDateString(),
                'finishDate' => Carbon::now()->addDays(360)->toDateString(),
            ];

            $propertyMetaRoomRateMapping = PropertyMetaRoomRateMapping::where('property_id', $propertyId)->with('propertyMetaRoomRate')->get()->toArray();
            $propertyMetaRoomRateMappingCollect = collect($propertyMetaRoomRateMapping);

            $channelManagerPropertyMapping = ChannelManagerPropertyMapping::where('property_id', $propertyId)->where('status', 1)->where('channel_manager_id', 7)->first()->toArray();

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

            $metaRoomRateMapping = [];
            $propertyMetaRoomRateMappingGroup = $propertyMetaRoomRateMappingCollect->groupBy('property_meta_room_rate.room_id')->toArray();
            foreach ($propertyMetaRoomRateMappingGroup as $roomGroup) {
                $roomGroup = reset($roomGroup);
                $metaRoomRateMapping[$roomGroup['property_meta_room_rate']['room_id']]['meta_code'] = $roomGroup['meta_code'];
            }


            foreach ($getMonthlyPeriod as $period) {

                $this->info(date('Y-m-d H:i:s') . ' : Meta Availability Start: ' . $property['property']['name'] . ' - ' . $period['startDate'] . ' / ' . $period['finishDate']);

                try {


                    $propertyRoomAvailability = PropertyRoomAvailability::where('property_id', $propertyId)
                        ->where('status', 1)->where('channel_id', null)
                        ->whereBetween('date', [$period['startDate'], $period['finishDate']])
                        ->get(['property_room_id', 'date', 'stop_sell', 'availability'])
                        ->toArray();
                    $propertyRoomAvailabilityCollect = collect($propertyRoomAvailability);


                    foreach ($metaRoomRateMapping as $roomId => $metaRoom) {
                        $metaRoomRateMapping[$roomId]['data'] = $propertyRoomAvailabilityCollect->where('property_room_id', $roomId)->sortBy('date')->toArray();
                    }


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

                    $xmlResponse->addAttribute('hotelId', $channelManagerPropertyMapping['channel_manager_property_id']);

                    foreach ($metaRoomRateMapping as $roomId => $metaRoom) {

                        $room = $xmlResponse->addChild('room');
                        $room->addAttribute('id', $metaRoom['meta_code']);

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

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

                            $roomRate['availability'] = $roomRate['stop_sell'] == 1 ? 0 : $roomRate['availability'];

                            $roomInventoryAvailability->addAttribute('date', $roomRate['date']);
                            $roomInventoryAvailability->addAttribute('quantity', $roomRate['availability']);

                        }

                    }


                    $request = $this->miraiService->inventoryRoomRateUpdate('webservice_updater.apro', $xmlResponse->asXML());

                    if (!$request['status']) {
                        throw new ApiErrorException('webservice_updater AVAILABILITY Error: ' . $request['message']);
                    }

                } catch (ApiErrorException $e) {
                    $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage());
                } catch (Exception $e) {
                    $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
                    Log::error($message);
                    $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message);
                }


                $this->info(date('Y-m-d H:i:s') . ' : Meta Price Start: ' . $property['property']['name'] . ' - ' . $period['startDate'] . ' / ' . $period['finishDate']);

                try {


                    $propertyMetaRoomRatePrice = PropertyMetaRoomRatePrice::where('property_id', $propertyId)
                        ->whereBetween('date', [$period['startDate'], $period['finishDate']])->where('status', 1)
                        ->whereIn('code', pickItemFromArray('code', $propertyMetaRoomRateMapping))
                        ->get(['date', 'code', 'amount', 'currency'])
                        ->toArray();

                    $propertyMetaRoomRatePriceCollect = collect($propertyMetaRoomRatePrice);

                    if (empty($propertyMetaRoomRatePrice)) {
                        $this->error(date('Y-m-d H:i:s') . ' : ' . $property['property']['name'] . ' : Price not found!');
                        continue;
                    }


                    $metaRoomRatePrice = [];
                    $metaRoomPricesOrdered = [];
                    foreach ($propertyMetaRoomRateMapping as $metaRoom) {

                        $metaRoomPrices = $propertyMetaRoomRatePriceCollect->where('code', $metaRoom['code'])->sortBy('date')->toArray();
                        $metaRoomPricesCollect = collect($metaRoomPrices);

                        $metaRoomPricesGroup = [];
                        foreach ($metaRoomPrices as $metaRoomPrice) {
                            $metaRoomPricesGroup[md5($metaRoomPrice['amount'])][$metaRoomPrice['date']] = $metaRoomPrice;
                            ksort($metaRoomPricesGroup[md5($metaRoomPrice['amount'])]);
                        }

                        foreach ($metaRoomPricesGroup as $priceGroupKey => $priceGroup) {

                            $priceDateGroup = 0;
                            foreach ($priceGroup as $priceGroupDate => $price) {

                                if (!isset($metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'])) {
                                    $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'] = $price['date'];
                                    $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date'];
                                } else {

                                    if (Carbon::parse($metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'])->addDay()->toDateString() != $price['date']) {

                                        $priceDateGroup++;
                                        $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['startDate'] = $price['date'];

                                    } else {
                                        $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date'];
                                    }

                                    $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['finishDate'] = $price['date'];

                                }

                                $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['amount'] = $price['amount'];
                                $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['currency'] = $price['currency'];
                                //$metaRoomPricesOrdered[$priceGroupKey][$priceDateGroup]['metaCode'] = $metaRoom['meta_code'];

                                $metaRoomPricesOrdered[$metaRoom['meta_code']][$priceGroupKey][$priceDateGroup]['title'] = $metaRoom['property_meta_room_rate']['title'];

                            }
                        }

                    }

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

                    $xmlResponse->addAttribute('hotelId', $channelManagerPropertyMapping['channel_manager_property_id']);

                    foreach ($metaRoomPricesOrdered as $roomRateMetaCode => $metaRoom) {

                        foreach ($metaRoom as $priceGroupKey => $metaRoomPriceGroup) {

                            $room = $xmlResponse->addChild('room');

                            $room->addAttribute('id', $roomRateMetaCode);

                            $currency = !empty($metaRoomPriceGroup) ? reset($metaRoomPriceGroup)['currency'] : 'EUR';

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

                            foreach ($metaRoomPriceGroup as $metaRoomPrice) {
                                $planning = $rate->addChild('planning');

                                $planning->addAttribute('from', $metaRoomPrice['startDate']);
                                $planning->addAttribute('to', $metaRoomPrice['finishDate']);
                                $planning->addAttribute('unitPrice', $metaRoomPrice['amount']);

                            }
                        }

                    }

                    $request = $this->miraiService->inventoryRoomRateUpdate('webservice_updater.apro', $xmlResponse->asXML());

                    if (!$request['status']) {
                        throw new ApiErrorException('webservice_updater PRICE Error: ' . $request['message']);
                    }

                    //Remove all prices then push to meta
                    PropertyMetaRoomRatePrice::where('property_id', $propertyId)->delete();


                } catch (ApiErrorException $e) {
                    $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $e->getMessage());
                } catch (Exception $e) {
                    $message = $e->getFile() . " " . $e->getLine() . " " . $e->getMessage();
                    Log::error($message);
                    $this->error(date('Y-m-d H:i:s') . ' : Error: ' . $message);
                }


            }


        }


        $this->info(date('Y-m-d H:i:s') . ' : Finished');


    }
}
