<?php

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

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Component\ComponentHelper;
use BlackSheepResearch\Component\UserPoints\Site\Utils\Cookie;
use BlackSheepResearch\Component\UserPoints\Site\UserPoints;
use BlackSheepResearch\Component\UserPoints\Site\Problems\RaffleData;
use BlackSheepResearch\Component\UserPoints\Site\Problems\RaffleInscription;
use BlackSheepResearch\Component\UserPoints\Site\Problems\SQL;

/**
 * UserPoints Content Plugin
 *
 * @package		Joomla
 * @subpackage	UserPoints
 * @since 		1.6
 */
class plgContentUp_raffle extends CMSPlugin {

    public $params = null;
    protected $article = null;
    protected $raffletext = '';
    protected $raffleid = 0;
    protected $raffle = null;
    protected $actiontype = '';
    protected $app = null;
    protected $db = null;
    protected $user = null;
    protected $userid = 0;
    protected $raffleIsActive = false;
    protected $processAll = false;

    public function onContentPrepare($context, &$article, &$params, $limitstart) {
        $this->article = $article;
        $this->params = ComponentHelper::getParams('com_userpoints');
        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);
        $this->app = Factory::getApplication();
        $this->db = SQL::getInstance();
        $this->user = UserPoints::getUser();
        $this->userid = intval($this->user->id);
        $print = Factory::getApplication()->input->get('print', '');
        $format = Factory::getApplication()->input->get('format', '');

        $regex = '#{AUP::RAFFLE=([^:]+)(?:::([A-Z]+))?}#Uis';
        $counter = preg_match_all($regex, $article->text, $matches);
        if (!$print && $format !== 'pdf' && $counter) {
            $this->handleTokens($counter, $matches);
        }
    }

    private function handleTokens($counter, $matches) {
        for ($i = 0; $i < $counter; $i++) {
            $this->raffleIsActive = false;
            $this->raffletext = $matches[0][$i];
            $this->raffleid = ctype_digit($matches[1][$i]) ? (int) $matches[1][$i] : 0;
            $this->raffle = new RaffleData($this->raffleid);
            $this->actiontype = $matches[2][$i] ?? 'BUTTON';
            if ($this->raffleid) {
                $this->raffle->drawRaffleIfDue();
                $this->raffleIsActive = $this->raffle->inscription && $this->raffle->published &&
                    !($this->raffle->winner1 || RaffleData::datetimeSQL() > $this->raffle->raffledate);
                $entryid = $this->app->input->get('aupraffleid', 0, 'int');
                if ($this->raffleIsActive && $this->userid && $entryid == $this->raffleid) {
                    Session::checkToken() or die('Invalid Token');
                    if ($this->canUserEnter()) {
                        $entry = new RaffleInscription();
                        $this->createRaffleEntry($entry);
                        // remove points if necessary
                        if ($this->raffle->removepointstoparticipate && $this->raffle->pointstoparticipate) {
                            UserPoints::newpoints('sysplgup_raffle', '', '', Text::_('AUP_LABEL_REGISTRATION_RAFFLE'), (abs($this->raffle->pointstoparticipate) * (-1)));
                        }
                        $this->app->enqueueMessage(Text::_('AUP_CONFIRM_RAFFLE_REGISTRATION'));
                    }
                }
                $this->processOneToken();
            } else {
                $this->replaceToken(Text::_('INVALID RAFFLE TOKEN'));
            }
        }
    }

    private function processOneToken() {
        switch ($this->actiontype) {
            case 'STATUS':
                $this->processStatus();
                break;
            case 'HISTORY':
                if ($this->userid) {
                    $this->processHistory();
                } else {
                    $this->replaceToken();
                }
                break;
            case 'BUTTON':
                if ($this->userid) {
                    $this->processButton();
                } else {
                    $this->processNoButton();
                }
                break;
            case 'COUNT':
                $this->processCount();
                break;
            case 'WINNERS':
                $this->processWinners();
                break;
            default:
                $this->processAll();
        }
    }

    private function processStatus() {
        $drawn = $this->raffle->winner1 ? Text::_('AUP_RAFFLE_HAS_BEEN_DRAWN') : Text::_('AUP_RAFFLE_NOT_YET_DRAWN');
        if (!$this->raffle->winner1) {
            if (SQL::datetimeSQL() > $this->raffle->raffledate) {
                $status = Text::_('AUP_RAFFLE_NOT_OPEN');
            } else {
                $status = $this->raffle->inscription ? Text::_('AUP_RAFFLE_OPENED_FOR_ENTRIES') : Text::_('AUP_RAFFLE_NOT_OPENED');
                $status .= ' ';
                $status .= $this->raffle->published ? Text::_('AUP_RAFFLE_AND_PUBLISHED') : Text::_('AUP_RAFFLE_AND_NOT_PUBLISHED');
            }
        } else {
            $status = '';
        }
        $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`=$this->raffleid";
        $this->db->setQuery($query);
        $num_subscription = $this->db->loadResult();
        $num_text = Text::_('AUP_NUMBER_SUBSCRIPTION_CURRENT_RAFFLE');
        $num_entered = "<p>$num_text $num_subscription</p>";
        $this->replaceToken("$status $drawn $num_entered");
    }

    private function processHistory() {
        // show tickets subscription$user
        $showTicketsList = "";
        $tickets = $this->getUserTickets();
        if ($tickets) {
            $text = Text::_('AUP_RAFFLE_YOU_ALREADY_HAVE_TICKETS');
            $showTicketsList = "<p>$text</p>";
            $showTicketsList .= '<ul>';
            foreach ($tickets as $ticket) {
                $showTicketsList .= '<li>' . $ticket . '</li>';
            }
            $showTicketsList .= '</ul>';
        }
        $this->replaceToken($showTicketsList);
    }

    private function processButton() {
        if ($this->app->input->get('aupraffleid', 0, 'int')) {
            $this->processTriggeredButton();
        } else {
            $this->processNewButton();
        }
    }

    private function processNoButton() {
        $this->replaceToken(Text::_('AUP_RAFFLE_NEED_TO_LOGIN_TO_ENTER'));
    }

    private function processNewButton() {
        if ($this->raffleIsActive) {
            if ($this->canUserEnter()) {
                if ($this->canAffordEntry()) {
                    $newText[] = $this->signUpText($this->getURL());
                } else {
                    $newText[] = '<p>' . Text::_('AUP_YOUDONOTHAVEENOUGHPOINTSTOPERFORMTHISOPERATION') . '</p>';
                }
            } else {
                $newText[] = '<p>' . Text::_('AUP_ALREADY_REGISTERED_FOR_THIS_RAFFLE') . '</p>';
            }
        } else {
            $newText[] = Text::_('AUP_RAFFLE_NOT_OPEN');
        }
        $this->replaceToken($newText);
    }

    private function processTriggeredButton() {
        if ($this->canUserEnter() && $this->canAffordEntry()) {
            $enteragain = Text::_('AUP_SUBSCRIBE_AGAIN');
            $this->replaceToken("<p><a href=\"{$this->getURL()}\">$enteragain</a></p>");
        } else {
            $this->replaceToken();
        }
    }

    private function processCount() {
        $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`=$this->raffleid";
        $this->db->setQuery($query);
        $num_subscription = $this->db->loadResult();
        $textcount = Text::_('AUP_NUMBER_SUBSCRIPTION_CURRENT_RAFFLE');
        $this->replaceToken("<p>$textcount $num_subscription</p>");
    }

    private function processWinners() {
        if ($this->raffle->inscription && $this->raffle->winner1) {
            $showname = $this->params->get('shownamewinner',0);
            $showusername = $this->params->get('showusernamewinner',0);
            $text = Text::_('AUP_PLG_RAFFLE_WINNING_TICKETS');
            $showWinnerList = "<p>$text</p>";
            $showWinnerList .= '<ol>';
            $winners = [$this->raffle->winner1, $this->raffle->winner2, $this->raffle->winner3];
            foreach ($winners as $winner) {
                if ($winner) {
                    $this->db->setQuery("SELECT ticket, userid FROM #__alpha_userpoints_raffle_inscriptions WHERE id = $winner");
                    $inscription = $this->db->loadObject();
                    if ($showname || $showusername) {
                        $user = UserPoints::getUser($inscription->userid);
                        $name = $showname ? $user->name : '';
                        $username = $showusername ? $user->username : '';
                    } else {
                        $name = $username = '';
                    }
                    $showWinnerList .= "<li>$inscription->ticket $name $username</li>";
                }
            }
            $showWinnerList .= '</ol>';
        }
        $this->replaceToken($showWinnerList ?? '');
    }

    private function processAll() {
        $this->processAll = true;
        $this->processStatus();
        $this->processWinners();
        if ($this->userid) {
            $this->processHistory();
            $this->processButton();
        } else {
            $this->processNoButton();
        }
        $this->processAll = false;
        $this->replaceToken();
        return;
    }

    private function replaceToken($replacement = '', $removeToken = true) {
        $one = 1;
        if (is_array($replacement)) {
            $replacement = implode('', $replacement);
        }
        if ($removeToken) {
            if ($this->processAll) {
                $replacement .= $this->raffletext;
            }
            $this->article->text = str_replace($this->raffletext, $replacement, $this->article->text, $one);
        }
    }

    private function getUserEntryCount() {
        $query = "SELECT COUNT(*) FROM #__alpha_userpoints_raffle_inscriptions WHERE `userid`=$this->userid AND `raffleid`=$this->raffleid";
        $this->db->setQuery($query);
        return $this->db->loadResult();
    }

    private function getUserTickets() {
        $query = "SELECT ticket FROM #__alpha_userpoints_raffle_inscriptions WHERE `userid`=$this->userid AND `raffleid`=$this->raffleid";
        $this->db->setQuery($query);
        return (array) $this->db->loadColumn();
    }

    private function getURL(): string {
        static $url = '';
        if (!$url) {
            $uri = Uri::getInstance();
            $uri->delVar('referrer');
            $uri->delVar('draw');
            $url = $uri->toString();
        }
        return $url;
    }

    private function canUserEnter(): bool {
        if (!$this->raffle->multipleentries) {
            $query = "SELECT COUNT(*) FROM #__alpha_userpoints_raffle_inscriptions "
                . " WHERE raffleid = $this->raffleid AND userid = $this->userid";
            $this->db->setQuery($query);
            $count = $this->db->loadResult();
            if ($count) {
                return false;
            }
        }
        return true;
    }

    private function canAffordEntry(): bool {
        $referreid = UserPoints::getAnyUserReferreID($this->userid);
        $currentpoints = UserPoints::getCurrentTotalPoints($referreid);
        return ($currentpoints >= $this->raffle->rafflepointstoparticipate || !$this->raffle->removepointstoparticipate);
    }

    private function signUpText($url): string {
        $buttontext = Text::_('AUP_SIGNUP_FOR_THIS_RAFFLE_NOW');
        $csrfToken = HTMLHelper::_('form.token');
        return <<<SIGN_UP

            <form action="$url" method="post" name="RaffleForm">
                <input type="hidden" name="aupraffleid" id="aupraffleid" value="{$this->raffleid}" />
                $csrfToken
                <input class="button" type="submit" name="Submit" value="$buttontext" />
            </form>

SIGN_UP;
    }

    private function createRaffleEntry($entry) {
        // Save registration
        $entry->id = NULL;
        $entry->raffleid = $this->raffleid;
        $entry->userid = $this->userid;
        $entry->ticket = $this->createRandomTicket(3, 2, 3, 4);
        $entry->referredraw = 0;
        $entry->inscription = RaffleData::datetimeSQL();
        $entry->save();
    }

    private function createRandomTicket($n1, $n2, $n3, $n4) {
        $ticket = '';
        while (!$ticket) {
            $ticket = $this->generateRandomTicket($n1, $n2, $n3, $n4);
            $this->db->setQuery("SELECT COUNT(*) FROM `#__alpha_userpoints_raffle_inscriptions` WHERE `ticket` = '$ticket'");
            if ($this->db->loadResult()) {
                $ticket = '';
            }
        }
        return $ticket;
    }

    private function generateRandomTicket($n1, $n2, $n3, $n4) {
        // Helper function to get random alphabetic characters
        $getRandomAlpha = function ($length) {
            $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            $result = '';
            for ($i = 0; $i < $length; $i++) {
                $result .= $characters[rand(0, strlen($characters) - 1)];
            }
            return $result;
        };

        // Helper function to get random numeric digits
        $getRandomDigits = function ($length) {
            $result = '';
            for ($i = 0; $i < $length; $i++) {
                $result .= rand(0, 9);
            }
            return $result;
        };

        // Generate each part according to specification
        $part1 = $getRandomAlpha($n1);
        $part2 = $getRandomAlpha($n2);
        $part3 = $getRandomDigits($n3);
        $part4 = $getRandomDigits($n4);

        // Combine all parts with spaces
        return $part1 . ' ' . $part2 . ' ' . $part3 . ' ' . $part4;
    }

    private function processObsolete() {
        $isAdmin = $this->user->authorise('core.admin');

        $raffleIsActive = $this->raffle->inscription && $this->raffle->published && !($this->raffle->winner1 || RaffleData::datetimeSQL() > $this->raffle->raffledate);

        $aupraffleid = Factory::getApplication()->input->get('aupraffleid', 0, 'int');
        $aupraffleuserid = Factory::getApplication()->input->get('aupraffleuserid', 0, 'int');
        // ---
        // if referral draw system
        $referredraw2 = Factory::getApplication()->input->get('referredraw2', 0, 'int');
        $draw2 = Factory::getApplication()->input->get('draw2', 0, 'int');

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);

        $newText = [];

        if (!$aupraffleid) { // form not sent
            // You can choose number subscriptions members
            //$query = "SELECT COUNT(DISTINCT userid) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$this->raffleid'";
            // You can choose number of tickets sold!
            $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`={$this->db->quote($this->raffleid)}";
            $this->db->setQuery($query);
            $num_subscription = $this->db->loadResult();
            $this->article->text .= "\n<p>" . Text::_('AUP_NUMBER_SUBSCRIPTION_CURRENT_RAFFLE') . " " . $num_subscription . "</p>\n";

            if ($raffleIsActive && $this->userid) {

                if ($this->canUserEnter()) {
                    $referredraw = '';
                    $draw = 0;
                    $ref_cookie = Cookie::get('referredraw');
                    if ($ref_cookie) {
                        $referredraw = UserPoints::getUserID($ref_cookie);
                    }
                    $draw_cookie = Cookie::get('draw');
                    if ($draw_cookie) {
                        $draw = $draw_cookie;
                    }

                    $referreid = UserPoints::getAnyUserReferreID($this->userid);
                    $currentpoints = UserPoints::getCurrentTotalPoints($referreid);
                    if ($currentpoints >= $this->raffle->pointstoparticipate) {
                        $newText[] = $this->signUpText($this->getURL(), $referredraw, $draw);
                    } else {
                        $newText[] = "<p>" . Text::_('AUP_YOUDONOTHAVEENOUGHPOINTSTOPERFORMTHISOPERATION') . "</p>";
                    }
                } else {
                    $newText[] = "<p>" . Text::_('AUP_ALREADY_REGISTERED_FOR_THIS_RAFFLE') . "</p>";
                }
            } elseif ($raffleIsActive && $this->userid) {
                $newText[] = "<p>" . Text::_('AUP_DRAW_HAS_BEEN_MADE_YOU_CANT_REGISTER') . "</p>";
                $this->replaceToken($newText);
                return;
            } else {
                $this->replaceToken($newText);
                return;
            }
        } elseif ($aupraffleid && $aupraffleuserid) {  // form has been sent
            $auprafflepoints = Factory::getApplication()->input->get('auprafflepoints', 0, 'FLOAT');
            $auprafflepointsremove = Factory::getApplication()->input->get('auprafflepointsremove', 0, 'FLOAT');
            $matchesultipleentries = Factory::getApplication()->input->get('multipleentries', 0, 'int');
            if (!$matchesultipleentries) {
                $query = "SELECT userid FROM #__alpha_userpoints_raffle_inscriptions WHERE `userid`='$aupraffleuserid' AND `raffleid`='$aupraffleid'";
                $this->db->setQuery($query);
                $alreadyregister = $this->db->loadResult();
            } else {
                $alreadyregister = 0;
            }

            if (!$alreadyregister) {
                // Save registration
                $row = new RaffleInscription();
                $row->id = NULL;
                $row->raffleid = $aupraffleid;
                $row->userid = $aupraffleuserid;
                $row->ticket = $this->createRandomTicket(3, 2, 3, 4);
                $row->referredraw = $referredraw2;
                $row->inscription = RaffleData::datetimeSQL();
                $row->save();

                // if referral draw system -> attribs one additional ticket to the referral user if already register to this raffle
                if ($draw2 && $referredraw2 && ($draw2 == $aupraffleid) && ($referredraw2 != $aupraffleuserid)) {

                    // check if the referral user have a first registration to this raffle
                    $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$aupraffleid' AND `userid`='$referredraw2'";
                    $this->db->setQuery($query);
                    $n_subscription = $this->db->loadResult();

                    // if first registration check if not already done by this user
                    $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$aupraffleid' AND `userid`='$referredraw2' AND `referredraw`='$aupraffleuserid'";
                    $this->db->setQuery($query);
                    $already_done_subscription = $this->db->loadResult();

                    if (!$already_done_subscription && $n_subscription) {
                        // Save additional registration for the referral user
                        $row2 = new RaffleInscription();
                        $row2->id = NULL;
                        $row2->raffleid = $aupraffleid;
                        $row2->userid = $referredraw2;
                        $row2->ticket = $this->createRandomTicket(3, 3, 3, 4);
                        $row2->referredraw = $aupraffleuserid;
                        $row2->inscription = RaffleData::datetimeSQL();
                        $row2->save();

                        // delete cookies for draw
                        setcookie("referredraw");
                        setcookie("draw");
                        Cookie::set('referredraw');
                        Cookie::set('draw');
                    }
                }

                // remove points if necessary
                if ($auprafflepointsremove && $auprafflepoints) {
                    UserPoints::newpoints('sysplgup_raffle', '', '', Text::_('AUP_LABEL_REGISTRATION_RAFFLE'), (abs($auprafflepoints) * (-1)));
                }
                $this->app->enqueueMessage(Text::_('AUP_CONFIRM_RAFFLE_REGISTRATION'));

                // You can choose number subscriptions members
                // $query = "SELECT COUNT(DISTINCT userid) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$aupraffleid'";
                // You can choose number of total tickets sold!
                if ($matchesultipleentries) {
                    $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$aupraffleid' AND `userid`='$aupraffleuserid'";
                } else {
                    $query = "SELECT COUNT(id) FROM #__alpha_userpoints_raffle_inscriptions WHERE `raffleid`='$aupraffleid'";
                }

                $this->db->setQuery($query);
                $num_subscription = $this->db->loadResult();
                $newText [] = "<p>" . Text::_('AUP_NUMBER_SUBSCRIPTION_CURRENT_RAFFLE') . " " . $num_subscription . "</p>";

                $newText[] = "<p>" . Text::_('AUP_YOUR_SUBSCRIPTION_HAS_BEEN_REGISTERED') . "</p>";
                // check new total points to play again
                $referreid = UserPoints::getAnyUserReferreID($this->userid);
                $currentpoints = UserPoints::getCurrentTotalPoints($referreid);

                if (($currentpoints >= $auprafflepoints || !auprafflepointsremove) && $matchesultipleentries) {
                    $newText[] = "<p><a href=\"{$this->getURL()}\">" . Text::_('AUP_SUBSCRIBE_AGAIN') . "</a></p>";
                }
            } else {
                $newText[] = "<p>" . Text::_('AUP_ALREADY_REGISTERED_FOR_THIS_RAFFLE') . "</p>";
            }
        }

        // Show link to invite
        if ($this->userid) {
            $referreid = UserPoints::getAnyUserReferreID($this->userid);
            // show invite link to register to this raffle and allow to the referral earn an additional free ticket
            $referhtml = <<<INVITATION

            <input type="text" name="referredraw_link" id="referredraw_link" onfocus="select();"
            readonly="readonly" class="inputbox" value="{$this->getURL()}/?referrer=$referreid&draw=$this->raffleid" />

INVITATION;
            // show tickets subscription$user
            $showTicketsList = "";
            $tickets = $this->getUserTickets();
            if ($tickets) {
                $showTicketsList = "<ul>";
                foreach ($tickets as $ticket) {
                    $showTicketsList .= "<li>" . $ticket . "</li>";
                }
                $showTicketsList .= "</ul>";
                $newText[] = $showTicketsList;
            }
        }

        //  article text updated
        $this->replaceToken($newText);
        $this->article->text = preg_replace(" |{AUP::RAFFLE(.*)}| ", implode("\n", $newText), $this->article->text);
    }
}
