<?php

namespace App\Classes\Modules\Transactions\Processors;

use App\Classes\Modules\Bookings\Services\CalculatesBookingTransferredAmount;
use App\Classes\Modules\ServiceTypes\Services\FetchesServiceConfigurations;
use App\Classes\Modules\Transactions\Services\ListsTransactions;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount;
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\Bookings\Services\UpdatesBookingStatus;

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\SegmentConstants;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Booking;
use App\Models\Document;
use App\Models\SegmentConstant;
use Meneses\LaravelMpdf\Facades\LaravelMpdf;

class CreateInvoiceTransactionProcessor
{
    /** @var ListsTransactions */
    private $listsTransactions;

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

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

    /** @var CalculatesBookingPaidAmount */
    private $calculatesBookingPaidAmount;

    /** @var CalculatesBookingTransferredAmount */
    private $calculatesBookingTransferredAmount;

    /** @var FetchesServiceConfigurations */
    private $fetchesServiceConfigurations;

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

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

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

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

    /** @var UpdatesBookingStatus */
    private $updatesBookingStatus;

    /**
     * CreateInvoiceTransactionProcessor constructor.
     * @param ListsTransactions $listsTransactions
     * @param CreatesTransaction $createsTransaction
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param CalculatesBookingPaidAmount $calculatesBookingPaidAmount
     * @param CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount
     * @param FetchesServiceConfigurations $fetchesServiceConfigurations
     * @param CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate
     * @param FetchesCompany $fetchesCompany
     * @param CreatesDocument $createsDocument
     * @param CreatesFiles $createsFile
     * @param UpdatesBookingStatus $updatesBookingStatus
     */
    public function __construct(ListsTransactions $listsTransactions, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPaidAmount $calculatesBookingPaidAmount, CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount, FetchesServiceConfigurations $fetchesServiceConfigurations, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesBookingStatus $updatesBookingStatus)
    {
        $this->listsTransactions = $listsTransactions;
        $this->createsTransaction = $createsTransaction;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->calculatesBookingPaidAmount = $calculatesBookingPaidAmount;
        $this->calculatesBookingTransferredAmount = $calculatesBookingTransferredAmount;
        $this->fetchesServiceConfigurations = $fetchesServiceConfigurations;
        $this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate;
        $this->fetchesCompany = $fetchesCompany;
        $this->createsDocument = $createsDocument;
        $this->createsFile = $createsFile;
        $this->updatesBookingStatus = $updatesBookingStatus;
    }


    /**
     * @param Booking $booking
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     */
    public function execute(Booking $booking) 
    {
        $payment_amount = $this->calculatesBookingTransferredAmount->execute($booking, $booking->fix_currency_id);
        $booking_amount = $booking->fix_amount;

        if ((float) $booking_amount !== (float) $payment_amount) {
            return;
        }

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

        $constants = SegmentConstant::where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->id', $booking->service->id)->first();

        if($constants->detail->is_billable && !$po_order_transaction) {
            return;
        }

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

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

        $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::PAYMENT);

        $total_service_charge = $booking->transactions()
            ->where('type', TransactionType::PAYMENT)
            ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
            ->sum('service_charge');

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

        $transaction_object = new TransactionObject(
            $billNumber,
            TransactionType::INVOICE,
            $transaction->issuer,
            $transaction->receiver,
            $transaction->recipient_bank_account_id,
            $transaction->payment_method,
            $payment_amount,
            $booking_amount,
            $transaction->currency_id,
            $transaction->original_currency_id,
            $booking_currency_average_rate,
            $total_tax,
            $total_service_charge,
            null,
            ApprovalStatus::APPROVED
        );
        $invoice_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object);

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

        $purchase_order_pdf = LaravelMpdf::loadView('pages.pdfs.purchase_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
        $document_object = new DocumentObject(
            DocumentType::PURCHASE_ORDER,
            [chunk_split('data:application/pdf;base64,'.base64_encode($purchase_order_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            'purchase_orders'
        );
        $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
        $this->createsFile->execute($document, $document_object);

        $deliver_order_pdf = LaravelMpdf::loadView('pages.pdfs.deliver_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
        $document_object = new DocumentObject(
            DocumentType::DELIVER_ORDER,
            [chunk_split('data:application/pdf;base64,'.base64_encode($deliver_order_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            'delivery_orders'
        );

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


        $invoice_pdf = LaravelMpdf::loadView('pages.pdfs.invoice', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
        $document_object = new DocumentObject(
            DocumentType::INVOICE,
            [chunk_split('data:application/pdf;base64,'.base64_encode($invoice_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            'invoices'
        );
        $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
        $this->createsFile->execute($document, $document_object);

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

        $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::BILL);

        $transaction_object = new TransactionObject(
            $billNumber,
            TransactionType::SUPPLIER_DELIVER,
            $transaction->issuer,
            $transaction->receiver,
            $transaction->recipient_bank_account_id,
            $transaction->payment_method,
            $payment_amount,
            $booking_amount,
            $transaction->currency_id,
            $transaction->original_currency_id,
            $booking_currency_average_rate,
            $total_tax,
            $total_service_charge,
            null,
            ApprovalStatus::APPROVED
        );
        $supplier_deliver_order_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object);

        $supplier_order_pdf = LaravelMpdf::loadView('pages.pdfs.supplier_deliver_order', ['supplier_deliver_order_transaction' => $supplier_deliver_order_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
        $document_object = new DocumentObject(
            DocumentType::SUPPLIER_DELIVER_ORDER,
            [chunk_split('data:application/pdf;base64,'.base64_encode($supplier_order_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            'supplier_delivery_orders'
        );
        $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
        $this->createsFile->execute($document, $document_object);

        $this->updatesBookingStatus->execute($booking, ApprovalStatus::COMPLETED);
    }
}
