<?php

class Bonus extends controller
{
    private $A_data = array();
    private $B_data = array();

    private function getRevenue($id)
    {
        $tot = 0;
        $qry = $this->DB->dbQuery("SELECT M.uid, P.usd FROM member AS M JOIN (package AS P) ON (M.package_id=P.package_id) WHERE M.upline_id=?", [$id]);
        while ($obj = $this->DB->dbFetchObject($qry)) {
            $tmp = $this->getRevenue($obj->uid);

            if ($tmp) $tot += $tmp;
            $tot += $obj->usd;
        }

        return $tot;
    }

    public function revenue($id)
    {
        $downline = $this->DB->dbFetchAll("SELECT M.uid, P.usd, M.foot FROM member AS M JOIN (package AS P) ON (M.package_id=P.package_id) WHERE M.upline_id=?", [$id]);
        $left = $right = 0;
        foreach ($downline as $val) {
            if ($val['foot'] == 'Left') {
                $left = $this->getRevenue($val['uid']);
                $left += $val['usd'];
            } elseif ($val['foot'] == 'Right') {
                $right = $this->getRevenue($val['uid']);
                $right += $val['usd'];
            }
        }
        return ['left' => '$' . number_format($left), 'right' => '$' . number_format($right)];
    }

    /*----------------------------------- Sponsor ----------------------------- */
    public function sponsor($id)
    {
        $sponsor = $this->DB->dbFetchColumn("SELECT P.sponsor FROM member AS T LEFT JOIN(package AS P) ON (T.package_id=P.package_id) WHERE T.uid=? ", [$id]);

        try {
            $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

            if ($topupdate)
                $qry = $this->DB->dbFetchAll("SELECT M.uid, M.name, M.account_type, M.bba_address, P.package, DATE_FORMAT(M.registration_date, '%d-%m-%Y %H:%i') AS join_date, CONCAT('$',(P.usd*$sponsor)) AS bonus FROM member AS M JOIN(package AS P) ON (M.package_id=P.package_id) WHERE M.sponsor_id=? AND M.registration_date >= ? ORDER BY M.registration_date ASC", [$id, $topupdate]);
            else
                $qry = $this->DB->dbFetchAll("SELECT M.uid, M.name, M.account_type, M.bba_address, P.package, DATE_FORMAT(M.registration_date, '%d-%m-%Y %H:%i') AS join_date, CONCAT('$',(P.usd*$sponsor)) AS bonus FROM member AS M JOIN(package AS P) ON (M.package_id=P.package_id) WHERE M.sponsor_id=? ORDER BY M.registration_date ASC", [$id]);
            return ($qry);
        } catch (PDOException $e) {
            echo $e->getMessage();
        }
    }

    private function _totSponsor($id)
    {
        try {
            $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

            if ($topupdate)
                $sponsor = $this->DB->dbFetchColumn("SELECT SUM(P.usd*P.sponsor) AS bonus FROM member AS T LEFT JOIN(package AS P) ON (T.package_id=P.package_id)  WHERE T.sponsor_id=? AND T.registration_date>=?", [$id, $topupdate]);
            else
                $sponsor = $this->DB->dbFetchColumn("SELECT SUM(P.usd*P.sponsor) AS bonus FROM member AS T LEFT JOIN(package AS P) ON (T.package_id=P.package_id)  WHERE T.sponsor_id=?", [$id]);

            return $sponsor;
        } catch (PDOException $e) {
            echo $e->getMessage();
        }
    }

    public function totSponsor($id)
    {
        return $this->_totSponsor($id);
    }

    /*----------------------------------- Pairing ----------------------------- */
    private function getChild($pid, $foot = 'Left')
    {
        $qry = $this->DB->dbQuery("SELECT M.uid, DATE(M.registration_date) AS REG, P.usd, P.pair_usd FROM member AS M LEFT JOIN(package AS P)ON(M.package_id=P.package_id) WHERE M.upline_id=? ORDER BY M.registration_date ASC", [$pid]);
        if ($this->DB->dbQNumRow($qry)) {
            while ($obj = $this->DB->dbFetchObject($qry)) {

                $d = (new DateTime($obj->REG))->getTimestamp();

                if ($foot == 'Left') {
                    if (isset($this->A_data[$d])) $this->A_data[$d] += 1;
                    else $this->A_data[$d] = 1;
                } else {
                    if (isset($this->B_data[$d])) $this->B_data[$d] += 1;
                    else $this->B_data[$d] = 1;
                }
                $this->getChild($obj->uid, $foot);
            }
        }
    }

    private function getNode($pid, $max)
    {
        $level = 1;
        $qry = $this->DB->dbQuery("SELECT M.uid, M.foot, DATE(M.registration_date) AS REG, P.usd, P.pair_usd FROM member AS M LEFT JOIN(package AS P)ON(M.package_id=P.package_id) WHERE M.upline_id=? ORDER BY M.registration_date ASC", [$pid]);
        if ($this->DB->dbQNumRow($qry)) {
            while ($obj = $this->DB->dbFetchObject($qry)) {
                $reg = $obj->REG;

                $d = (new DateTime($reg))->getTimestamp();
                if ($obj->foot == 'Left') {
                    if (isset($this->A_data[$d])) $this->A_data[$d] += 1;
                    else $this->A_data[$d] = 1;
                } else if ($obj->foot == 'Right') {
                    if (isset($this->B_data[$d])) $this->B_data[$d] += 1;
                    else $this->B_data[$d] = 1;
                }
                $this->getChild($obj->uid, $obj->foot);
            }
        }
    }

    private function getpairing($id)
    {
        $data = [];
        $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

        if ($topupdate)
            $obj = $this->DB->dbFetchObject("SELECT M.uid, DATE(M.registration_date) REG, P.pair_level, P.pair_usd, P.max_pair FROM member AS M LEFT JOIN(package AS P)ON(M.package_id=P.package_id) WHERE M.uid=? AND M.registration_date >= ?", [$id, $topupdate]);
        else
            $obj = $this->DB->dbFetchObject("SELECT M.uid, DATE(M.registration_date) REG, P.pair_level, P.pair_usd, P.max_pair FROM member AS M LEFT JOIN(package AS P)ON(M.package_id=P.package_id) WHERE M.uid=?", [$id]);

        if ($obj) {
            $max = $obj->max_pair;
            $pairUsd = $obj->pair_usd;

            $day = new DateTime($obj->REG);

            $this->getNode($id, $max);
            ksort($this->A_data);
            ksort($this->B_data);


            $curdate = (new DateTime('now'))->getTimestamp();
            $no = 1;
            $pair = $left = $right = $remain_left = $remain_right = 0;
            $remains = '';

            while ($day->getTimestamp() <= $curdate) {

                $date = $day->format("Y-m-d");
                $d = $day->getTimestamp();

                if (array_key_exists($d, $this->A_data)) {
                    $left += $this->A_data[$d];
                }
                if (array_key_exists($d, $this->B_data)) {
                    $right += $this->B_data[$d];
                }

                if ($left && $right) {
                    $leftMax = $left; //*$pairUsd;                    
                    $rightMax = $right; //*$pairUsd;                    

                    if (($leftMax >= $max) && ($rightMax >= $max)) {
                        $pair = $max;
                        $remain_left = $remain_right = 0;
                        $remains = $state = 'Flush Out';
                    } else {
                        if ($left > $right) {
                            $pair = $right;
                            $remain_left = $left - $pair;
                            $remain_right = 0;
                            $remains = "Left : " . $remain_left;
                        } else if (($left < $right)) {
                            $pair = $left;
                            $remain_right = ($right - $pair);
                            $remain_left = 0;
                            $remains = "Right : " . $remain_right;
                        } else if ($left == $right) {
                            $pair = $left;
                            $remain_right = $remain_left = 0;
                            $remains = null;
                        }
                    }
                    $bonus = ($pair * $pairUsd);
                    $bonus = number_format($bonus, 2);

                    $data[] = ['date' => $date, 'left' => $left, 'right' => $right, 'pair' => $pair, 'status' => $remains, 'bonus' => '$' . $bonus, 'usd' => $bonus];

                    $left = $remain_left;
                    $right = $remain_right;
                    $no++;
                    $pair = 0;
                }
                $day->modify("+1 day");
            }
        }

        return $data;
    }

    public function pairingData($id)
    {
        $data = $this->getpairing($id);

        return ($data);
    }

    public function pairing($id)
    {
        try {
            $pairing = $this->getpairing($id);

            $total = array_sum(array_column($pairing, 'usd'));
            return $total;
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

    /*----------------------------------- Passive ----------------------------- */
    public function passive($id)
    {
        $topupdate = $this->DB->dbFetchColumn("SELECT DATE(topup_date) FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

        $obj = $this->DB->dbFetchObject("SELECT DATE(T.registration_date) AS reg, P.usd FROM member AS T JOIN (package AS P) ON (T.package_id=P.package_id) WHERE T.uid=?", [$id]);

        if ($topupdate)
            $start = new DateTime($topupdate);
        else
            $start = new DateTime($obj->reg);

        $end = new DateTime("now");
        //$duration = $start->diff($end)->days + 1;
        $duration = 1;

        $freezeStart = new DateTime('2026-01-12');
        $freezeEnd   = new DateTime('2026-01-19');

        while ($start < $end) {
            if (!($start > $freezeStart && $start <= $freezeEnd)) {
                $duration++;
            }

            $start->modify('+1 day');
        }

        $passive = (($obj->usd * 0.5) / 100) * $duration;

        return $passive;
    }


    /*------------------------------- Matching Profit -------------------------------*/
    private function getROI($id, $level)
    {
        $data = [];
        $tmp = [];

        $passive = $this->DB->dbFetchColumn(
            "SELECT P.passive 
         FROM member AS T 
         JOIN package AS P ON T.package_id=P.package_id 
         WHERE T.uid=?",
            [$id]
        ) ?? 0;

        $matchLevel = [
            1 => 0.3 * $passive,
            2 => 0.2 * $passive,
            3 => 0.1 * $passive,
            4 => 0.05 * $passive,
            5 => 0.05 * $passive,
            6 => 0.05 * $passive
        ];

        $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

        try {
            if ($topupdate) {
                $qry = $this->DB->dbQuery(
                    "SELECT T.uid, T.registration_date AS reg, P.usd 
             FROM member AS T 
             JOIN package AS P ON T.package_id=P.package_id 
             WHERE T.sponsor_id=? AND T.registration_date>=? 
             ORDER BY T.registration_date",
                    [$id, $topupdate]
                );
            } else {
                $qry = $this->DB->dbQuery(
                    "SELECT T.uid, T.registration_date AS reg, P.usd 
             FROM member AS T 
             JOIN package AS P ON T.package_id=P.package_id 
             WHERE T.sponsor_id=? 
             ORDER BY T.registration_date",
                    [$id]
                );
            }

            while ($obj = $this->DB->dbFetchObject($qry)) {
                if ($level <= 6) {
                    $tmp = $this->getROI($obj->uid, $level + 1);
                    $data = array_merge($data, $tmp);
                }

                $profit = $matchLevel[$level] ?? 0;

                $data[] = [
                    'level' => $level,
                    'sponsor' => "$id->$obj->uid",
                    'profit' => number_format($profit, 2),
                    'reg' => $obj->reg
                ];
            }
        } catch (PDOException $e) {
            echo $e->getMessage();
        }

        usort($data, function ($a, $b) {
            return strtotime($a['reg']) <=> strtotime($b['reg']);
        });

        return $data;
    }

    public function roi($id)
    {
        $data = $this->getROI($id, 1);

        return $data;
    }

    /*------------------------------- Earning -------------------------------*/
    private function sponsorByDate($id)
    {
        try {
            $topupDate = $this->DB->dbFetchColumn(
                "SELECT MAX(topup_date) FROM topup WHERE uid=?",
                [$id]
            );

            $sqlSponsor = "
            SELECT P.sponsor 
            FROM member M
            JOIN package P ON M.package_id = P.package_id
            WHERE M.uid=?
        ";

            if ($topupDate) {
                $sqlSponsor .= " AND M.registration_date >= ?";
                $sponsorRate = $this->DB->dbFetchColumn($sqlSponsor, [$id, $topupDate]);
            } else {
                $sponsorRate = $this->DB->dbFetchColumn($sqlSponsor, [$id]);
            }

            if (!$sponsorRate) {
                return [];
            }

            $qry = $this->DB->dbQuery(
                "SELECT 
                DATE(M.registration_date) AS join_date,
                SUM(P.usd * ?) AS bonus
             FROM member M
             JOIN package P ON M.package_id = P.package_id
             WHERE M.sponsor_id=?
             GROUP BY DATE(M.registration_date)
             ORDER BY join_date ASC",
                [$sponsorRate, $id]
            );

            $data = [];
            while ($row = $this->DB->dbFetchAssoc($qry)) {
                $data[$row['join_date']] = (float)$row['bonus'];
            }

            return $data;
        } catch (PDOException $e) {
            error_log($e->getMessage());
            return [];
        }
    }

    public function earning($id)
    {
        $auth = $this->checkPermission();

        try {
            if (!$auth)
                throw new Exception("Access denied..");
            $sponsor = $this->sponsorByDate($id);

            $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

            //pairing
            $dataPairing = $this->getpairing($id);
            $pairing = [];
            foreach ($dataPairing as $item) {
                $date = $item['date'];
                if (!isset($pairing[$date])) {
                    $pairing[$date] = 0;
                }
                $pairing[$date] += $item['usd'];
            }

            //Matching Profit
            $matching = $this->roi($id);
            $now = new DateTime("now");
            $matchingBonus = [];
            foreach ($matching as $val) {
                $start = new DateTime($val['reg']);
                ['profit' => $usd] = $val;

                while ($start <= $now) {
                    $key = $start->format("Y-m-d");

                    if (!isset($matchingBonus[$key])) {
                        $matchingBonus[$key] = 0;
                    }
                    $matchingBonus[$key] += $usd;

                    $start->modify("+1 day");
                }
            }

            ksort($matchingBonus);

            $obj = $this->DB->dbFetchObject("SELECT DATE(T.registration_date) AS reg, P.usd FROM member AS T JOIN (package AS P) ON (T.package_id=P.package_id) WHERE T.uid=?", [$id]);
            $maxProfit = 3 * (float) $obj->usd;

            if ($topupdate)
                $reg = $topupdate;
            else
                $reg = $obj->reg;

            $start = new DateTime($reg);
            $now = (new DateTime("now"))->getTimestamp();

            $i = 1;
            $day = [];
            $data = [];
            $passive = ((float) $obj->usd * 0.5) / 100;
           
            $freezeStart = new DateTime('2026-01-12');
            $freezeEnd   = new DateTime('2026-01-19');

            $tot = 0;
            $table = [];
            while ($start->getTimestamp() <= $now) {
                $dateKey = $start->format("Y-m-d");
                $bonusSponsor = (float) ($sponsor[$dateKey] ?? 0);
                $bonusPairing = (float) ($pairing[$dateKey] ?? 0);
                $bonusROI     = (float) ($matchingBonus[$dateKey] ?? 0);

                if (!($start > $freezeStart && $start <= $freezeEnd)) {
                    $bonus = $bonusSponsor + $bonusPairing + $bonusROI;
                } else
                    $bonus = $passive + $bonusSponsor + $bonusPairing + $bonusROI;

                $tot += $bonus;

                if ($tot >= $maxProfit) break;

                $data[] = round($bonus, 2);
                $day[] = $i;

                $table[] = [
                    'date' => $start->format('Y-m-d'),
                    'passive' => (!($start >= $freezeStart && $start < $freezeEnd)) ? round($passive) : null,
                    'sponsor' => round($bonusSponsor),
                    'pairing' => round($bonusPairing),
                    'roi' => round($bonusROI),
                    'topup' => $topupdate
                ];

                $start->modify("+1 day");
                $i++;
            }
            return ['day' => $day, 'data' => $data, 'table' => $table];
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

    public function balance($id)
    {
        $auth = $this->checkPermission();

        try {
            if (!$auth)
                throw new Exception("Access denied..");

            $topupdate = $this->DB->dbFetchColumn("SELECT topup_date FROM topup WHERE uid=? ORDER BY topup_date DESC LIMIT 1", [$id]);

            if ($topupdate)
                $wd = $this->DB->dbFetchColumn("SELECT SUM(amount) FROM withdraw WHERE bba_id=? AND withdraw_date>=?", [$id, $topupdate]);
            else
                $wd = $this->DB->dbFetchColumn("SELECT SUM(amount) FROM withdraw WHERE bba_id=?", [$id]);

            $wd = $wd ?? 0;

            //Register Wallet
            $rw = $this->DB->dbFetchColumn("SELECT amount FROM register_wallet WHERE uid=?", [$id]);
            $rw=  $rw?? 0;

            $sponsor = $this->totSponsor($id);
            $sponsor = $sponsor ?? 0;

            $pairing = $this->pairing($id);
            $passive = $this->passive($id);

            //Matching Profit
            $matching = $this->roi($id);
            $now = new DateTime("now");
            $matchingProfit = [];
            foreach ($matching as $val) {
                $start = new DateTime($val['reg']);
                ['profit' => $usd] = $val;

                while ($start <= $now) {
                    $key = $start->format("Y-m-d");

                    if (!isset($matchingProfit[$key])) {
                        $matchingProfit[$key] = 0;
                    }
                    $matchingProfit[$key] += $usd;

                    $start->modify("+1 day");
                }
            }

            ksort($matchingProfit);
            $matchingTotal = array_sum($matchingProfit);

            // Max Profit
            $package = $this->DB->dbFetchColumn("SELECT P.usd FROM member AS T JOIN(package AS P) ON (T.package_id=P.package_id)  WHERE T.uid=?", [$id]);
            $maxProfit = 3 * (float) $package;

            $totalBalance = ($sponsor + $pairing + $passive + $matchingTotal) - ($wd+$rw);
            if (($totalBalance + $wd) >= $maxProfit) $totalBalance = $maxProfit - ($wd+$rw);

            $balance = number_format($totalBalance, 2);
            return $balance;
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
}
