<?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\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use BlackSheepResearch\Component\UserPoints\Site\UserPoints;

class RaffleData extends ProblemBase {

    protected static $fieldnames = [];
    protected static $fieldtypes = [];
    protected static $count = -1;
    protected static $tablename = '#__alpha_userpoints_raffle';
    protected static $updatetable = '#__alpha_userpoints_raffle';
    protected static $keyname = 'id';
    protected $raffletickets = [];
    protected $issued = [];

    public function __construct(int $id = 0) {
        parent::__construct();
        if ($id) {
            $this->getOneRow($id);
            $db = SQL::getInstance();
            $query = "SELECT * FROM #__alpha_userpoints_raffle_inscriptions WHERE raffleid = $id";
            $db->setQuery($query);
            foreach ($db->loadObjectList() as $ticket) {
                $this->raffletickets[$ticket->id] = $ticket;
                $this->issued[$ticket->id] = $ticket->ticket;
            }
        }
    }

    public static function getNextID(): int {
        $db = SQL::getInstance();
        $now = SQL::datetimeSQL();
        $db->setQuery(sprintf("SELECT id FROM `%s` WHERE published != 0 AND inscription != 0
            AND raffledate > '$now' AND raffledate != '%s' AND winner1 = 0
            AND winner2 = 0 AND winner3 = 0 LIMIT 1", self::$tablename, SQL::nulldatetimeSQL()));
        return (int) $db->loadResult();
    }

    public static function current(): array {
        $db = SQL::getInstance();
        $db->setQuery(sprintf("SELECT id, description, raffledate FROM `%s` WHERE published != 0 AND winner1 = 0 ORDER BY id", self::$tablename));
        return self::makeProblems((array) $db->loadObjectList(), 'id');
    }

    public static function getRangeWithInscriptions(int $limitstart, int $limit, int $filter_category_id): array {
        $db = SQL::getInstance();
        $filter = $filter_category_id ? "WHERE r.category = $filter_category_id" : '';
        $db->setQuery(sprintf("SELECT r.*, ri.numregistered, c.title AS category_title FROM `%s` AS r "
                . " LEFT JOIN (SELECT raffleid, COUNT(raffleid) AS numregistered FROM #__alpha_userpoints_raffle_inscriptions GROUP BY raffleid) AS ri ON r.id = ri.raffleid "
                . " LEFT JOIN #__categories AS c ON r.category = c.id %s ORDER BY r.id DESC LIMIT %d, %d", self::$tablename, $filter, $limitstart, $limit));
        return self::makeProblems((array) $db->loadObjectList(), 'id');
    }

    public function acceptsEntries(): bool {
        if ($this->raffle->inscription && $this->raffle->published && !$this->raffle->winner1 &&
            (self::datetimeSQL() < $this->raffledate || self::nulldatetimeSQL() == $this->raffledate)) {
            return true;
        }
        return false;
    }

    public function raffleSystemText() {
        switch ($this->rafflesystem) {
            case '3':
                return Text::_('AUP_EMAIL_ONLY');
            case '2':
                return Text::_('AUP_EMAIL_WITH_A_LINK_TO_DOWNLOAD');
            case '1':
                return Text::_('AUP_COUPON_CODES');
            case '0':
            default:
                return Text::_('AUP_POINTS');
        }
    }

    public function entriesListLink() {
        if (!$this->inscription || !$this->countEntries()) {
            return "-";
        }
        if (UserPoints::getUser()->authorise('core.create', 'com_userpoints')) {
            return <<<ENTRIES
                    <a href="index.php?option=com_userpoints&amp;task=exportListUsersRaffle&amp;id=$this->id">{$this->countEntries()}</a>
ENTRIES;
        }
        return $this->countEntries();
    }

    public function canUserEnter($userid) {
        if (!$this->multipleentries) {
            $id = intval($userid);
            foreach ($this->raffletickets as $ticket) {
                if ($userid == $ticket->userid) {
                    return false;
                }
            }
        }
        return true;
    }

    public function removeEntryPoints() {
        if ($this->removepointstoparticipate && $this->pointstoparticipate) {
            UserPoints::newpoints('sysplgup_raffle', '', '', Text::_('AUP_LABEL_REGISTRATION_RAFFLE'), (abs($this->pointstoparticipate) * (-1)));
        }
    }

    public function countEntries() {
        return count($this->raffletickets);
    }

    public function getUserTickets($userid) {
        foreach ($this->raffletickets as $ticket) {
            if ($userid == $ticket->userid) {
                $usertickets = $ticket->ticket;
            }
        }
        return $usertickets ?? [];
    }

    public function createRandomTicket($n1, $n2, $n3, $n4) {
        $db = SQL::getInstance();
        $ticket = '';
        while (!$ticket) {
            $ticket = $this->generateRandomTicket($n1, $n2, $n3, $n4);
            if (in_array($ticket, $this->issued)) {
                $ticket = '';
            }
        }
        $this->issued[] = $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;
    }

    public function raffleIsActive () : bool {
        $indaterange = $this->raffledate == self::nulldatetimeSQL || self::datetimeSQL() < $this->raffledate;
        return $this->inscription && $this->published && !$this->winner1 && $indaterange;
    }

    public function canDraw () {
        return $this->inscription && $this->published && !$this->winner1 && !($this->countEntries() < $this->numwinner);
    }

    /**
     * Draw raffle winners and process rewards
     *
     * @return void
     */
    public function drawRaffle(): bool {
        $excluded = [];
        // Choose winners based on number of winners needed
        $winners = $this->selectWinningEntries($excluded);
        if (0 == array_product($winners)) {
            return false;
        }
        // Save winners to database
        $this->winner1 = $winners[0] ?? 0;
        $this->winner2 = $winners[1] ?? 0;
        $this->winner3 = $winners[2] ?? 0;
        $this->save();

        // Process rewards for each winner
        for ($i = 0; $i < count($winners); $i++) {
            if (!empty($winners[$i])) {
                $winnerNumber = $i + 1;
                $userid = $this->inscription ? $this->raffletickets[$winners[$i]]->userid : $winners[$i];
                $this->processReward($userid, $winnerNumber);
            }
        }

        return true;
    }

    public function drawRaffleIfDue(): bool {
        if (!$this->winner1 && self::nulldatetimeSQL() != $this->raffledate && self::datetimeSQL() > $this->raffledate) {
            return $this->drawRaffle();
        }
        return false;
    }

    public function winnerLinks() {
        if ($this->winner1) {
            $winnerarray = [intval($this->winner1), intval($this->winner2), intval($this->winner3)];
            if ($this->inscription) {
                $db = SQL::getInstance();
                $winnerlist = implode(',', $winnerarray);
                $db->setQuery("SELECT id, userid FROM #__alpha_userpoints_raffle_inscriptions "
                    . " WHERE id IN ($winnerlist)");
                $winnerdata = $db->loadObjectList();
                foreach ($winnerarray as $winner) {
                    foreach ($winnerdata as $wdata) {
                        if ($wdata->id == $winner) {
                            $users[] = $wdata->userid;
                        }
                    }
                }
            } else {
                $users = $winnerarray;
            }
            if (isset($users)) {
                return PointsUser::makeWinnerLinks($users ?? []);
            }
        } elseif ($this->canDraw()) {
            $text = Text::_('AUP_MAKE_THE_RAFFLE_NOW');
            $link = "index.php?option=com_userpoints&amp;task=makeraffle&amp;id=$this->id";
            return <<<DRAW_LINK

            <a href="$link">$text</a>

DRAW_LINK;

        }
        return "<i>" . Text::_('AUP_PENDING') . "</i>";
    }

    /**
     * Select winners based on raffle configuration
     *
     * @param array $excluded Array of already chosen positions to exclude
     * @return array Array of winner user IDs
     */
    private function selectWinningEntries(array &$excluded): array {
        // Select winners based on number configured in raffle
        for ($i = 0; $i < $this->numwinner; $i++) {
            $winners[] = $this->chooseWinner($excluded);
        }
        return $winners ?? [];
    }

    /**
     * Choose a winner from eligible participants
     *
     * @param int $inscription Whether to use inscription mode or all users
     * @param int $raffleID Raffle ID
     * @param array $excluded Array of already chosen positions to exclude
     * @return int|false User ID of winner or false if no eligible winner found
     */
    private function chooseWinner(array &$excluded) {
        $count = $this->inscription ? count($this->issued) : PointsUser::countActiveUsers();
        if ($count <= count($excluded)) {
            return null;
        }

        $choice = Random::get(1, $count, $excluded);
        $excluded[] = $choice;

        if ($this->inscription) {
            $winningentry = array_keys($this->issued)[$choice-1];
            return $winningentry;
        } else {
            $winningentry = PointsUser::getActiveRecordN($choice);
            return $winningentry->userid ?? 0;
        }
    }

    /**
     * Process reward for a winner based on raffle system
     *
     * @param intPointsUser::makeWinnerLinks([$row->winner1, $row->winner2, $row->winner3]); $winnerId Winner user ID
     * @param int $winnerNumber Position number (1, 2, or 3)
     * @param object $params Component parameters
     * @return void
     */
    private function processReward(int $winnerId, int $winnerNumber): void {
        $referrerId = UserPoints::getAnyUserReferreID($winnerId);

        if (!$referrerId) {
            return;
        }

        switch ($this->rafflesystem) {
            case '1': // Coupon code
                $params = ComponentHelper::getParams('com_userpoints');
                $couponId = $this->{"couponcodeid$winnerNumber"} ?? 0;
                if ($couponId) {
                    // Send email notification if enabled
                    if ($this->sendcouponbyemail) {
                        $this->sendnotification4couponcode($referrerId, $couponId);
                    }

                    // Send uddeim notification if enabled
                    if ($params->get('sendMsgUddeim', 0) && $params->get('fromIdUddeim', 0)) {
                        $this->sendUddeimNotification($winnerId, $couponId, $params);
                    }
                }
                break;

            case '2': // Download link
                $downloadLink = $this->{"link2download$winnerNumber"} ?? '';
                if ($downloadLink) {
                    $this->sendnotificationDownload($referrerId, $downloadLink);
                }
                break;

            case '3': // Simple email
                $this->sendSimpleEmail($referrerId, $winnerNumber);
                break;

            default: // Points
                $pointsToEarn = $this->{"pointstoearn$winnerNumber"} ?? 0;
                if ($pointsToEarn) {
                    UserPoints::newpoints('sysplgup_raffle', $referrerId, '', $this->description, $pointsToEarn);
                }
        }
    }

    /**
     * Send uddeim notification for coupon code
     *
     * @param int $winnerId Winner user ID
     * @param int $couponId Coupon ID
     * @param object $params Component parameters
     * @return void
     */
    private function sendUddeimNotification(int $winnerId, int $couponId, $params): void {
        require_once(JPATH_SITE . '/components/com_userpoints/helpers/uddeim.api.php');

        $app = Factory::getApplication();
        $siteName = $app->getCfg('sitename');
        $fromId = (int) $params->get('fromIdUddeim', 0);

        if ($fromId && $winnerId) {
            $couponCode = (new Coupon($couponId))->couponcode;
            $message = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG_COUPONCODE'), $siteName, $couponCode);
            uddeIMAPI::sendNewMessage($fromId, $winnerId, $message);
        }
    }

    /**
     * Send notification email for coupon code
     *
     * @param int $referrerId User referrer ID
     * @param int $couponId Coupon ID
     * @return void
     */
    private function sendnotification4couponcode($referrerId, $couponId) {
        if (!$referrerId) {
            return;
        }

        $couponCode = (new Coupon($couponId))->couponcode;
        $app = Factory::getApplication();
        $params = ComponentHelper::getParams('com_userpoints');
        $jsNotification = $params->get('jsNotification', 0);
        $jsNotificationAdmin = $params->get('fromIdUddeim', 0);

        $mailFrom = $app->getCfg('mailfrom');
        $fromName = $app->getCfg('fromname');
        $siteName = $app->getCfg('sitename');

        $userInfo = UserPoints::getUserInfo($referrerId);
        $email = $userInfo->email;
        $subject = Text::_('AUP_EMAILNOTIFICATION_SUBJECT_COUPONCODE');
        $message = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG_COUPONCODE'), $siteName, $couponCode);

        $this->sendNotification($jsNotification, $jsNotificationAdmin, $userInfo, $mailFrom, $fromName, $email, $subject, $message);
    }

    /**
     * Send notification email with download link
     *
     * @param int $referrerId User referrer ID
     * @param string $linkToDownload Download link
     * @return void
     */
    private function sendnotificationDownload($referrerId, $linkToDownload) {
        if (!$referrerId) {
            return;
        }

        $app = Factory::getApplication();
        $params = ComponentHelper::getParams('com_userpoints');
        $jsNotification = $params->get('jsNotification', 0);
        $jsNotificationAdmin = $params->get('fromIdUddeim', 0);

        $mailFrom = $app->getCfg('mailfrom');
        $fromName = $app->getCfg('fromname');
        $siteName = $app->getCfg('sitename');

        $userInfo = UserPoints::getUserInfo($referrerId);
        $email = $userInfo->email;
        $subject = Text::_('AUP_EMAILNOTIFICATION_SUBJECT_MAIL_WITH_DOWNLOAD');
        $message = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG_MAIL_WITH_DOWNLOAD'), $siteName, $linkToDownload);

        $this->sendNotification($jsNotification, $jsNotificationAdmin, $userInfo, $mailFrom, $fromName, $email, $subject, $message);
    }

    /**
     * Send simple notification email
     *
     * @param int $referrerId User referrer ID
     * @param int $num Winner number (1, 2, or 3)
     * @return void
     */
    private function sendSimpleEmail($referrerId, $num) {
        if (!$referrerId) {
            return;
        }

        $app = Factory::getApplication();
        $params = ComponentHelper::getParams('com_userpoints');
        $jsNotification = $params->get('jsNotification', 0);
        $jsNotificationAdmin = $params->get('fromIdUddeim', 0);

        $mailFrom = $app->getCfg('mailfrom');
        $fromName = $app->getCfg('fromname');

        $userInfo = UserPoints::getUserInfo($referrerId);
        $email = $userInfo->email;
        $subject = Text::_('AUP_RF' . $num . '_SUBJECT_EMAIL');
        $message = Text::_('AUP_RF' . $num . '_BODY_EMAIL');

        $this->sendNotification($jsNotification, $jsNotificationAdmin, $userInfo, $mailFrom, $fromName, $email, $subject, $message);
    }

    /**
     * Common notification sending logic
     *
     * @param int $jsNotification JomSocial notification flag
     * @param int $jsNotificationAdmin JomSocial notification admin ID
     * @param object $userInfo User information
     * @param string $mailFrom Sender email
     * @param string $fromName Sender name
     * @param string $email Recipient email
     * @param string $subject Email subject
     * @param string $message Email message
     * @return void
     */
    private function sendNotification($jsNotification, $jsNotificationAdmin, $userInfo, $mailFrom, $fromName, $email, $subject, $message) {
        jimport('joomla.mail.helper');

        if (!$jsNotification) {
            // Send email notification
            $mailer = Factory::getMailer();
            $mailer->setSender(array($mailFrom, $fromName));
            $mailer->setSubject($subject);
            $mailer->CharSet = "utf-8";
            $mailer->setBody($message);
            $mailer->addRecipient($email);
            $mailer->Send();
        } else if ($userInfo->userid && $jsNotificationAdmin) {
            // Send JomSocial notification
            require_once JPATH_ROOT . '/components/com_community/libraries/core.php';
            $Cparams = new CParameter('');
            CNotificationLibrary::add('system_messaging', $jsNotificationAdmin, $userInfo->userid, $subject, $message, '', $Cparams);
        }
    }
}
