<?php

namespace App\Lib;

use App\Constants\Status;
use App\Models\Plan;
use App\Models\Reward;
use App\Models\Transaction;
use App\Models\User;
use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\File;

class Matrix
{
    /**
     * User who purchase the plan
     *
     * @var object
     */
    private $user;


    /**
     * Plan which has purchased by the user
     *
     * @var object
     */
    private $plan;


    /**
     * Matrix height
     *
     * @var integer
     */
    private $height;


    /**
     * Matrix width
     *
     * @var integer
     */
    private $width;


    /**
     * All transactions data of this process
     *
     * @var array
     */
    private $transactions = [];


    /**
     * Transaction number of transactions
     *
     * @var array
     */
    private $trx;


    protected function __construct($user, $plan, $trx = null)
    {
        $general = gs();
        $this->user = $user;
        $this->plan = $plan;
        $this->height = $general->matrix_height;
        $this->width = $general->matrix_width;
        $this->trx = $trx ?? getTrx();

        //throw exception
        // $this->getException();
    }

    public static function set($user)
    {
        return new Matrix($user, Plan::first());
    }

    public function setMatrix(User $user)
    {
        $this->user = $user;
        $this->plan = Plan::first();
    }

    /**
     * Purchase user plan
     *
     * @return void;
     */
    public function addUserToNetwork()
    {
        $user = $this->user;
        $plan = $this->plan;
        $user->plan_id = $plan->id;
        $user->rank = 0;
        $user->save();

        // $this->getPosition();
        // increemnt network size
        $upper = $user->upper;

        while ($upper) {
            $upper->increment('active_network_size', 1);
            $upper->update(['is_network_updated' => true]);
            $this->updateRank($upper);
            $upper = $upper->upper;
        }
    }

    public function updateRank(?User $user)
    {
        if (!$user) return;

        $activatedLowers = $user->lowers()->whereNotNull('network_activated_at')->get();

        if ($activatedLowers->count() == $this->width) {
            $newRank = $activatedLowers->pluck('rank')->sort()->first() + 1;
            $this->checkAndApplyForReward($user, $newRank);
            $user->rank = $newRank;
            $user->save();
        }

        $this->updateRank($user->upper);
    }

    public function checkAndApplyForReward(User $user, int $newRank)
    {
        if ($user->rank == $newRank) return;

        $reward = RewardLib::getLevelReward($newRank);

        if ($user->network_activated_at == null) {
            throw new Exception('Network not activated yet user queued for getting reward.');
        }

        if ($user->network_activated_at->addDays($reward['time']) >= now()) {
            Reward::create([
                'user_id' => $user->id,
                'prize' => $reward['reward'],
                'rank' => $newRank,
                'status' => Status::Pending
            ]);
        };
    }

    public function getPosition()
    {
        if (!$this->user->ref_by) {
            return false;
        }

        $user = $this->user;
        $referral = $this->user->referral;
        $isBreak = false;

        // Direct position
        $nextPosition = $this->nextPosition($referral->id);

        if ($nextPosition) {
            $user->position_id  = $referral->id;
            $user->position     = $nextPosition;
            $user->save();
            return true;
        }

        for ($level = 1; $level < 100000; $level++) {

            $myref = $this->showPositionBelow($referral->id);

            $next =   $myref;
            for ($i = 1; $i < $level; $i++) {
                $next = array();
                foreach ($myref as $uu) {
                    $n = $this->showPositionBelow($uu);
                    $next = array_merge($next, $n);
                }
                $myref = $next;
            }

            foreach ($next as $uu) {
                $nextPosition = $this->nextPosition($uu);
                if ($nextPosition) {
                    $user->position_id = $uu;
                    $user->position = $nextPosition;
                    $user->save();
                    $isBreak = true;
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Get all immediate below users
     *
     * @param integer $id
     * @return array
     */
    private function showPositionBelow($id)
    {
        return User::where('position_id', $id)->pluck('id')->toArray();
    }

    /**
     * Get the next position
     *
     * @param integer $id
     * @return integer
     */
    private function nextPosition($id)
    {
        $count = User::where('position_id', $id)->count();

        if ($count < $this->width) {
            return $count + 1;
        }
        return 0;
    }

    // /**
    //  * Give rewards commission to user
    //  *
    //  * @return void;
    //  */
    // protected function giveRewardsCommission()
    // {
    //     $user = $this->user;

    //     $rewards = RewardLib::rewards();

    //     $referral = $user->referral;

    //     foreach ($rewards as $key => $value) {

    //         if (!$referral) {
    //             break;
    //         }


    //         if (now()->lte(Carbon::parse($referral->network_activated_at)->addDays($value['time']))) {
    //             $referral->increment('balance', 200);
    //             $this->pushTransaction([
    //                 'amount' => 200,
    //                 'post_balance' => $referral->balance,
    //                 'trx_type' => '+',
    //                 'details' => 'Rewards commission from user '. $this->user->username,
    //                 'remark' => Status::CommissionReward,
    //                 'user_id' => $referral->id,
    //             ]);
    //         }

    //         $referral = $referral->referral;
    //     }
    // }

    // /**
    //  * Give direct referral commission to referrer
    //  *
    //  * @return void
    //  */
    // public function referralCommission()
    // {

    //     $user = $this->user;
    //     $referral = $user->referral;
    //     $plan = $this->plan;
    //     if ($referral) {
    //         $referral->balance += $plan->referral_bonus;
    //         $referral->save();

    //         //Push to transactions
    //         $this->pushTransaction([
    //             'user_id' => $referral->id,
    //             'amount' => showAmount($plan->referral_bonus),
    //             'post_balance' => showAmount($referral->balance),
    //             'trx_type' => '+',
    //             'remark' => Status::CommissionReferral,
    //             'details' => 'Referral commission from ' . $user->username,
    //         ]);

    //         notify($referral, 'REFERRAL_COMMISSION', [
    //             'amount' => showAmount($plan->referral_bonus),
    //             'username' => $user->username,
    //             'currency' => gs()->cur_text,
    //             'trx' => $this->trx,
    //             'post_balance' => showAmount($referral->balance),
    //         ]);
    //     }
    // }

    // /**
    //  * Give direct level commission to upper
    //  *
    //  * @return void
    //  */
    // public function levelCommission(): void
    // {
    //     $user = $this->user;

    //     $commissions = $this->plan->level;
    //     for ($i = 0; $i < $this->height; $i++) {
    //         $commission = @$commissions[$i];
    //         if (!$commission) {
    //             break;
    //         }
    //         $upper = $user->upper;
    //         if (!$upper) {
    //             break;
    //         }
    //         $upper->balance += $commission->amount;
    //         $upper->save();

    //         //push to transactions
    //         $this->pushTransaction([
    //             'user_id' => $upper->id,
    //             'amount' => showAmount($commission->amount),
    //             'post_balance' => showAmount($upper->balance),
    //             'trx_type' => '+',
    //             'remark' => Status::CommissionLevel,
    //             'details' => 'Level ' . ($i + 1) . ' commission from ' . $user->username,
    //         ]);

    //         $user = $upper;
    //     }
    // }

    // /**
    //  * Push all transaction data to transactions
    //  *
    //  * @param array $data
    //  * @return void
    //  */
    // private function pushTransaction($data)
    // {
    //     $transactions[] = [
    //         'user_id' => $data['user_id'],
    //         'amount' => $data['amount'],
    //         'post_balance' => $data['post_balance'],
    //         'charge' => 0,
    //         'trx_type' => $data['trx_type'],
    //         'details' => $data['details'],
    //         'remark' => @$data['remark'],
    //         'trx' => $this->trx,
    //         'created_at' => now(),
    //     ];
    //     $this->transactions = array_merge($this->transactions, $transactions);
    // }

    // /**
    //  * Store transactions to database
    //  *
    //  * @param array $data
    //  * @return void
    //  */
    // public function storeTransactions()
    // {
    //     $transactions = $this->transactions;
    //     Transaction::insert($transactions);
    // }

    // /**
    //  * All exception of this process
    //  *
    //  * @return void
    //  */
    // private function getException()
    // {
    // if ($this->user->plan) {
    //     $message = 'You can\'t buy plan twice';
    //     $this->throwException($message);
    // }

    // if ($this->user->balance < $this->plan->price) {
    //     $message = 'You don\'t have sufficient balance';
    //     $this->throwException($message);
    // }

    // $referral = $this->user->referral;

    // if ($referral) {
    //     if ($referral->plan_id != $this->plan->id) {
    //         $message = 'You have to purchase a plan which have purchased your referrer';
    //         $this->throwException($message);
    //     }
    // }
    // }

    // /**
    //  * All exception will throw from here
    //  *
    //  * @return void
    //  */
    // private function throwException($message)
    // {
    //     throw new Exception($message);
    // }
}
