<?php

namespace App\Classes\Modules\Vouchers\Processors\Voucherify;

use App\Classes\Modules\Vouchers\Services\CreatesVoucher;
use App\Classes\Modules\Vouchers\Services\FetchesVoucher;
use App\Classes\Modules\Vouchers\DataTransferObjects\VoucherObject;
use App\Classes\Modules\Vouchers\Services\Voucherify\RedeemsVoucherifyVoucher;
use App\Classes\Modules\Vouchers\Services\CreatesVoucherRedemption;
use App\Classes\Modules\Vouchers\DataTransferObjects\RedeemVoucherifyVoucherObject;
use App\Classes\Modules\Vouchers\DataTransferObjects\CreateVoucherifyOrderObject;
use App\Classes\Modules\Vouchers\Services\Voucherify\CreatesVoucherifyOrder;
use App\Classes\Modules\Vouchers\Services\CreatesVoucherEntityMapping;
use App\Classes\Modules\Vouchers\DataTransferObjects\VoucherEntityObject;
use App\Classes\Modules\Rewards\Services\CreatesUserReward;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\VoucherifyEntityType;
use App\Models\User;
use App\Models\Transaction;
use App\Models\Voucher;
use Illuminate\Support\Facades\Log;

class BookingToVoucherifyProcessor
{
    /** @var CreatesVoucher */
    private $createsVoucher;

    /** @var FetchesVoucher */
    private $fetchesVoucher;

    /** @var CreatesVoucherRedemption */
    private $createsVoucherRedemption;

    /** @var RedeemsVoucherifyVoucher */
    private $redeemsVoucherifyVoucher;

    /** @var CreatesVoucherifyOrder */
    private $createsVoucherifyOrder;

    /** @var CreatesVoucherEntityMapping */
    private $createsVoucherEntityMapping;

    /** @var CreatesUserReward */
    private $createsUserReward;

    /**
     * BookingToVoucherifyProcessor constructor.
     * @param CreatesVoucher $createsVoucher
     * @param FetchesVoucher $fetchesVoucher
     * @param CreatesVoucherRedemption $createsVoucherRedemption
     * @param RedeemsVoucherifyVoucher $redeemsVoucherifyVoucher
     * @param CreatesVoucherifyOrder $createsVoucherifyOrder
     * @param CreatesVoucherEntityMapping $createsVoucherEntityMapping
     * @param CreatesUserReward $createsUserReward
    */
    public function __construct(CreatesVoucher $createsVoucher, FetchesVoucher $fetchesVoucher, CreatesVoucherRedemption $createsVoucherRedemption, RedeemsVoucherifyVoucher $redeemsVoucherifyVoucher, CreatesVoucherifyOrder $createsVoucherifyOrder, CreatesVoucherEntityMapping $createsVoucherEntityMapping, CreatesUserReward $createsUserReward)
    {
        $this->createsVoucher = $createsVoucher;
        $this->fetchesVoucher = $fetchesVoucher;
        $this->createsVoucherRedemption = $createsVoucherRedemption;
        $this->redeemsVoucherifyVoucher = $redeemsVoucherifyVoucher;
        $this->createsVoucherifyOrder = $createsVoucherifyOrder;
        $this->createsVoucherEntityMapping = $createsVoucherEntityMapping;
        $this->createsUserReward = $createsUserReward;
    }


    /**
     * @param User $user
     * @param Transaction $transaction
     * @param int $companyId
     * @param float $amount
     * @param float $voucherDiscountAmount
     * @param string $voucherCode
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     * @throws \Voucherify\ClientException
     */
    public function execute(User $user, Transaction $transaction, int $companyId, float $amount, float $voucherDiscountAmount, ?string $voucherCode = "")
    {
        try{
            $voucherify_customer_id = "";
            $voucherify_order_id = "";
            if($voucherCode){
                $redeemVoucherifyVoucherObject = new RedeemVoucherifyVoucherObject($companyId, $transaction->id, $voucherCode, $amount, $user);
                $redeemVoucherResult = $this->redeemsVoucherifyVoucher->execute($redeemVoucherifyVoucherObject);
                $redeemedVoucher = $redeemVoucherResult->voucher;
                $redemptionId = $redeemVoucherResult->id;

                if($redeemVoucherResult && isset($redeemVoucherResult->order)){
                    $voucherify_order_id = $redeemVoucherResult->order->id;
                }

                if($redeemVoucherResult && isset($redeemVoucherResult->customer)){
                    $voucherify_customer_id = $redeemVoucherResult->customer->id;
                }

                $voucher = $this->recordVoucherInfo($redeemedVoucher, $transaction);
                $this->createsVoucherRedemption->execute($transaction, $voucher, $redemptionId, $voucherDiscountAmount);
                $this->recordVoucherForUserInfo($user, $voucher);
            }
            else{
                $createVoucherifyOrderObject = new CreateVoucherifyOrderObject($user, $companyId, $transaction->id, $amount, true, $transaction->type == TransactionType::TOP_UP);
                $createVoucherufyOrderResult = $this->createsVoucherifyOrder->execute($createVoucherifyOrderObject);

                if($createVoucherufyOrderResult && isset($createVoucherufyOrderResult->id)){
                    $voucherify_order_id = $createVoucherufyOrderResult->id;
                    if(isset($createVoucherufyOrderResult->customer)){
                        $voucherify_customer_id = $createVoucherufyOrderResult->customer->id;
                    }
                }
            }

            $this->recordVoucherifyOrderInfo($voucherify_order_id, $transaction);
            $this->recordVoucherifyCustomerInfo($voucherify_customer_id, $user);

        } catch (\Exception $e) {
            Log::error($e);
        }
    }

    private function recordVoucherInfo(object $redeemedVoucher){
        //Create records at 3 tables
        $voucherValue = isset($redeemedVoucher->discount->amount_off) ? $redeemedVoucher->discount->amount_off : $redeemedVoucher->discount->percent_off;
        $voucherType = $redeemedVoucher->discount ? $redeemedVoucher->discount->type : null;

        $voucherObject= new VoucherObject($redeemedVoucher->code, isset($redeemedVoucher->metadata->name) ? $redeemedVoucher->metadata->name : "", $voucherType, $voucherValue);
        $voucher = $this->createsVoucher->execute($voucherObject);
        if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]);

        return $voucher;
    }

    private function recordVoucherForUserInfo(User $user, Voucher $voucher){
        //create reward to user (user_reward)
        $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
        if($voucherCount == 0){
            $this->createsUserReward->execute(null, $user, $voucher->id);
        }
    }

    private function recordVoucherifyOrderInfo(string $voucherify_order_id, Transaction $transaction){
        //Update Database - 1 table
        if($voucherify_order_id){
            $voucherify_entity = $transaction->voucherifyEntities()->first();
            if(!$voucherify_entity){
                $voucherEntityObject = new VoucherEntityObject($voucherify_order_id, VoucherifyEntityType::ORDER);
                $this->createsVoucherEntityMapping->execute($transaction, $voucherEntityObject);
            }
        }
    }

    private function recordVoucherifyCustomerInfo(string $voucherify_customer_id, User $user){
        //Update Database - 1 table
        if($voucherify_customer_id){
            $voucherify_entity = $user->voucherifyEntities()->first();
            if(!$voucherify_entity){
                $voucherEntityObject = new VoucherEntityObject($voucherify_customer_id, VoucherifyEntityType::CUSTOMER);
                $this->createsVoucherEntityMapping->execute($user, $voucherEntityObject);
            }
        }
    }
}
