<?php


namespace App\Core\Service;


use App\Core\Repository\Booking\BookingRepository;
use App\Core\Repository\Property\PropertyRepository;
use App\Core\Repository\PropertySummary\PropertySummaryRepository;
use App\Exceptions\ApiErrorException;
use App\Models\CurrencyRates;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class PropertySummaryService
{
    private $countryRepository;

    public function __construct(
        PropertyRepository $propertyRepository,
        BookingRepository $bookingRepository,
        PropertySummaryRepository $propertySummaryRepository
    )
    {
        $this->propertyRepository = $propertyRepository;
        $this->bookingRepository = $bookingRepository;
        $this->propertySummaryRepository = $propertySummaryRepository;

        $commissionChannelCategoryMapping = [
            2 => 'commission_offline',
            3 => 'commission',
            4 => 'commission_channel',
        ];

        $this->commissionChannelCategoryMapping = $commissionChannelCategoryMapping;

        $this->exchangeCurrencyCode = 'EUR';
    }

    public function calculatePeriodCommission($propertyId, $type, $periodStartDate, $periodFinishDate)
    {

        $propertyDetailCriteria = [
            'criteria' => [
                ['field' => 'id', 'condition' => '=', 'value' => $propertyId],
                //['field' => 'status', 'condition' => '=', 'value' => 1],
            ],
            'with' => ['propertyBookingEngines.channel'],
            'firstRow' => true
        ];

        $propertyDetail = $this->propertyRepository->findByCriteria($propertyDetailCriteria);

        $propertyCreateTime = Carbon::parse($propertyDetail['invoice_start_date'])->toDateString();

        if ($propertyCreateTime > $periodFinishDate) {
            throw new ApiErrorException('Property not ready for transaction: ' . $propertyCreateTime . ' - ' . $propertyDetail['name']);
        }

        $channelBaseCommission = [];
        foreach ($propertyDetail['property_booking_engines'] as $propertyBookingEngine) {

            $commissionRateParam = isset($this->commissionChannelCategoryMapping[$propertyBookingEngine['channel']['channel_category_id']]) ? $this->commissionChannelCategoryMapping[$propertyBookingEngine['channel']['channel_category_id']] : 'commission';
            $commissionRateRate = $propertyDetail[$commissionRateParam];


            $bookingListCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyBookingEngine['property_id']],
                    ['field' => 'channel_id', 'condition' => '=', 'value' => $propertyBookingEngine['channel_id']],
                    ['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'whereIn' =>
                    [
                        ['field' => 'status', 'value' => [1, 3]]
                    ]
            ];

            if ($type == 1) {
                $bookingListCriteria['criteria'][] = ['field' => 'checkout_date', 'condition' => '>=', 'value' => $periodStartDate];
                $bookingListCriteria['criteria'][] = ['field' => 'checkout_date', 'condition' => '<', 'value' => $periodFinishDate];
            }

            if ($type == 2) {
                $bookingListCriteria['criteria'][] = ['field' => 'reservation_time', 'condition' => '>=', 'value' => Carbon::parse($periodStartDate, 'UTC')->timestamp];
                $bookingListCriteria['criteria'][] = ['field' => 'reservation_time', 'condition' => '<', 'value' => Carbon::parse($periodFinishDate, 'UTC')->timestamp];
            }

            $bookingList = $this->bookingRepository->findByCriteria($bookingListCriteria, ['id', 'property_id', 'channel_id', 'total', 'currency_code', 'checkin_date', 'checkout_date']);

            $bookingListCollect = collect($bookingList)->groupBy('currency_code')->map(function ($group) {
                return [
                    'total' => $group->sum('total')
                ];
            });

            $bookingListCollect = $bookingListCollect ? $bookingListCollect->toArray() : [];

            $exchangeRate = 1;
            $exchangeRateOriginal = 1;
            $exchangeRateDate = Carbon::parse($periodStartDate)->lastOfMonth()->toDateString();
            foreach ($bookingListCollect as $bookingCurrency => $bookingTotal) {

                if ($bookingCurrency != $this->exchangeCurrencyCode) {
                    $exchangeRateCheck = CurrencyRates::where('currency_code', $bookingCurrency)
                        ->where('exc_currency_code', $this->exchangeCurrencyCode)
                        ->where('date', $exchangeRateDate)
                        ->first();

                    if ($exchangeRateCheck) {
                        $exchangeRateCheck = $exchangeRateCheck->toArray();

                    } else {
                        $exchangeRateCheck = CurrencyRates::where('currency_code', $bookingCurrency)
                            ->where('exc_currency_code', $this->exchangeCurrencyCode)
                            ->where('date', '<', $exchangeRateDate)
                            ->orderBy('date', 'DESC')
                            ->first()->toArray();
                    }

                    $exchangeRate = $exchangeRateCheck['rate'];
                    $exchangeRateDate = $exchangeRateCheck['date'];

                }

                $bookingTotalWithExchange = $bookingTotal['total'] * $exchangeRate;
                $bookingCommissionWithExchange = $bookingTotalWithExchange * $commissionRateRate / 100;

                $channelBaseCommission[$propertyBookingEngine['channel_id']][$bookingCurrency] = [
                    'count' => count($bookingList),
                    'total' => (float)number_format($bookingTotal['total'], '4', '.', ''),
                    'exchangeRate' => $exchangeRate,
                    'exchangeRateDate' => $exchangeRateDate,
                    'exchangeData' => [
                        'total' => (float)number_format($bookingTotalWithExchange, '4', '.', ''),
                        'commission' => (float)number_format($bookingCommissionWithExchange, '4', '.', ''),
                        'commissionRate' => $commissionRateRate,
                        'currencyCode' => $this->exchangeCurrencyCode,
                    ]
                ];

            }


        }


        $channelBaseCount = 0;
        $channelBaseTotal = 0;
        $channelBaseTotalCommission = 0;
        foreach ($channelBaseCommission as $channelBase) {
            $channelBaseValue = reset($channelBase);
            $channelBaseCount += $channelBaseValue['count'];
            $channelBaseTotal += $channelBaseValue['exchangeData']['total'];
            $channelBaseTotalCommission += $channelBaseValue['exchangeData']['commission'];
        }

        $commission = [
            'channelBaseCount' => $channelBaseCount,
            'channelBaseTotal' => $channelBaseTotal,
            'channelBaseTotalCommission' => $channelBaseTotalCommission,
        ];

        return $commission;
    }


    public function summaryWithPeriod($propertyId, $period, $type)
    {
        $response = ['status' => -1, 'message' => '', 'data' => null];

        $periodStartDate = Carbon::parse($period . '-01')->toDateString();
        $periodFinishDate = Carbon::parse($periodStartDate)->addMonth()->firstOfMonth()->toDateString();


        try {

            $propertyDetailCriteria = [
                'criteria' => [
                    ['field' => 'id', 'condition' => '=', 'value' => $propertyId],
                    //['field' => 'status', 'condition' => '=', 'value' => 1],
                ],
                'with' => ['propertyBookingEngines.channel'],
                'firstRow' => true
            ];

            $propertyDetail = $this->propertyRepository->findByCriteria($propertyDetailCriteria);

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


            $conditions = [
                'type' => $type,
                'commission' => $propertyDetail['commission'],
                'commission_payment' => $propertyDetail['commission_payment'],
                'commission_offline' => $propertyDetail['commission_offline'],
                'commission_channel' => $propertyDetail['commission_channel'],
            ];

            $periodCommission = $this->calculatePeriodCommission($propertyId, $type, $periodStartDate, $periodFinishDate);

            $propertySummaryCreateParam = [
                'property_id' => $propertyId,
                'period' => $period,
                'count' => $periodCommission['channelBaseCount'],
                'commission' => (float)number_format($periodCommission['channelBaseTotalCommission'], '2', '.', ''),
                'total' => (float)number_format($periodCommission['channelBaseTotal'], '2', '.', ''),
                'currency' => $this->exchangeCurrencyCode,
                'type' => $type,
                'status' => 1,
            ];


            $propertySummaryCheckCriteria = [
                'criteria' => [
                    ['field' => 'property_id', 'condition' => '=', 'value' => $propertyId],
                    ['field' => 'period', 'condition' => '=', 'value' => $period],
                    ['field' => 'type', 'condition' => '=', 'value' => $type],
                ],
                'firstRow' => true
            ];

            $propertySummaryCheck = $this->propertySummaryRepository->findByCriteria($propertySummaryCheckCriteria);

            if ($propertySummaryCheck) {
                $propertySummaryCreate = $this->propertySummaryRepository->update($propertySummaryCheck['id'], $propertySummaryCreateParam);
            } else {
                $propertySummaryCreate = $this->propertySummaryRepository->create($propertySummaryCreateParam);
            }

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


            $response = [
                'status' => true,
                'data' => $propertySummaryCreate['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);
    }

}
