<?php

namespace App\Lib;

use App\Constants\Status;
use App\Models\Reward;
use App\Models\Transaction;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;

class UserNetwork
{
    public function getUsersLevelWise(User $user)
    {
        $cacheKey = 'user:' . $user->id . ':network';

        $data = Cache::get($cacheKey);
        if ($data && ($user->is_network_updated == false)) {
            return $data;
        }

        $network = collect();

        $user->load('lowers.level');

        $queue = collect();
        $user->distance = 0;
        $queue->push($user);

        while (!$queue->isEmpty()) {
            $newQueue = collect();

            foreach ($queue as $member) {
                $member->load(['lowers', 'lowers.level']);
                foreach ($member->lowers as $referral) {
                    $referral->distance = $member->distance + 1;
                    $newQueue->push($referral);
                    $network->push($referral);
                }
            }

            $queue = $newQueue;
        }

        foreach ($network as $item) {
            if ($item->lowers) {
                unset($item->lowers);
            }
        }

        Cache::put($cacheKey, $network, 120);

        File::put('network.json', json_encode($network));
        return $network;
    }

    public function getLevelWiseUserCount(User $user)
    {
        $network = $this->getUsersLevelWise($user);

        $result = [];
        for ($i = 1; $i <= 7; $i++) {
            $result["level{$i}"]['total'] = $network->where('distance', $i)->count();
            $result["level{$i}"]['fullyActivated'] = $network->where('distance', $i)->where('network_status', Status::NetworkFullActivated)->count();
            $result["level{$i}"]['partiallyActivated'] = $network->where('distance', $i)->where('network_status', Status::NetworkPlanPurchased)->count();
            $result["level{$i}"]['networkPending'] = $network->where('distance', $i)->where('network_status', Status::NetworkPending)->count();
        }

        return $result;
    }

    public function getActiveUsersLevel(User $user)
    {
        return $this->getUsersLevelWise($user)->where('network_status', Status::NetworkFullActivated);
    }

    public function getPartiallyActiveUsersLevel(User $user)
    {
        return $this->getUsersLevelWise($user)->where('network_status', Status::NetworkPlanPurchased);
    }

    public function getInactiveUsersLevel(User $user)
    {
        return $this->getUsersLevelWise($user)->where('network_status',  Status::NetworkPending);
    }

    public function getRewardedUserList(User $user)
    {
        $network = $this->getActiveUsersLevel($user);
        $rewards = Reward::with("user")->whereIn('user_id', $network->pluck('id'))
            ->where('status', Status::Approved)->get();
        return $rewards;
    }

    public function getDonarList(User $user)
    {
        $referralUsers = collect();
        $user->load(['referral', 'referral.referral', 'referral.referral.referral', 'referral.referral.referral.referral', 'referral.referral.referral.referral.referral', 'referral.referral.referral.referral.referral.referral', 'referral.referral.referral.referral.referral.referral.referral']);
        $referral = $user->referral;
        $refcount = 0;

        while ($referral) {
            if ($refcount >= 7) {
                break;
            }
            $referralUsers->push($referral);
            $refcount++;
            $referral = $referral->referral;
        }

        if ($refcount < 7) {
            $remaining = 7 - $refcount;
            $users = User::where('dummy_person', true)->whereNotIn('username',['admin'])->with('bankDetail')->latest()->take($remaining)->get();
            $referralUsers = $referralUsers->merge($users);
        }

        return $referralUsers;
    }

    public function getRewardUsersLevel(User $user)
    {
        $network_ids = $this->getActiveUsersLevel($user)->pluck('id');
        $network = User::whereIn('id', $network_ids)
            ->with(['transactions' => function ($query) {
                $query->where('remark', Status::CommissionReward);
            }])
            ->get()
            ->filter(function ($user) {
                return $user->transactions->sum('amount') > 0;
            })
            ->map(function ($user) {
                $user->total_reward = $user->transactions->sum('amount');
                return $user;
            });

        return $network;
    }

    public function filterNetworkData(Collection $data)
    {
        $request = request();

        return $data->filter(function ($item) use ($request) {
            $matchesSearch = true;
            $matchesDistance = true;

            // Search filter
            if ($request->search) {
                $searchTerm = $request->search;
                $matchesSearch = stripos($item->firstname, $searchTerm) !== false || stripos($item->lastname, $searchTerm) !== false || stripos($item->system_id, $searchTerm) !== false || stripos($item->email, $searchTerm) !== false || stripos($item->username, $searchTerm) !== false || stripos($item->mobile, $searchTerm) !== false;
            }

            // Distance filter
            if ($request->distance) {
                $matchesDistance = $item->distance == $request->distance;
            }

            return $matchesSearch && $matchesDistance;
        });
    }

    public function filterRewardData(Collection $data)
    {
        $request = request();
        return $data->filter(function ($item) use ($request) {
            $matchesSearch = true;
            $matchesDistance = true;

            // Search filter
            if ($request->search) {
                $searchTerm = $request->search;
                $matchesSearch = stripos($item->user->firstname, $searchTerm) !== false || stripos($item->user->lastname, $searchTerm) !== false || stripos($item->user->system_id, $searchTerm) !== false || stripos($item->user->email, $searchTerm) !== false || stripos($item->user->username, $searchTerm) !== false || stripos($item->user->mobile, $searchTerm) !== false;
            }

            // Distance filter
            if ($request->distance) {
                $matchesDistance = $item->distance == $request->distance;
            }

            return $matchesSearch && $matchesDistance;
        });
    }
}
