<?php

namespace App\Classes\Modules\Transactions\Processors;

use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Bookings\Services\GeneratesBookingQuotation;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Bookings\Services\CalculatesBookingCurrencyAverageRate;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;

use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Booking;
use App\Models\Document;
use Carbon\Carbon;
use Meneses\LaravelMpdf\Facades\LaravelMpdf;

class CreateProformaInvoiceTransactionProcessor
{

    /** @var CreatesTransaction */
    private $createsTransaction;

    /** @var GeneratesTransactionBillNumber */
    private $generatesTransactionBillNumber;


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

    /** @var CalculatesBookingCurrencyAverageRate */
    private $calculatesBookingCurrencyAverageRate;

    /** @var FetchesCompany */
    private $fetchesCompany;

    /** @var CreatesDocument */
    private $createsDocument;

    /** @var CreatesFiles */
    private $createsFile;


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

    /** @var GeneratesBookingQuotation */
    private $generatesBookingQuotation;

    /** @var FetchesBookingQuotation */
    private $fetchesBookingQuotation;

    /** @var FetchesCompanyPaymentAttemptLimit */
    private $fetchesCompanyPaymentAttemptLimit;


    /**
     * CreateProformaInvoiceTransactionProcessor constructor.
     * @param CreatesTransaction $createsTransaction
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount
     * @param CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate
     * @param FetchesCompany $fetchesCompany
     * @param CreatesDocument $createsDocument
     * @param CreatesFiles $createsFile
     * @param CalculatesBookingOutstanding $calculatesBookingOutstanding
     * @param GeneratesBookingQuotation $generatesBookingQuotation
     * @param FetchesBookingQuotation $fetchesBookingQuotation
     * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
     */
    public function __construct(CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, CalculatesBookingOutstanding $calculatesBookingOutstanding, GeneratesBookingQuotation $generatesBookingQuotation, FetchesBookingQuotation $fetchesBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit)
    {
        $this->createsTransaction = $createsTransaction;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
        $this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate;
        $this->fetchesCompany = $fetchesCompany;
        $this->createsDocument = $createsDocument;
        $this->createsFile = $createsFile;
        $this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
        $this->generatesBookingQuotation = $generatesBookingQuotation;
        $this->fetchesBookingQuotation = $fetchesBookingQuotation;
        $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
    }


    /**
     * @param Booking $booking
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     */
    public function execute(Booking $booking) 
    {

        $po_order_transaction = $booking->transactions()
            ->where('type', TransactionType::PURCHASE_ORDER)
            ->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])
            ->first();

        $outstanding = $this->calculatesBookingOutstanding->execute($booking);

        $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $outstanding)), $booking->convertible_currency_id, $booking->service_id,  $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::CASH);

        $configurations = $this->fetchesBookingQuotation->execute($booking->company, $conversionObject);

        $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);

        $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');


        $object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id,
            $configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(),
            $configurations->getTotal(), $configurations->getForeignTotal(), 1,
            $configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(),
            $configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], isset($billPlzBill) ? $billPlzBill->id : NULL);

        $this->createsTransaction->execute($booking, $object);

        $billNumber = $this->generatesTransactionBillNumber->execute('PROFORMA-');

        $payable_amount = $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->sum('amount');
        $booking_amount = $booking->fix_amount;

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

        $booking_currency_average_rate = $booking_amount / $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total');

        $total_service_charge = $booking->transactions()
            ->where('type', TransactionType::PAYMENT)
            ->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])
            ->sum('service_charge');

        $total_tax = $booking->transactions()
            ->where('type', TransactionType::PAYMENT)
            ->whereIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])
            ->sum('tax');

        $transaction_object = new TransactionObject(
            $billNumber,
            TransactionType::PROFORMA,
            $transaction->issuer,
            $transaction->receiver,
            $transaction->recipient_bank_account_id,
            $transaction->payment_method,
            $payable_amount,
            $booking_amount,
            $transaction->currency_id,
            $transaction->original_currency_id,
            $booking_currency_average_rate,
            $total_tax,
            $total_service_charge,
            null,
            ApprovalStatus::APPROVED
        );

        $perofrma_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object);

        $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]);

        $purchase_order_pdf = LaravelMpdf::loadView('pages.pdfs.proforma_invoice', ['invoice_transaction' => $perofrma_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
        $document_object = new DocumentObject(
            DocumentType::PROFORMA_INVOICE,
            [chunk_split('data:application/pdf;base64,'.base64_encode($purchase_order_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            'proforma_invoices'
        );

        /** @var Document $document */
        $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
        $this->createsFile->execute($document, $document_object);


    }
}
