<?php


namespace App\Core\Service;


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

class PropertyInvoiceService
{
    private $countryRepository;

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

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

        $this->commissionChannelCategoryMapping = $commissionChannelCategoryMapping;

        $this->exchangeCurrencyCode = 'EUR';
    }

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

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

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

        $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' => 'checkout_date', 'condition' => '>=', 'value' => $periodStartDate],
                    ['field' => 'checkout_date', 'condition' => '<', 'value' => $periodFinishDate],
                ],
                'whereIn' =>
                    [
                        ['field' => 'status', 'value' => [1, 3]]
                    ]
            ];

            $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] = [
                    '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,
                    ]
                ];

            }


        }


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

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

        return $commission;
    }


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

        $excludeTaxPropertyIds = [1683]; //$propertyDetail['country']

        $exchangeCurrencyCode = 'EUR';

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


        $periodFixedValueStartDate = Carbon::parse($period . '-01')->toDateString();
        $periodFixedValueFinishDate = 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');
            }

            if ($propertyDetail['invoice_start_date']) {
                $propertyCreateTime = Carbon::parse($propertyDetail['invoice_start_date'])->toDateString();
            } elseif ($propertyDetail['invoice_start_date']) {
                $propertyCreateTime = Carbon::parse($propertyDetail['activation_date'])->toDateString();
            }

            //$propertyCreateTime = '2024-08-10';

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

            $conditions = [
                'invoice_type' => $propertyDetail['invoice_type'],
                'fee' => $propertyDetail['fee'],
                'credit' => $propertyDetail['credit'],
                'commission' => $propertyDetail['commission'],
                'commission_payment' => $propertyDetail['commission_payment'],
                'commission_offline' => $propertyDetail['commission_offline'],
                'commission_channel' => $propertyDetail['commission_channel'],
            ];


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

            if ($propertyDetail['invoice_type'] == 'ANN') {

                $periodYearCount = (Carbon::parse($propertyCreateTime)->lastOfMonth()->diffInMonths($periodFinishDate) % 12) + 1;
                $periodAnnuallyInvoiceDate = Carbon::parse($propertyCreateTime)->addMonth($periodYearCount * 12)->toDateString();

                $periodFixedValueFinishDate = Carbon::parse($propertyCreateTime)->addMonths($periodYearCount * 12)->firstOfMonth()->toDateString();
                $periodFixedValueStartDate = Carbon::parse($periodFixedValueFinishDate)->subMonths($periodYearCount * 12)->firstOfMonth()->toDateString();

                $periodCommissionFroFixed = $this->calculatePeriodCommission($propertyId, $periodFixedValueStartDate, $periodFinishDate);

            } else {
                $periodCommissionFroFixed = $periodCommission;
            }


            $channelBaseTotal = $periodCommission['channelBaseTotal'];
            $channelBaseTotalCommission = $periodCommission['channelBaseTotalCommission'];

            if(isset($propertyDetail['promotion_fee']) && !empty($propertyDetail['promotion_fee']) && !empty($propertyDetail['promotion_fee_month'])) {
                if($periodFinishDate < Carbon::parse($propertyDetail['activation_date'])->addMonths($propertyDetail['promotion_fee_month'])->toDateString()) {
                    $propertyDetail['fee'] = $propertyDetail['promotion_fee'];
                }
            }

            $fixedValue = 0; //$propertyDetail['fee']
            if ($propertyDetail['invoice_type'] == 'MNT') {

                //if ($propertyDetail['credit'] == 0) {
                $fixedValue = $propertyDetail['fee'];
                //} else if ($periodCommissionFroFixed['channelBaseTotalCommission'] < $propertyDetail['credit']) {
                //$fixedValue = $propertyDetail['fee'];
                //}

                $isPropertyFirstMonth = Carbon::parse($propertyCreateTime)->format('Y-m') == Carbon::parse($periodStartDate)->format('Y-m') ? true : false;

                if ($isPropertyFirstMonth) {
                    $propertyFirstMonthDiffInDays = Carbon::parse($propertyCreateTime)->diffInDays(Carbon::parse($periodFinishDate)->subDay());
                    $perDailyFixedValue = $fixedValue / Carbon::parse($periodStartDate)->daysInMonth;

                    $fixedValue = $propertyFirstMonthDiffInDays * $perDailyFixedValue;

                }

            } elseif ($propertyDetail['invoice_type'] == 'ANN') {

                if (Carbon::parse($periodFixedValueStartDate)->format('Y-m') == $period) {
                    $fixedValue = $propertyDetail['fee'];
                }

            }


            if ($periodCommissionFroFixed['channelBaseTotal'] < $propertyDetail['credit']) {
                $channelBaseTotalCommission = 0;
            } else {
                $channelBaseTotalCommission = ($periodCommissionFroFixed['channelBaseTotal'] - $propertyDetail['credit']) * $propertyDetail['commission'] / 100;
            }

            //Suspended
            if($propertyDetail['status'] == 3) {
                $fixedValue = 0;
                $channelBaseTotalCommission = ($periodCommissionFroFixed['channelBaseTotal']) * $propertyDetail['commission'] / 100;;
            }

            $fixedTaxValue = $fixedValue * 0.20;
            if (in_array($propertyDetail['id'], $excludeTaxPropertyIds)) {
                $fixedTaxValue = 0;
            }

            $totalInvoiceValue = $fixedValue + $fixedTaxValue + $channelBaseTotalCommission;


            $propertyInvoiceCreateParam = [
                'property_id' => $propertyId,
                'period' => $period,
                'fixed' => (float)number_format($fixedValue, '2', '.', ''),
                'fixed_tax' => (float)number_format($fixedTaxValue, '2', '.', ''),
                'commission' => (float)number_format($channelBaseTotalCommission, '2', '.', ''),
                'total' => (float)number_format($totalInvoiceValue, '2', '.', ''),
                'currency' => 'EUR',
                'sales' => (float)number_format($channelBaseTotal, '2', '.', ''),
                'summary' => !empty($channelBaseCommission) ? json_encode($channelBaseCommission) : null,
                'conditions' => json_encode($conditions),
                'status' => 1,
            ];

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

            $propertyInvoiceCheck = $this->propertyInvoiceRepository->findByCriteria($propertyInvoiceCheckCriteria);

            if ($propertyInvoiceCheck) {
                $propertyInvoiceCreate = $this->propertyInvoiceRepository->update($propertyInvoiceCheck['id'], $propertyInvoiceCreateParam);
            } else {
                $propertyInvoiceCreate = $this->propertyInvoiceRepository->create($propertyInvoiceCreateParam);
            }

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


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

}
