<?php


namespace App\Classes\Modules\Bookings\ControllersLogic;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Bookings\Services\UpdatesBookingFixedAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\Modules\Bookings\Services\UpdatesBookingInvoiceStatus;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionV2Processor;
use App\Classes\Modules\Bookings\DataTransferObjects\UpdateBookingInvoiceStatusObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\BookingResource;
use App\Models\Booking;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic
{

    /**
     * @return array
     */
    protected function notification():array {
        return [
            'title' => 'Update Purchase Order With Booking Amount Update',
            'message' => 'You have successfully updated booking amount'
        ];
    }

    /** @var FetchesBooking */
    private $fetchesBooking;

    /** @var UpdatesBookingFixedAmount */
    private $updatesBookingFixedAmount;

    /** @var CalculatesBookingOutstanding */
    private $calculatesBookingOutstanding;

    /** @var CalculatesBookingPayableAmount */
    private $calculatesBookingPayableAmount;

    /** @var CalculatesBookingRefundAmount */
    private $calculatesBookingRefundAmount;

    /** @var UpdatesBookingInvoiceStatus */
    private $updatesBookingInvoiceStatus;

    /** @var CreateInvoiceTransactionV2Processor */
    private $createInvoiceTransactionProcessor;

    /**
     * UpdateBookingAmountWithPOLogic constructor.
     * @param FetchesBooking $fetchesBooking
     * @param UpdatesBookingFixedAmount $updatesBookingFixedAmount
     * @param CalculatesBookingOutstanding $calculatesBookingOutstanding
     * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount
     * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
     * @param UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus
     * @param CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor
     */
    public function __construct(FetchesBooking $fetchesBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus, CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor)
    {
        $this->fetchesBooking = $fetchesBooking;
        $this->updatesBookingFixedAmount = $updatesBookingFixedAmount;
        $this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
        $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
        $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
        $this->updatesBookingInvoiceStatus = $updatesBookingInvoiceStatus;
        $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
    }

    /**
     * @param Request $request
     * @param string $id
     * @return JsonResponse
     * @throws \App\Classes\Exceptions\MalformedRequestException
     * @throws \App\Classes\Exceptions\CriteriaNotFulfilledException
     */
    public function logic(Request $request, $id = '') : JsonResponse
    {
        /** @var Booking $booking */
        $booking = $this->fetchesBooking->execute(['id' => $request->route('id') ?? $id]);
        $outstandingAmount = $this->calculatesBookingOutstanding->execute($booking);
        $bookingAttribute = $booking->attributesKVP()->where('key', KVPKey::BOOKING_AMOUNT_UPDATE)->first();


        // cief todo: 90 - BEFORE REVERT
        $refundAmount = $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id);
        $paidAmount = floatval($this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id)) - floatval($refundAmount);

        if($paidAmount > 0 && $outstandingAmount > 0 && !$bookingAttribute){
            if($refundAmount != $outstandingAmount){
                throw new MalformedRequestException('Purchase Order at this point can only be edited after editing the booking amount');
            }
        }
        //

        // cief todo: 90 - REVERTED STARTS
        // $paidAmount = floatval($this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id)) - floatval($this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id));

        // if($paidAmount > 0 && $outstandingAmount > 0 && !$bookingAttribute){
        //     throw new MalformedRequestException('Purchase Order at this point can only be edited after editing the booking amount');
        // }
        // cief todo: 90 - REVERTED ENDS

        if($bookingAttribute){
            $total = collect($request->input('products'))->sum(function($product){
                return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price']));
            });
            $bookingAmountUpdate = (float)$bookingAttribute->value;
            // $isTally = ($total === $bookingAmountUpdate) ? true : false;
            $isTally = bccomp($total, $bookingAmountUpdate, 3) === 0;

            if(!$isTally){
                throw new MalformedRequestException('Purchase Order total not tally with updated booking amount of ' . $bookingAmountUpdate);
            }
            $booking->transactions()->where('type', TransactionType::PROFORMA)->delete();
            $booking = $this->updatesBookingFixedAmount->execute($booking, $bookingAmountUpdate);

            if($booking->invoice_status === ApprovalStatus::APPROVED){
                $updateBookingInvoiceStatusObject = new UpdateBookingInvoiceStatusObject($booking->id, ApprovalStatus::PENDING_VERIFICATION);
                $this->updatesBookingInvoiceStatus->execute($booking, $updateBookingInvoiceStatusObject);
            }
            else{
                // $this->createInvoiceTransactionProcessor->execute($booking);
                $this->createInvoiceTransactionProcessor->execute($booking, "", null, [
                        'generateEInvoice' => false,
                        'generateEInvoiceWithNormalInvoiceTemplate' => false,
                        'generateEInvoiceRefund' => false,
                        'bookingOriginalStatus' => $booking->status
                    ]
                );
            }

            $request->merge(['is_privilleged_update' => true]);

            $bookingAttribute->delete();
        }

        return $this->resourceResponse(new BookingResource($booking));
    }
}
