<?php

namespace App\Classes\Modules\Transactions\Processors;


use App\Classes\Modules\Bookings\ControllersLogic\UpdateBookingAmountLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Documents\Services\DeletesDocument;
use App\Classes\Modules\Wallets\Processors\CreditWalletProcessor;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\Modules\Transactions\Standards\Rules\CanUpdateRefundTransactionStatus;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Classes\ValueObjects\Constants\RemarkRefundReason;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Transaction;
use ErrorException;
use Illuminate\Support\Facades\Log;

class UpdateRefundTransactionStatusProcessor
{
    /** @var FetchesCompany */
    private $fetchesCompany;

    /** @var FetchesTransaction */
    private $fetchesTransaction;

    /** @var UpdatesTransactionStatus */
    private $updatesTransactionStatus;

    /** @var DeletesDocument */
    private $deletesDocument;

    /** @var CreditWalletProcessor */
    private $creditWalletProcessor;

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

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

    /** @var UpdateBookingAmountLogic */
    private $updateBookingAmountLogic;

    /** @var CanUpdateRefundTransactionStatus */
    private $canUpdateRefundTransactionStatus;

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

    /**
     * UpdateRefundTransactionStatusProcessor constructor.
     * @param FetchesCompany $fetchesCompany
     * @param FetchesTransaction $fetchesTransaction
     * @param UpdatesTransactionStatus $updatesTransactionStatus
     * @param DeletesDocument $deletesDocument
     * @param CreditWalletProcessor $creditWalletProcessor
     * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount
     * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
     * @param UpdateBookingAmountLogic $updateBookingAmountLogic
     * @param CanUpdateRefundTransactionStatus $canUpdateRefundTransactionStatus
     * @param CreateInvoiceTransactionV2Processor $createInvoiceTransactionV2Processor
     */
    public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdateBookingAmountLogic $updateBookingAmountLogic, CanUpdateRefundTransactionStatus $canUpdateRefundTransactionStatus, CreateInvoiceTransactionV2Processor $createInvoiceTransactionV2Processor)
    {
        $this->fetchesCompany = $fetchesCompany;
        $this->fetchesTransaction = $fetchesTransaction;
        $this->updatesTransactionStatus = $updatesTransactionStatus;
        $this->deletesDocument = $deletesDocument;
        $this->creditWalletProcessor = $creditWalletProcessor;
        $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
        $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
        $this->updateBookingAmountLogic = $updateBookingAmountLogic;
        $this->canUpdateRefundTransactionStatus = $canUpdateRefundTransactionStatus;
        $this->createInvoiceTransactionV2Processor = $createInvoiceTransactionV2Processor;
    }


    /**
     * @param Booking $booking
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     */
    public function execute(int $transactionId, int $transactionStatus, string $refundRemark = "")
    {
        $refundTransaction = $this->fetchesTransaction->execute(['id' => $transactionId]);

        $refundTransaction = $this->updatesTransactionStatus->execute($refundTransaction, $transactionStatus);

        $paymentTransaction = $refundTransaction->owner;

        $supplierRefundTransaction = $paymentTransaction->transactions()->supplierRefunds()->where('status', [ApprovalStatus::PENDING_VERIFICATION])->first();

        $booking = $paymentTransaction->owner;

        // $reference = $paymentTransaction->amount - $refundTransaction->amount < 0.01 ? 'Fully Refund for Ref. ' . $booking->marking : 'Partially Refund for Ref. ' . $booking->marking;
        if ($paymentTransaction->amount - $refundTransaction->amount < 0.01) {
            $reference = 'Return Inward for Ref. ' . $booking->marking;
        } else {
            $refundRemark = $refundTransaction->remarks && $refundTransaction->remarks->first() ? $refundTransaction->remarks->first()->content : $refundRemark;
            $remarkGroup = RemarkRefundReason::REFUND_REASONS[$refundRemark] ?? null;
            $reference = $remarkGroup ? $remarkGroup . ' for Ref. ' . $booking->marking : $refundRemark . ' for Ref. ' . $booking->marking;
        }

        $refundAmount = $this->calculatesBookingRefundAmount->calculateRefundAmount($paymentTransaction, $booking->fix_currency_id);

        $paidAmount = $paymentTransaction->original_amount - $refundAmount;

        $claimed = Transaction::where('id', $refundTransaction->id)
            ->where('process_status', null)
            ->update(['process_status' => ApprovalStatus::PENDING_VERIFICATION]);

        if (!$claimed) {
            throw new ErrorException("This refund transaction has already been processed");
        }

        if ($refundTransaction->status == ApprovalStatus::APPROVED) {
            $this->creditWalletProcessor->execute($booking->company, $refundTransaction->type, $refundTransaction->amount, $reference, $refundTransaction);

            $po_transaction = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();

            if ($po_transaction) {
                $this->updatesTransactionStatus->execute($po_transaction, (float) number_format($po_transaction->amount, 2, '.', '')  === (float) number_format((float)$booking->fix_amount - $refundTransaction->original_amount, 2, '.', '') ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::PENDING_SUBMISSION);
            }

            // cief todo: 90 - REVERTED STARTS (commented before revert)
            // $request['fix_amount'] = $booking->fix_amount - $refundTransaction->original_amount;
            // $request->route()->setParameter('id', $booking->id);
            // $this->updateBookingAmountLogic->execute($request);
            // cief todo: 90 - REVERTED ENDS (commented before revert)
        }

        if ($supplierRefundTransaction) {
            $this->updatesTransactionStatus->execute($supplierRefundTransaction, $transactionStatus);
        }

        if (!$paidAmount > 0) {
            $this->updatesTransactionStatus->execute($paymentTransaction, ApprovalStatus::REFUNDED);
        }

        //A full refund will need invoice generated
        if($refundAmount === $booking->fix_amount){
            $this->createInvoiceTransactionV2Processor->execute($booking, "", null, [
                'generateEInvoice' => false,
                'generateEInvoiceWithNormalInvoiceTemplate' => false,
                'generateEInvoiceRefund' => true,
                'bookingOriginalStatus' => $booking->status
            ]);
        }

        Transaction::where('id', $refundTransaction->id)
            ->where('process_status', ApprovalStatus::PENDING_VERIFICATION)
            ->update(['process_status' => ApprovalStatus::APPROVED]);
    }
}
