<?php


namespace App\Classes\Modules\Transactions\ControllersLogic;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Companies\Services\FetchesCompanyModule;
use App\Classes\Modules\Groups\DataTransferObjects\GroupObject;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\Modules\Transactions\Processors\CreatePaymentTransactionProcessor;
use App\Models\Group;
use App\Classes\Modules\Groups\Services\CreatesGroup;
use App\Models\Transaction;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Wallets\Processors\CreateWalletTopUpTransactionProcessor;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\GroupTransaction;
use App\Http\Resources\WalletTransactionResource;
use App\Models\Wallet;
use App\Classes\Modules\Transactions\Processors\ReleaseGoodsToCustomerProcessor;


class CreateGroupsLogic extends AbstractControllerLogic
{


    /**
     * @return array
     */
    protected function notification():array {
        return [
            'title' => 'Create Group Transaction',
            'message' => 'You have successfully created a group transaction'
        ];
    }

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

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


    /** @var FetchesCompanyModule  */
    private $fetchesCompanyModule;

    /** @var CreateWalletTopUpTransactionProcessor */
    private $createWalletTopUpTransactionProcessor;

    /** @var CreatePaymentTransactionProcessor */
    private $createPaymentTransactionProcessor;

    /** @var CreatesGroup */
    private $createsGroup;

    /** @var ReleaseGoodsToCustomerProcessor */
    private $releaseGoodsToCustomerProcessor;


    public function __construct(
        FetchesTransaction $fetchesTransaction,
        GeneratesTransactionBillNumber $generatesTransactionBillNumber,
        FetchesCompanyModule $fetchesCompanyModule,
        CreateWalletTopUpTransactionProcessor $createWalletTopUpTransactionProcessor,
        CreatePaymentTransactionProcessor $createPaymentTransactionProcessor,
        CreatesGroup $createsGroup,
        ReleaseGoodsToCustomerProcessor $releaseGoodsToCustomerProcessor
    )
    {
        $this->fetchesTransaction = $fetchesTransaction;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->fetchesCompanyModule = $fetchesCompanyModule;
        $this->createWalletTopUpTransactionProcessor = $createWalletTopUpTransactionProcessor;
        $this->createPaymentTransactionProcessor = $createPaymentTransactionProcessor;
        $this->createsGroup = $createsGroup;
        $this->releaseGoodsToCustomerProcessor = $releaseGoodsToCustomerProcessor;
    }

    public function logic(Request $request) : JsonResponse
    {
        $payment_method     = PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')];
        $groupPyamentStatus = ApprovalStatus::PENDING_VERIFICATION;

        $invoice_ids    = json_decode($request->route('transaction_ids'));
        $billNumber     = $this->generatesTransactionBillNumber->execute('PYMT-');

        // todo-new: check why is this not working
        // $invoices = $this->fetchesTransaction->execute(['id_in' => $invoice_ids]);
        $invoices       = Transaction::whereIn('id', $invoice_ids)->get();

        foreach ($invoices as $invoice) {
            $order = null;
            if ($invoice->owner instanceof Transaction) {
                if ($invoice->owner) {
                    if ($invoice->owner->owner) {
                        $order = $invoice->owner->owner->owner;
                    }
                }
            } else if (!($invoice->owner instanceof Transaction) && !($invoice->owner instanceof Wallet)) {
                if ($invoice->owner) {
                    $order = $invoice->owner->owner;
                }
            }

            if (!$order) {
                throw new MalformedRequestException("There is an error while paying for invoice {$invoice->bill_no}");
            }
        }

        if ($payment_method == PaymentMethodType::WALLET) {

            $companyModuleId = $invoices->first()->receiver;

            $wallet = Wallet::where('owner_id', $companyModuleId)->first();

            // todo-new: verify currently checking wallet amount based on 'amount' value passed by frontend
            if((float) number_format(($wallet->amount - $request->input('amount')),2) < 0){
                throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
            }

            $groupPyamentStatus = ApprovalStatus::APPROVED;
        }

        $issuer                 = 0;
        $receiver               = 0;
        $amount                 = 0;
        $original_amount        = 0;
        $currency_id            = 0;
        $original_currency_id   = 0;
        $currency_rate          = 0;
        $tax                    = 0;
        $service_charge         = 0;

        $group = new GroupObject(
            $issuer,
            $receiver,
            $amount,
            $original_amount,
            $currency_id,
            $original_currency_id,
            $currency_rate,
            $tax,
            $service_charge,
            $payment_method,
            '-',
            $groupPyamentStatus,
        );

        $group = $this->createsGroup->execute($group);

        foreach ($invoices as $invoice) {
            if ($payment_method == PaymentMethodType::WALLET) {
                $paymentTransaction = $this->createPaymentTransactionProcessor->execute($invoice, PaymentMethodType::WALLET, $request->input('bank_code'), false);

                if($paymentTransaction && $paymentTransaction->status == ApprovalStatus::APPROVED){
                    $pL = $invoice->owner;
                    $this->releaseGoodsToCustomerProcessor->execute($pL, $invoice);
                }
            }

            $issuer                 = $invoice->issuer;
            $receiver               = $invoice->receiver;
            $amount                 += $invoice->amount;
            $original_amount        += $invoice->original_amount;
            $currency_id            = $invoice->currency_id;
            $original_currency_id   = $invoice->original_currency_id;
            $currency_rate          = $invoice->currency_rate;
            $tax                    += $invoice->tax;
            $service_charge         += $invoice->service_charge;

            // creates group transaction
            $groupTransaction = new GroupTransaction();
            $groupTransaction->group_id = $group->id;
            $groupTransaction->transaction_id = $invoice->id;
            $groupTransaction->save();
        }

        if (in_array($payment_method, [PaymentMethodType::PAYMENT_GATEWAY, PaymentMethodType::CASH])) {
            // create only one billplz payment for wallet top up
            $amount = floatval(str_replace(',', '', $request->input('amount')));
            $companyModuleId = $invoice->receiver;
            $companyModule = $this->fetchesCompanyModule->execute(['id' => $companyModuleId]);

            $topUpTransaction = $this->createWalletTopUpTransactionProcessor->execute($companyModule, $amount, $request->input('bank_code'), $payment_method, $billNumber, true);
        }

        $group->issuer                  = $issuer;
        $group->receiver                = $receiver;
        $group->reference               = $payment_method === PaymentMethodType::PAYMENT_GATEWAY ? $topUpTransaction->payment_reference : $billNumber;
        $group->amount                  = $amount;
        $group->original_amount         = $original_amount;
        $group->currency_id             = $currency_id;
        $group->original_currency_id    = $original_currency_id;
        $group->currency_rate           = $currency_rate;
        $group->tax                     = $tax;
        $group->service_charge          = $service_charge;
        $group->status                  = $groupPyamentStatus;
        $group->save();

        if ($payment_method === PaymentMethodType::PAYMENT_GATEWAY) {
            return $this->resourceResponse(new WalletTransactionResource($topUpTransaction));

        } else {
            return $this->response([]);
        }
    }
}
