<?php

namespace App\Classes\Modules\Billplzs\ControllersLogic;

use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Jobs\Commands\V2\CreateInvoiceTransactionV2CommandJob;
use App\Classes\Modules\Billplzs\DataTransferObjects\BillplzXSignatureObject;
use App\Classes\Modules\Billplzs\Services\GetBillplzBill;
use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\CreateReceiptVoucherTransactionProcessor;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject;
use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance;
use App\Classes\Modules\Wallets\Services\UpdatesWallet;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\TransactionResource;

use App\Models\Booking;
use App\Models\User;
use App\Models\Wallet;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class CallbackBillplzLogic
{


    /** @var GetBillplzBill */
    private $getBillplzBill;

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

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

    /** @var UpdatesWalletBalance */
    private $updatesWalletBalance;

    /** @var CreateCashBackTransactionProcessor */
    private $createCashBackTransactionProcessor;

    /** @var RecalculatesWalletBalance */
    private $recalculatesWalletBalance;

    /** @var CreateReceiptVoucherTransactionProcessor */
    private $createReceiptVoucherTransactionProcessor;

    /**
     * CallbackBillplzLogic constructor.
     * @param GetBillplzBill $getBillplzBill
     * @param FetchesTransaction $fetchesTransaction
     * @param UpdatesTransactionStatus $updatesTransactionStatus
     * @param UpdatesWalletBalance $updatesWalletBalance
     * @param RecalculatesWalletBalance $recalculatesWalletBalance
     * @param CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor
     */
    public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWalletBalance $updatesWalletBalance, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor)
    {
        $this->getBillplzBill = $getBillplzBill;
        $this->fetchesTransaction = $fetchesTransaction;
        $this->updatesTransactionStatus = $updatesTransactionStatus;
        $this->updatesWalletBalance = $updatesWalletBalance;
        $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor;
        $this->recalculatesWalletBalance = $recalculatesWalletBalance;
        $this->createReceiptVoucherTransactionProcessor = $createReceiptVoucherTransactionProcessor;
    }


    /**
     * @param Request $request
     * @return bool|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @throws MalformedRequestException
     * @throws ResourceNotFoundException
     */
    public function execute(Request $request)
    {
        $billplzXSignatureObject = new BillplzXSignatureObject($request);

        if(!$billplzXSignatureObject->isValidSignature()){
            Log::info('Billplz signature validation failed', [
                'computed_signature' => $billplzXSignatureObject->getBillPlzComputedXSignature(),
                'request_signature' => $request->input('x_signature'),
                'constructed_string' => $billplzXSignatureObject->getBillPlzConstructString(),
                'request_data' => $request->all(),
            ]); //cief todo: 137 - DEBUG
            throw new MalformedRequestException('Billplz Payment validation failed.');
        }

        $billPlz = $this->getBillplzBill->execute($billplzXSignatureObject->getBillPlzId());

        if(!$billPlz) throw new ResourceNotFoundException('Billplz bill not found.');

        $transaction = $this->fetchesTransaction->execute(['payment_reference' => $billplzXSignatureObject->getBillPlzId()]);

        if($billPlz->state === 'paid') {
            $status = ApprovalStatus::APPROVED;
        }


        if($billPlz->state === 'due') {
            $status = $billplzXSignatureObject->getStatus() === 'failed' ? ApprovalStatus::REJECTED : ApprovalStatus::PENDING_VERIFICATION;
        }

        if($transaction->status !== ApprovalStatus::COMPLETED && $transaction->status !== ApprovalStatus::APPROVED){
            $this->updatesTransactionStatus->execute($transaction, $status);
        }

        if($transaction->owner instanceof Wallet) {
            $this->updatesWalletBalance->execute($transaction->owner, $transaction->amount);
        }

        $booking = $transaction->owner instanceof Booking ? $transaction->booking : (count($transaction->owner->owner->bookings()->get())? $transaction->owner->owner->bookings()->orderBy('id', 'DESC')->first(): null);

        if($transaction->owner instanceof Booking && $booking && $transaction){
            $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction);
        }

        if($booking){
            CreateInvoiceTransactionV2CommandJob::dispatch($booking);
        }

//        if ($transaction->type == TransactionType::PAYMENT) {
//            $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction);
//        }

        $token = Auth::fromUser(User::find(1));
        $request->headers->set('Authorization', 'Bearer '.$token);

        $marking = $transaction->owner instanceof Booking ? $transaction->booking->marking : (count($transaction->owner->owner->bookings()->get())? $transaction->owner->owner->bookings()->orderBy('id', 'DESC')->first()->marking: null);

        return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $marking ?? null, 'transaction' => $transaction, 'status' => $status]);

    }
}
