<?php

/*
 * @component UserPoints
 * @copyright Copyright (C) 2015-2022 Bernard Gilly - Adrien Roussel - Martin Brampton
 * @license : GNU/GPL
 * @Website : https://remository.com
 */

namespace BlackSheepResearch\Component\UserPoints\Site\Problems;

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\User\User;
use BlackSheepResearch\Component\UserPoints\Site\UserPoints;

class PointsUser extends ProblemBase {

    protected static $fieldnames = [];
    protected static $fieldtypes = [];
    protected static $count = -1;
    protected static $users = [];
    protected static $users_by_tableid = [];
    protected static $users_by_userid = [];
    protected static $tablename = '#__alpha_userpoints_combined';
    protected static $updatetable = '#__alpha_userpoints';
    protected static $keyname = 'id';
    private static $refSQL = "SELECT *, id AS rid FROM `%s` WHERE `referreid`= %s";
    private static $userSQL = "SELECT *, id AS rid FROM `%s` WHERE userid = %s";
    private static $tableSQL = "SELECT *, id AS rid FROM `%s` WHERE id = %s";

    public function __construct() {
        parent::__construct();
    }

    public static function withReferrerID(string $referrerid) {
        $pointsuser = new self();
        $pointsuser->loadWithSQL(self::$users, SQL::getInstance()->quote($referrerid), self::$refSQL);
        return $pointsuser;
    }

    public static function withUserID(int $userid) {
        $pointsuser = new self();
        if ($userid) {
            $pointsuser->loadWithSQL(self::$users_by_userid, $userid, self::$userSQL);
        }
        return $pointsuser;
    }

    public static function withTableID(int $id) {
        $pointsuser = new self();
        if ($id) {
            $pointsuser->loadWithSQL(self::$users_by_tableid, $id, self::$tableSQL);
        }
        return $pointsuser;
    }

    public static function withEither(string $referrerid = '', int $userid = 0) {
        return $referrerid ? self::withReferrerID($referrerid) : self::withUserID($userid);
    }

    private function loadWithSQL($stored, $key, $sql) {
        if (isset($stored[$key])) {
            $this->problem_object = $stored[$key];
        } else {
            $db = SQL::getInstance();
            $db->setQuery(sprintf($sql, self::$tablename, $key));
            $object = $db->loadObject();
            if (is_object($object)) {
                self::$users[$object->referreid] = $object;
                self::$users_by_userid[$object->userid] = $object;
                self::$users_by_tableid[$object->id] = $object;
                $this->problem_object = $object;
            }
        }
    }

    public function newPointsEvent(string $rule_id, float $points = 0.0, string $keyreference = '', string $datareference = '', int $force = 0) {
        Factory::getLanguage()->load('com_userpoints', JPATH_SITE);
        if ('sysplgup_recommend' == $rule_id AND $this->referreid AND $keyreference AND $datareference) {
            $force = 1;
        }
    }

    public function checkTotalAfterRecalculate ($rule_id) {
        $this->refresh();
        if ($this->referreid) {
            UserPoints::checkRankMedal($this->referreid, $rule_id);
        }
    }

    public function updateUserAccount($details) {
        $this->last_update = SQL::datetimeSQL();
        if ($details->plugin_function == 'sysplgup_invitewithsuccess') {
            // update number referrees
            $this->referrees++;
        }
        $this->save();

        $checkWinner = UserPoints::notifyWinner($details->referreid, true, 0.0);
        $rule = Rule::getActiveByRuleName($details->plugin_function);
        if (!$checkWinner AND $rule->notification) {
            UserPoints::sendnotification($details->referreid, $details, $rule, 0);
        }
        // If referral user exist
        if ($this->referraluser != '' AND $details->plugin_function != 'sysplgup_buypointswithpaypal' AND $details->plugin_function != 'sysplgup_raffle' AND $details->plugin_function != 'sysplgup_referralpoints') {
            $referral_rule = Rule::getActiveByRuleName('sysplgup_referralpoints');
            if ($referral_rule AND $rule->allow_referral) {
                $referraluserpoints = round(($details->points * $referral_rule->points) / 100, 2);
                if ($referraluserpoints > 0) {
                    UserPoints::userpoints('sysplgup_referralpoints', $this->referraluser, $referraluserpoints, '', Text::_('AUP_REFERRALPOINTS') . ' - ' . $this->referraluser);
                    $this->checkTotalAfterRecalculate($details->rule);
                }
            }
        }
        // update Ranks / Medals if necessary
        UserPoints::checkRankMedal($details->referreid, $details->rule);
    }

    public function deleteWithRelated() {
        if (is_object($this->problem_object)) {
            unset(self::$users[$this->referrerid]);
            unset(self::$users_by_tableid[$this->id]);
            unset(self::$users_by_userid[$this->userid]);
            $table = self::$updatetable;
            $db = SQL::getInstance();
            $id = intval($this->userid);
            $sql = "DELETE u, ud, uda, r, m FROM `$table` AS u"
                    . " LEFT JOIN `#__alpha_userpoints_details` AS ud ON ud.referreid = u.referreid "
                    . " LEFT JOIN `#__alpha_userpoints_details_archive` AS uda ON uda.referreid = u.referreid "
                    . " LEFT JOIN `#__alpha_userpoints_raffle_inscriptions` AS r ON r.userid = u.userid "
                    . " LEFT JOIN `#__alpha_userpoints_medals` AS m ON m.rid = u.id"
                    . " WHERE u.userid = $id";
            $db->setQuery($sql);
            $db->execute();
        }
    }

    public function removeReferral() {
        if ($this->referreid) {
            $db = SQL::getInstance();
            $table = self::$updatetable;
            // if the user has been a referral user
            $query1 = "UPDATE `$table` SET referraluser = '' WHERE referraluser = {$db->quote($this->referreid)}";
            $db->setQuery($query1);
            $db->execute();

            // recount referrees for the referral user
            $query2 = "UPDATE `$table` SET referrees = referrees-1 WHERE referreid = {$db->quote($this->referraluser)}";
            $db->setQuery($query2);
            $db->execute();
        }
    }

    public function refreshAll() {
        if ($this->id) {
            $this->last_update = self::datetimeSQL();
            $this->levelrank = $this->leveldate = $this->profileviews = $this->referrees = 0;
            $this->referraluser = '';
            $this->save();
        }
    }

    public function refresh() {
        if ($this->id) {
            $this->last_update = self::datetimeSQL();
            $this->save();
        }
    }

    public static function getUser(int $id = 0) {
        $userobj = new User($id);
        if ($id) {
            return $userobj->id ? $userobj : new User(0);
        }
        $application = Factory::getApplication();
        $user = $application->getIdentity();
        if (empty($user)) {
            $application->loadIdentity();
            $user = $application->getIdentity();
        }
        return empty($user) ? $userobj : $user;
    }

    public static function loadMultiple(array $ids) {
        if (count($ids)) {
            $db = SQL::getInstance();
            $db->setQuery(sprintf("SELECT *, id AS rid FROM `%s` WHERE id IN (%s)", self::$tablename, self::listIDs($ids)));
            foreach ((array) $db->loadObjectList() as $object) {
                self::$users_by_tableid[$object->id] = self::$users[$object->referreid] = self::$users_by_userid[$object->userid] = $object;
            }
        }
    }

    public static function purge() {
        $db = SQL::getInstance();
        $db->setQuery("SELECT a.id FROM `#__alpha_userpoints` AS a LEFT JOIN `#__users` AS u ON a.userid = u.id"
                . " WHERE a.userid != 0 AND u.id IS NULL");
        $orphans = $db->loadColumn();
        self::loadMultiple($orphans);
        foreach ($orphans as $orphan) {
            self::withTableID($orphan)->deleteWithRelated();
        }
    }

    public static function inactiveCount(int $period) {
        $db = SQL::getInstance();
        $now = SQL::datetimeSQL();
        $query = "SELECT COUNT(*) FROM `#__alpha_userpoints` "
                . " WHERE userid > 0 AND published != 0 AND referreid != 'GUEST' "
                . " AND (TO_DAYS('$now') - TO_DAYS(last_update)) > $period";
        $db->setQuery($query);
        return $db->loadResult();
    }

    public static function refreshEveryUser() {
        $db = SQL::getInstance();
        $now = SQL::datetimeSQL();
        $db->setQuery("UPDATE `#__alpha_userpoints` SET `last_update` = '$now'");
        $db->execute();
        $db->setQuery("UPDATE `#__alpha_userpoints_details` SET enabled = 0");
        $db->execute();
    }

    public static function levelrankCount(int $levelrank): int {
        $db = SQL::getInstance();
        $query = "SELECT COUNT(*) FROM #__alpha_userpoints WHERE levelrank = $levelrank";
        $db->setQuery($query);
        return (int) $db->loadResult();
    }

    public static function resetByLevelranks(array $levelranks) {
        // change level/rank user if necessary
        $db = SQL::getInstance();
        $ranklist = implode(',', array_map('\\intval', $levelranks));
        $db->setQuery("UPDATE #__alpha_userpoints SET levelrank='0' WHERE `levelrank` IN ($ranklist)");
        $db->execute();
    }

    public static function getNewUsers(): array {
        $db = SQL::getInstance();
        $db->setQuery("SELECT u.id, u.username FROM #__users AS u "
                . " LEFT JOIN #__alpha_userpoints AS a ON u.id = a.userid WHERE a.referreid IS NULL");
        return (array) $db->loadObjectList();
    }

    private static function makeNewReferrerID($user): string {
        $params = ComponentHelper::getParams('com_userpoints');
        if ('r' == $params->get('referralIDtype','')) {
            return strtoupper(uniqid(strtoupper($params->get('prefix_selfregister', '')), true));
        } else {
            return strtoupper($params->get('prefix_selfregister', '')) . strtoupper($user->username);
        }
    }

    public static function recordNewUsers() {
        $db = SQL::getInstance();
        $now = SQL::datetimeSQL();
        $ruleNewUser = Rule::withRuleName('sysplgup_newregistered');
        $newusers = self::getNewUsers();
        foreach ($newusers as $user) {
            $newreferreid = self::makeNewReferrerID($user);
            // add this new user data into the inserts for the userpoints table
            $values[] = "(
                {$user->id},
                '{$newreferreid}',
                '{$now}'
                )";
        }
        if (isset($values)) {
            $query = "INSERT INTO #__alpha_userpoints (userid,referreid,last_update) VALUES " . implode(',', $values);
            $db->setQuery($query);
            $db->execute();
        }
        if ($ruleNewUser->published AND $ruleNewUser->points) {
            self::recordNewDetails($ruleNewUser);
        }
    }

    private static function recordNewDetails($ruleNewUser) {
        $db = SQL::getInstance();
        $now = SQL::datetimeSQL();
        $welcome = Text::_('AUP_WELCOME');
        $constantdetail = "{$ruleNewUser->points},
                '{$now}',
                '{$ruleNewUser->rule_expire}',
                {$ruleNewUser->autoapproved},
                '{$ruleNewUser->plugin_function}',
                {$ruleNewUser->autoapproved},
                '{$welcome}'";
        $query = "SELECT a.referreid FROM #__alpha_userpoints AS a " .
                " LEFT JOIN #__alpha_userpoints_details AS d ON a.referreid = d.referreid " .
                " WHERE a.referreid != 'GUEST' AND d.referreid IS NULL";
        $db->setQuery($query);
        $newdetails = (array) $db->loadObjectList();
        foreach ($newdetails as $detail) {
            $values[] = "(
                '{$detail->referreid}',
                {$constantdetail})";
        }
        if (isset($values)) {
            $query = "INSERT INTO #__alpha_userpoints_details " .
                    " (referreid,points,insert_date,expire_date,status,rule_id,approved,datareference) VALUES " . implode(',', $values);
            $db->setQuery($query);
            $db->execute();
        }
    }

    public static function makeWinnerLinks(array $winners): string {
        $db = SQL::getInstance();
        $winnerlist = implode(',', $winners);
        $table = self::$tablename;
        $db->setQuery("SELECT * FROM `$table` WHERE userid IN ($winnerlist)");
        $users = $db->loadObjectList();
        $html = '';
        $baselink = 'index.php?option=com_userpoints&task=showdetails';
        foreach ($winners as $winner) {
            if ($winner) {
                foreach ($users as $user) {
                    if ($user->userid == $winner) {
                        $html .= <<<WINNER_LINK

                    <a href="$baselink&cid=$user->referreid&name=$user->name">$user->name</a>

WINNER_LINK;
                    }
                }
            }
        }
        return $html;
    }

    public static function countActiveUsers () : int {
        $db = SQL::getInstance();
        $db->setQuery(sprintf("SELECT COUNT(userid) FROM `%s` WHERE blocked = 0 AND published !=  0", self::$tablename));
        return $db->loadResult();
    }

    public static function getActiveRecordN (int $n) {
        $db = SQL::getInstance();
        $db->setQuery(sprintf("SELECT * FROM `%s` WHERE blocked = 0 AND published !=  0 ORDER BY `userid` LIMIT 1 OFFSET %d", self::$tablename, $n-1));
        return $db->loadObject();
    }
}
