<?php

namespace App\Classes\Modules\Transactions\Processors;

use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CalculatesTransactionServiceCharge;
use App\Classes\Modules\Transactions\Services\CalculatesTransactionTransferFee;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\SegmentConstants;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Company;
use App\Models\SegmentConstant;
use App\Models\Transaction;
use Illuminate\Support\Collection;

class CreateSupplierTransactionProcessor
{

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

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

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

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

    /** @var CalculatesTransactionServiceCharge */
    private $calculatesTransactionServiceCharge;

    /** @var CalculatesTransactionTransferFee */
    private $calculatesTransactionTransferFee;

    /** @var Collection */
    private $bills;

    /** @var Collection */
    private $transferFee;

    /**
     * CreateSupplierTransactionProcessor constructor.
     * @param FetchesTransaction $fetchesTransaction
     * @param UpdatesTransactionStatus $updatesTransactionStatus
     * @param CreatesTransaction $createsTransaction
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge
     * @param CalculatesTransactionTransferFee $calculatesTransactionTransferFee
     */
    public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge, CalculatesTransactionTransferFee $calculatesTransactionTransferFee)
    {
        $this->fetchesTransaction = $fetchesTransaction;
        $this->updatesTransactionStatus = $updatesTransactionStatus;
        $this->createsTransaction = $createsTransaction;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->calculatesTransactionServiceCharge = $calculatesTransactionServiceCharge;
        $this->calculatesTransactionTransferFee = $calculatesTransactionTransferFee;
        $this->bills = collect();
        $this->transferFee = collect();
    }

    /**
     * @param Company $supplier
     * @param String $rate
     * @param array $payments
     * @throws \App\Classes\Exceptions\MalformedRequestException
     */
    public function execute(Company $supplier, String $rate, Array $payments) {

        foreach($payments as $payment){

            /** @var Transaction $payment */
            $payment = $this->fetchesTransaction->execute(['id' => $payment['id']]);

            if($payment->status !== ApprovalStatus::APPROVED) continue;

            $totalRefund = $payment->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->sum('original_amount');

            $original_amount_after_refund = $payment->original_amount - $totalRefund;

            $this->updatesTransactionStatus->execute($payment, ApprovalStatus::COMPLETED);
            $billNumber = $this->generatesTransactionBillNumber->execute('SPLR-');
            $constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first();

            $serviceCharge = $this->calculatesTransactionServiceCharge->execute($original_amount_after_refund, $rate, $constant);

            $object = new TransactionObject($billNumber, TransactionType::BILL, $supplier->id, 1,
                $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
                $original_amount_after_refund * (1 / $rate), $original_amount_after_refund, 1, $payment->original_currency_id,
                $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_SUBMISSION);

            /** @var Transaction $billTransaction */
            $billTransaction = $this->createsTransaction->execute($payment, $object);
            $this->updatesTransactionStatus->execute($billTransaction, ApprovalStatus::PENDING_VERIFICATION);
            $this->pushBill($billTransaction);

            $transferFeeNumber = $this->generatesTransactionBillNumber->execute('TRFR-');
            $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant);
            $object = new TransactionObject($transferFeeNumber, TransactionType::TRANSFER_FEE, 1, $supplier->id,
                $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
                $original_amount_after_refund, $original_amount_after_refund, $payment->original_currency_id, $payment->original_currency_id,
                1, 0, $transferFee, null, ApprovalStatus::PENDING_VERIFICATION);

            $this->pushTransferFee($this->createsTransaction->execute($billTransaction, $object));
        }

    }

    /**
     * @return Collection
     */
    public function getBills(): Collection
    {
        return $this->bills;
    }

    /**
     * @return Collection
     */
    public function getTransferTransactions(): Collection
    {
        return $this->transferFee;
    }

    /**
     * @param $bill
     */
    private function pushBill($bill): void
    {
        $this->bills->push($bill);
    }

    /**
     * @param $transferFee
     */
    private function pushTransferFee($transferFee): void
    {
        $this->transferFee->push($transferFee);
    }

}
