<?php

namespace App\Classes\Modules\Vouchers\ControllersLogic;


use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Vouchers\Processors\CreateVoucherProcessor;
use App\Classes\Modules\Vouchers\Services\Voucherify\FetchesVoucherifyVoucher;
use App\Classes\Modules\Vouchers\Services\Voucherify\ValidatesVoucherifyVoucher;
use App\Classes\Modules\Vouchers\Services\Voucherify\CreatesVoucherifyVoucherInACampaign;
use App\Classes\Modules\Vouchers\Services\Voucherify\ListsVoucherifyVouchers;
use App\Classes\Modules\Vouchers\Services\Voucherify\FetchesVoucherifyCampaign;
use App\Classes\Modules\Vouchers\Processors\Voucherify\NewCustomerToVoucherifyProcessor;
use App\Classes\Modules\Vouchers\Services\CreatesVoucher;
use App\Classes\Modules\Vouchers\Services\FetchesVoucher;
use App\Classes\Modules\Vouchers\Services\UpdatesVoucherCampaign;
use App\Classes\Modules\Rewards\Services\CreatesUserReward;
use App\Classes\Modules\KeyValuePairs\Services\CreatesKeyValuePair;
use App\Classes\Modules\KeyValuePairs\Services\UpdatesKeyValuePair;
use App\Classes\Modules\Vouchers\Standards\Rules\CanCreateVoucher;
use App\Classes\Modules\Vouchers\DataTransferObjects\ValidateVoucherifyVoucherObject;
use App\Classes\Modules\Vouchers\DataTransferObjects\CreateVoucherObject;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\KeyValuePairObject;
use App\Classes\Modules\Vouchers\DataTransferObjects\VoucherCampaignObject;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Classes\ValueObjects\Constants\Vouchers;
use App\Models\User;
use App\Models\VoucherCampaign;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;


class CreateVoucherLogic extends AbstractControllerLogic
{
    /**
     * @return array
     */
    protected function notification():array {
        return [
            'title' => 'Create Voucher',
            'message' => 'You have successfully created a voucher'
        ];
    }

    /** @var ValidatesVoucherifyVoucher */
    private $validatesVoucherifyVoucher;

    /** @var CreatesVoucherifyVoucherInACampaign */
    private $createsVoucherifyVoucherInACampaign;

    /** @var ListsVoucherifyVouchers */
    private $listsVoucherifyVouchers;

    /** @var FetchesVoucherifyCampaign */
    private $fetchesVoucherifyCampaign;

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

    /** @var CreateVoucherProcessor */
    private $createVoucherProcessor;

    /** @var CanCreateVoucher */
    private $canCreateVoucher;

    /** @var CreatesKeyValuePair */
    private $createsKeyValuePair;

    /** @var UpdatesKeyValuePair */
    private $updatesKeyValuePair;

    /** @var UpdatesVoucherCampaign */
    private $updatesVoucherCampaign;

    /** @var NewCustomerToVoucherifyProcessor */
    private $newCustomerToVoucherifyProcessor;

    /**
     * CreateVoucherLogic constructor.
     * @param ValidatesVoucherifyVoucher $validatesVoucherifyVoucher
     * @param CreatesVoucherifyVoucherInACampaign $createsVoucherifyVoucherInACampaign
     * @param ListsVoucherifyVouchers $listsVoucherifyVouchers
     * @param FetchesVoucherifyCampaign $fetchesVoucherifyCampaign
     * @param CreatesUserReward $createsUserReward
     * @param CreateVoucherProcessor $createVoucherProcessor
     * @param CanCreateVoucher $canCreateVoucher
     * @param CreatesKeyValuePair $createsKeyValuePair
     * @param UpdatesKeyValuePair $updatesKeyValuePair
     * @param UpdatesVoucherCampaign $updatesVoucherCampaign
     * @param NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor
     */
    public function __construct(CreatesUserReward $createsUserReward, ValidatesVoucherifyVoucher $validatesVoucherifyVoucher, CreateVoucherProcessor $createVoucherProcessor, CreatesVoucherifyVoucherInACampaign $createsVoucherifyVoucherInACampaign, CanCreateVoucher $canCreateVoucher, ListsVoucherifyVouchers $listsVoucherifyVouchers, FetchesVoucherifyCampaign $fetchesVoucherifyCampaign, CreatesKeyValuePair $createsKeyValuePair, UpdatesKeyValuePair $updatesKeyValuePair, UpdatesVoucherCampaign $updatesVoucherCampaign, NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor)
    {
        $this->createsUserReward = $createsUserReward;
        $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher;
        $this->createVoucherProcessor = $createVoucherProcessor;
        $this->createsVoucherifyVoucherInACampaign = $createsVoucherifyVoucherInACampaign;
        $this->canCreateVoucher = $canCreateVoucher;
        $this->listsVoucherifyVouchers = $listsVoucherifyVouchers;
        $this->fetchesVoucherifyCampaign = $fetchesVoucherifyCampaign;
        $this->createsKeyValuePair = $createsKeyValuePair;
        $this->updatesKeyValuePair = $updatesKeyValuePair;
        $this->updatesVoucherCampaign = $updatesVoucherCampaign;
        $this->newCustomerToVoucherifyProcessor = $newCustomerToVoucherifyProcessor;
    }

    /**
     * @param Request $request
     * @return JsonResponse
     * @throws MalformedRequestException
     */
    public function logic(Request $request) : JsonResponse
    {
        //Process request input
        $userParam = User::where('id', $request->input('userId'))->first();
        $voucherCodeInput = $request->input('voucherCode');
        $voucherCampaign = VoucherCampaign::where('name', $voucherCodeInput)->where('is_voucher_code_unique', true)->first();

        $userRewardObject = new CreateVoucherObject(
            $voucherCodeInput,
            $userParam,
            $voucherCampaign !== null ? true : false,
        );
        $this->canCreateVoucher->passes($userRewardObject);

        //Initialize variables
        $result = null;

        $user = Auth::user(); /** @var User $user */
        if($user && isset($user->type) && in_array($user->type, RoleTypes::ADMIN_ROLES) && $userParam){
            $user = User::where('id', $userParam->id)->first();
        }
        else{
            $user = $userParam ? $userParam : $user;
        }

        //Voucherify - To check if user exist at Voucherify, create if not exist
        $voucherify_entity = $user->voucherifyEntities()->first();
        if(!$voucherify_entity){
            $this->newCustomerToVoucherifyProcessor->execute($user->company()->first()->id, $user, false);
        }

        //Voucherify - creates new voucher at voucherify
        if ($voucherCampaign) {
            $result = $this->newVoucherifyVoucherIssuanceHandler($voucherCodeInput);
        }
        else //Voucherify - validates existing voucher at voucherify
        {
            $result = $this->existingVoucherValidationHandler($voucherCodeInput, $user);
        }

        //Save validated voucher info to local DB
        if(!isset($result['reason'] ) && $result['validatedVoucherCode']){
            $voucher = $this->createVoucherProcessor->execute($user, $result['validatedVoucherCode'], $result['voucherCampaignId'] ?? null);
            $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
            if($voucherCount == 0){
                $result = $this->createsUserReward->execute(null, $user, $voucher->id);
            }
            else{
                $result['reason'] = 'Voucher already added';
            }
        }

        //Unset property that is set for internal processing
        unset($result['validatedVoucherCode']);
        unset($result['voucherCampaignId']);

        return $this->response(['data' => $result]);
    }

    private function newVoucherifyVoucherIssuanceHandler($voucherCodeInput){
        $result = [];
        $total = 0;
        $limit = 0;
        $voucherCampaign = VoucherCampaign::where('slug', strtolower($voucherCodeInput))->first();

        if($voucherCampaign){
            $result['voucherCampaignId'] = $voucherCampaign->id;
            $voucherList = $this->listsVoucherifyVouchers->execute($voucherCampaign->campaign_id);  //Remote Voucherify
            if (isset($voucherList->vouchers) && isset($voucherList->total)) {
                $total  = $voucherList->total;
            }
            $voucherifyCampaign = $this->fetchesVoucherifyCampaign->execute($voucherCampaign->campaign_id);  //Remote Voucherify

            $campaignName = $voucherifyCampaign->name;
            $campaignId = $voucherifyCampaign->id;
            $campaignMetadata = $voucherifyCampaign->metadata;

            if(isset($campaignMetadata) && isset($campaignMetadata['voucher_limit_per_month'])){
                $limit = (int) $campaignMetadata['voucher_limit_per_month'];
            }
            else{
                $limit = 9999;
            }

            $voucherCampaignObject= new VoucherCampaignObject($campaignId, $campaignName, null, $limit);
            $this->updatesVoucherCampaign->execute($voucherCampaign, $voucherCampaignObject);

            if($total >= $voucherCampaign['limit_per_month']){
                $result['reason'] = 'Quota for the month exceeded';
            }
            else{
                //To track voucher issuance by admin
                $total  = $voucherList->total + 1;
                $key = strtoupper($campaignId)."_".strtoupper(Carbon::now()->format('M'))."_TOTAL";
                $keyValuePairObject = new KeyValuePairObject(
                    $key,
                    $total
                );
                $metadata = $voucherCampaign->attributesKVP()->where('key', $key)->first();
                if($metadata){
                    $this->updatesKeyValuePair->execute($metadata, $keyValuePairObject);
                }
                else{
                    $this->createsKeyValuePair->execute($voucherCampaign, $keyValuePairObject);
                }

                $createdVoucher = $this->createsVoucherifyVoucherInACampaign->execute($campaignName, "");  //Remote Voucherify
                if(isset($createdVoucher->message)){
                    $result['reason'] = $createdVoucher->message;
                }
                else{
                    $result['validatedVoucherCode'] = $createdVoucher->code;
                }
            }
        }
        else{
            $result['reason'] = 'Campaign not set';
        }

        return $result;
    }

    private function existingVoucherValidationHandler(string $voucherCodeInput, User $user){
        $result = [];
        $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $voucherCodeInput, 0, 0, $user, true); //When adding voucher into user account, isNewOrder is marked as true, some campaign has validation on this property
        $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject); //Remote Voucherify

        if(isset($voucherifyVoucherValidated->reason)){
            $result['reason'] = $voucherifyVoucherValidated->reason;
        }
        else if($voucherifyVoucherValidated){
            $result['validatedVoucherCode'] = $voucherCodeInput;
        }
        return $result;
    }
}
