<?php

namespace App\Classes\Modules\Milestones\Processors;

use App\Classes\Modules\Vouchers\Services\Voucherify\CreatesVoucherifyVoucher;
use App\Classes\Modules\Vouchers\Services\CreatesVoucher;
use App\Classes\Modules\Vouchers\Services\FetchesVoucher;
use App\Classes\Modules\Vouchers\Services\Voucherify\FetchesVoucherifyVoucher;
use App\Classes\Modules\Vouchers\Services\Voucherify\ValidatesVoucherifyVoucher;
use App\Classes\Modules\Vouchers\DataTransferObjects\ValidateVoucherifyVoucherObject;
use App\Classes\Modules\Vouchers\DataTransferObjects\VoucherObject;
use App\Classes\Modules\Milestones\Services\FetchesMilestone;
use App\Classes\Modules\Milestones\Services\CreatesMilestoneProgress;
use App\Classes\Modules\Rewards\Services\CreatesUserReward;
use App\Classes\ValueObjects\Constants\RewardType;
use App\Models\Milestone;
use App\Models\MilestoneProgress;
use App\Models\Reward;
use App\Models\User;
use Illuminate\Support\Facades\Log;

class CheckMilestonesForRewardProcessor
{
    /** @var CreatesVoucherifyVoucher */
    private $createsVoucherifyVoucher;

    /** @var CreatesVoucher */
    private $createsVoucher;

    /** @var CreatesMilestoneProgress */
    private $createsMilestoneProgress;

    /** @var FetchesMilestone */
    private $fetchesMilestone;

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

    /** @var FetchesVoucherifyVoucher */
    private $fetchesVoucherifyVoucher;

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

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

    /**
     * CheckMilestonesForRewardProcessor constructor.
     * @param CreatesVoucherifyVoucher $createsVoucherifyVoucher
     * @param CreatesVoucher $createsVoucher
     * @param FetchesMilestone $fetchesMilestone
     * @param CreatesMilestoneProgress $createsMilestoneProgress
     * @param CreatesUserReward $createsUserReward
     * @param FetchesVoucherifyVoucher $fetchesVoucherifyVoucher
     * @param FetchesVoucher $fetchesVoucher
     * @param ValidatesVoucherifyVoucher $validatesVoucherifyVoucher
    */
    public function __construct(CreatesVoucherifyVoucher $createsVoucherifyVoucher, CreatesVoucher $createsVoucher, FetchesMilestone $fetchesMilestone, CreatesMilestoneProgress $createsMilestoneProgress, CreatesUserReward $createsUserReward, FetchesVoucherifyVoucher $fetchesVoucherifyVoucher, FetchesVoucher $fetchesVoucher, ValidatesVoucherifyVoucher $validatesVoucherifyVoucher)
    {
        $this->createsVoucherifyVoucher = $createsVoucherifyVoucher;
        $this->createsVoucher = $createsVoucher;
        $this->fetchesMilestone = $fetchesMilestone;
        $this->createsMilestoneProgress = $createsMilestoneProgress;
        $this->createsUserReward = $createsUserReward;
        $this->fetchesVoucherifyVoucher = $fetchesVoucherifyVoucher;
        $this->fetchesVoucher = $fetchesVoucher;
        $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher;
    }


    /**
     * @param User $user
     * @param array $milestone_constants
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     * @throws \Voucherify\ClientException
     */
    public function execute(User $user, array $milestone_constants)
    {
        try{
            foreach($milestone_constants as $constant)
            {
                 //update milestone progress
                /** @var Milestone $milestone */
                $milestone = $this->fetchesMilestone->execute(['name' => $constant]);
                $result = null;
                if($user->milestoneProgress->count() > 0){
                    $result = $user->milestoneProgress->where('milestone_id', $milestone->id)->first();
                }

                if(!$result){
                    $result = $this->createsMilestoneProgress->execute($milestone, $user->id);
                }

                if($result){
                    //fetch completed miletones
                    $completedMilestones = MilestoneProgress::where('user_id', $user->id)->pluck('milestone_id');
                    $rewards = $milestone->rewards;

                    //check for rewards that have a corresponding milestone count
                    if(count($rewards) > 0){
                        $this->checkMilestoneForReward($user, $completedMilestones, $rewards);
                    }
                }
            }
        } catch (\Exception $e) {
            Log::error($e);
        }
    }

    /**
     * @param User $user
     * @param object $completedMilestones
     * @param object $rewards
     * @return void
     */
    private function checkMilestoneForReward(User $user, object $completedMilestones, object $rewards)
    {
        $milestoneIds = $rewards[0]->milestones->pluck('id');
        if ($milestoneIds->intersect($completedMilestones)->count() >= count($rewards[0]->milestones)) {
            /** @var Reward $reward */
            foreach ($rewards as $reward) {
                $result = null;
                if (!$user->rewards->contains('reward_id', $reward->id)) {
                    $voucherId = 0;
                    $voucherName = '';
                    $voucherType = "";
                    if($reward->type == RewardType::REWARD_AMOUNT){
                        //Voucherify - Create Voucher
                        $result = $this->createsVoucherifyVoucher->execute($user, intval($reward->value));
                    }
                    else{
                        //Voucherify - Validates Voucher
                        $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $reward->value, 0, 0, $user);
                        $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject);

                        if(!isset($voucherifyVoucherValidated->reason)){
                            //Voucherify - Get Voucher
                            $result = $this->fetchesVoucherifyVoucher->execute($user, $reward->value);
                        }
                    }

                    if($result && !isset($result->reason)){
                        $voucherName = isset($result->campaign) ? $result->campaign : $reward->name;
                        $voucherType = $result->discount->type;
                        $voucherValue = isset($result->discount->amount_off) ? $result->discount->amount_off : $result->discount->percent_off;
                        $voucherStartDate = $result->start_date;
                        $voucherEndDate = $result->expiration_date;

                        //create voucher
                        $voucherObject = new VoucherObject($result->code, isset($voucherName) ? $voucherName : "", null, $voucherType, $voucherValue, null, $voucherStartDate, $voucherEndDate);
                        $voucher = $this->createsVoucher->execute($voucherObject);
                        if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]);
                        $voucherId = $voucher->id;

                        //create reward to user (user_reward)
                        $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
                        if($voucherCount == 0){
                            $this->createsUserReward->execute($reward, $user, $voucherId);
                        }
                    }
                    else{
                        Log::info('CheckMilestonesForRewardProcessor: no voucher fetched or created for reward '. json_encode($reward));
                    }
                }
            }
        }
    }
}
