<?php

/**
 * Returns all tickets list
 * 
 * @return array
 */
function zb_TicketsGetAll() {
    $query = "SELECT * from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL ORDER BY `date` DESC";
    $result = simple_queryall($query);
    return ($result);
}

/**
 * Returns available tickets count
 * 
 * @return int
 */
function zb_TicketsGetCount() {
    $query = "SELECT COUNT(`id`) from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL";
    $result = simple_query($query);
    $result = $result['COUNT(`id`)'];
    return ($result);
}

/**
 * Returns tickets limited from-to
 * 
 * @param int $from
 * @param int $to
 * @return array
 */
function zb_TicketsGetLimited($from, $to) {
    $query = "SELECT * from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL  ORDER BY `date` DESC LIMIT " . $from . "," . $to . ";";
    $result = simple_queryall($query);
    return ($result);
}

/**
 * Returns count of new opened tickets
 * 
 * @return int
 */
function zb_TicketsGetAllNewCount() {
    $query = "SELECT COUNT(`id`) from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL AND `status`='0' ORDER BY `date` DESC";
    $result = simple_query($query);
    $result = $result['COUNT(`id`)'];
    return ($result);
}

/**
 * Returns all tickets by some existing usrname
 * 
 * @param string $login
 * @return array
 */
function zb_TicketsGetAllByUser($login) {
    $login = vf($login);
    $query = "SELECT `id`,`date`,`status` from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL AND `from`='" . $login . "' ORDER BY `date` DESC";
    $result = simple_queryall($query);
    return ($result);
}

/**
 * Returns tickets data by its ID
 * 
 * @param int $ticketid
 * @return array
 */
function zb_TicketGetData($ticketid) {
    $ticketid = vf($ticketid, 3);
    $query = "SELECT * from `ticketing` WHERE `id`='" . $ticketid . "'";
    $result = simple_query($query);
    return ($result);
}

/**
 * Returns thread ID for ticket/reply record.
 *
 * @param array $ticketData
 *
 * @return int
 */
function zb_TicketGetThreadIdByData($ticketData = array()) {
    $result = 0;
    if (!empty($ticketData)) {
        $ticketId = ubRouting::filters($ticketData['id'], 'int');
        $replyId = ubRouting::filters($ticketData['replyid'], 'int');
        if (!empty($replyId)) {
            $result = $replyId;
        } else {
            $result = $ticketId;
        }
    }
    return ($result);
}

/**
 * Returns safe ticket user login as formatted suffix.
 *
 * @param array $ticketData
 *
 * @return string
 */
function zb_TicketGetUserLoginSuffix($ticketData = array()) {
    $result = '()';
    if (!empty($ticketData)) {
        $userLogin = '';
        if (isset($ticketData['from'])) {
            $userLogin = trim($ticketData['from']);
        }
        if (empty($userLogin) or strtoupper($userLogin) == 'NULL') {
            if (isset($ticketData['to'])) {
                $userLogin = trim($ticketData['to']);
            }
        }
        if (!empty($userLogin) and strtoupper($userLogin) != 'NULL') {
            $userLogin = ubRouting::filters($userLogin, 'safe');
            $result = '(' . $userLogin . ')';
        }
    }
    return ($result);
}

/**
 * Returns array of replies by some existing ticket
 * 
 * @param int $ticketid
 * @return array
 */
function zb_TicketGetReplies($ticketid) {
    $ticketid = vf($ticketid, 3);
    $query = "SELECT * from `ticketing` WHERE `replyid`='" . $ticketid . "' ORDER by `id` ASC";
    $result = simple_queryall($query);
    return ($result);
}

/**
 * Deletes ticket from database
 * 
 * @param int $ticketid
 * 
 * @return void
 */
function zb_TicketDelete($ticketid) {
    $ticketid = vf($ticketid, 3);
    $query = "DELETE FROM `ticketing` WHERE `id`='" . $ticketid . "'";
    nr_query($query);
    log_register("TICKET DELETE [" . $ticketid . "]");
}

/**
 * Deletes all of ticket replies followed by some ticket
 * 
 * @param int $ticketid
 * 
 * @return void
 */
function zb_TicketDeleteReplies($ticketid) {
    $ticketid = vf($ticketid, 3);
    $query = "DELETE FROM `ticketing` WHERE `replyid`='" . $ticketid . "'";
    nr_query($query);
    log_register("TICKET REPLIES DELETE [" . $ticketid . "]");
}

/**
 * Deletes ticket reply from database
 * 
 * @param int $replyid
 * 
 * @return void
 */
function zb_TicketDeleteReply($replyid) {
    $replyid = vf($replyid, 3);
    $replyData = zb_TicketGetData($replyid);
    $threadId = zb_TicketGetThreadIdByData($replyData);
    $query = "DELETE FROM `ticketing` WHERE `id`='" . $replyid . "'";
    nr_query($query);
    log_register("TICKET REPLY DELETE [" . $replyid . "] [" . $threadId . "]");
}

/**
 * Updates reply text by its ID
 * 
 * @param int $replyid
 * @param string $newtext
 * 
 * @return void
 */
function zb_TicketUpdateReply($replyid, $newtext) {
    $replyid = vf($replyid, 3);
    $replyData = zb_TicketGetData($replyid);
    $threadId = zb_TicketGetThreadIdByData($replyData);
    $userLoginSuffix = zb_TicketGetUserLoginSuffix($replyData);
    $newtext = strip_tags($newtext);
    simple_update_field('ticketing', 'text', $newtext, "WHERE `id`='" . $replyid . "'");
    log_register("TICKET REPLY EDIT [" . $replyid . "] " . $userLoginSuffix . " [" . $threadId . "]");
}

/**
 * Creates new ticket into database
 * 
 * @param string $from
 * @param string $to
 * @param string $text
 * @param int $replyto
 * @param string $admin
 * 
 * @return int
 */
function zb_TicketCreate($from, $to, $text, $replyto = 'NULL', $admin = '') {
    $from = mysql_real_escape_string($from);
    $to = mysql_real_escape_string($to);
    $admin = mysql_real_escape_string($admin);
    $text = mysql_real_escape_string(strip_tags($text));
    $date = curdatetime();
    $replyto = vf($replyto);
    $query = "INSERT INTO `ticketing` (`id` , `date` , `replyid` , `status` ,`from` , `to` , `text`, `admin`) "
        . "VALUES (NULL , '" . $date . "', " . $replyto . ", '0', '" . $from . "', '" . $to . "', '" . $text . "', '" . $admin . "');";
    nr_query($query);

    $logreplyto = (empty($replyto)) ? '' : 'REPLY TO [' . $replyto . ']';
    $newticketid = simple_get_lastid('ticketing');
    log_register('TICKET CREATE (' . $to . ') ' . $logreplyto);
    if (!empty($replyto)) {
        log_register("TICKET REPLY CREATE [" . $newticketid . "] [" . $replyto . "]");
    }
    return ($newticketid);
}


/**
 * Sends message to Telegram chat
 * 
 * @param int $ticketId
 * @param string $message
 * @param int $chatId
 * 
 * @return array
 */
function zb_TicketSendCTTGMessage($ticketId, $message, $chatId) {
    global $ubillingConfig;
    $result = array(
        'ok' => false,
        'message_id' => 0,
        'raw_response' => '',
        'error' => ''
    );
    $ticketId = ubRouting::filters($ticketId, 'int');
    $chatId = ubRouting::filters($chatId, 'int');
    $message = strip_tags($message);
    $message = ubRouting::filters($message, 'safe');
    
    if ($ubillingConfig->getAlterParam('CLAPTRAPBOT_HELPDESK_INTEGRATION')) {
        $ticketData = zb_TicketGetData($ticketId);
        $threadId = zb_TicketGetThreadIdByData($ticketData);
        $userLoginSuffix = zb_TicketGetUserLoginSuffix($ticketData);

        $clapTrapBot = new ClapTrapBot($ubillingConfig->getAlterParam('CLAPTRAPBOT_TOKEN'));
        $sendingResultRaw = $clapTrapBot->directSendToChatId($chatId, $message);
        $result = zb_TicketCTTGParseApiResult($sendingResultRaw);
        $result['raw_response'] = $sendingResultRaw;

        $isCurrent = 0;
        if ($result['ok'] and !empty($result['message_id'])) {
            zb_TicketCTTGMapSetCurrent($ticketId, 0);
            $isCurrent = 1;
            log_register('TICKET TG SEND OK REPLY [' . $ticketId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` MSGID `' . $result['message_id'] . '` [' . $threadId . ']');
        } else {
            log_register('TICKET TG SEND FAIL REPLY [' . $ticketId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` [' . $threadId . ']');
        }

        zb_TicketCTTGMapLogAction(
            $ticketId,
            $threadId,
            $chatId,
            $result['message_id'],
            'send',
            ($result['ok']) ? 'ok' : 'fail',
            $message,
            $sendingResultRaw,
            $result['error'],
            whoami(),
            $isCurrent
        );
    }

    return ($result);
}

/**
 * Parses Telegram API JSON response.
 *
 * @param string $rawResult
 *
 * @return array
 */
function zb_TicketCTTGParseApiResult($rawResult) {
    $result = array(
        'ok' => false,
        'message_id' => 0,
        'error' => ''
    );
    if (!empty($rawResult)) {
        $decoded = json_decode($rawResult, true);
        if (is_array($decoded)) {
            if (!empty($decoded['ok'])) {
                $result['ok'] = true;
            }

            if (isset($decoded['result'])) {
                if (is_array($decoded['result'])) {
                    if (isset($decoded['result']['message_id'])) {
                        $result['message_id'] = ubRouting::filters($decoded['result']['message_id'], 'int');
                    }
                }
            }

            if (isset($decoded['description'])) {
                $result['error'] = ubRouting::filters($decoded['description'], 'safe');
            }
        } else {
            $result['error'] = 'Invalid telegram JSON reply';
        }
    } else {
        $result['error'] = 'Empty telegram reply';
    }
    return ($result);
}

/**
 * Writes Telegram sync action into dedicated ticketing map table.
 *
 * @param int $ticketReplyId
 * @param int $ticketThreadId
 * @param int $chatId
 * @param int $messageId
 * @param string $action
 * @param string $status
 * @param string $requestPayload
 * @param string $responsePayload
 * @param string $errorText
 * @param string $admin
 * @param int $isCurrent
 *
 * @return void
 */
function zb_TicketCTTGMapLogAction($ticketReplyId, $ticketThreadId, $chatId, $messageId, $action, $status, $requestPayload = '', $responsePayload = '', $errorText = '', $admin = '', $isCurrent = 0) {
    $ticketReplyId = ubRouting::filters($ticketReplyId, 'int');
    $ticketThreadId = ubRouting::filters($ticketThreadId, 'int');
    $chatId = ubRouting::filters($chatId, 'int');
    $messageId = ubRouting::filters($messageId, 'int');
    $isCurrent = ubRouting::filters($isCurrent, 'int');
    $action = mysql_real_escape_string($action);
    $status = mysql_real_escape_string($status);
    $admin = mysql_real_escape_string($admin);
    $requestPayload = mysql_real_escape_string($requestPayload);
    $responsePayload = mysql_real_escape_string($responsePayload);
    $errorText = mysql_real_escape_string($errorText);
    $mapDb = new NyanORM('ticketing_tgmap');
    $mapDb->data('ticket_reply_id', $ticketReplyId);
    $mapDb->data('ticket_thread_id', $ticketThreadId);
    $mapDb->data('chat_id', $chatId);
    $mapDb->data('message_id', $messageId);
    $mapDb->data('action', $action);
    $mapDb->data('status', $status);
    $mapDb->data('request_payload', $requestPayload);
    $mapDb->data('response_payload', $responsePayload);
    $mapDb->data('error_text', $errorText);
    $mapDb->data('is_current', $isCurrent);
    $mapDb->data('admin', $admin);
    $mapDb->data('created_at', curdatetime());
    $mapDb->data('updated_at', curdatetime());
    $mapDb->create();
}

/**
 * Sets current state flag for all Telegram map records by reply ID.
 *
 * @param int $ticketReplyId
 * @param int $state
 *
 * @return void
 */
function zb_TicketCTTGMapSetCurrent($ticketReplyId, $state = 0) {
    $ticketReplyId = ubRouting::filters($ticketReplyId, 'int');
    $state = ubRouting::filters($state, 'int');
    $mapDb = new NyanORM('ticketing_tgmap');
    $mapDb->where('ticket_reply_id', '=', $ticketReplyId);
    $mapDb->data('is_current', $state);
    $mapDb->data('updated_at', curdatetime());
    $mapDb->save(true, true);
}

/**
 * Returns latest current Telegram map record for reply.
 *
 * @param int $ticketReplyId
 *
 * @return array
 */
function zb_TicketCTTGMapGetCurrent($ticketReplyId) {
    $result = array();
    $ticketReplyId = ubRouting::filters($ticketReplyId, 'int');
    $mapDb = new NyanORM('ticketing_tgmap');
    $mapDb->where('ticket_reply_id', '=', $ticketReplyId);
    $mapDb->where('is_current', '=', '1');
    $mapDb->orderBy('id', 'DESC');
    $mapDb->limit(1);
    $mapData = $mapDb->getAll();
    if (!empty($mapData)) {
        $result = $mapData[0];
    }
    return ($result);
}

/**
 * Returns latest statuses for Telegram actions by reply ID.
 *
 * @param int $ticketReplyId
 *
 * @return array
 */
function zb_TicketCTTGMapGetStatusByReply($ticketReplyId) {
    $result = array(
        'has_records' => false,
        'send' => '',
        'edit' => '',
        'delete' => ''
    );
    $ticketReplyId = ubRouting::filters($ticketReplyId, 'int');
    $mapDb = new NyanORM('ticketing_tgmap');
    $mapDb->selectable('action,status');
    $mapDb->where('ticket_reply_id', '=', $ticketReplyId);
    $mapDb->orderBy('id', 'DESC');
    $rawData = $mapDb->getAll();
    if (!empty($rawData)) {
        $result['has_records'] = true;
        foreach ($rawData as $io => $eachRecord) {
            if ($eachRecord['action'] == 'send' and empty($result['send'])) {
                $result['send'] = $eachRecord['status'];
            } else {
                if ($eachRecord['action'] == 'edit' and empty($result['edit'])) {
                    $result['edit'] = $eachRecord['status'];
                } else {
                    if ($eachRecord['action'] == 'delete' and empty($result['delete'])) {
                        $result['delete'] = $eachRecord['status'];
                    }
                }
            }
        }
    }
    return ($result);
}

/**
 * Returns textual status label for Telegram action.
 *
 * @param string $status
 *
 * @return string
 */
function zb_TicketCTTGMapStatusLabel($status = '') {
    $result = 'n/a';
    if (!empty($status)) {
        $result = strtoupper($status);
    }

    $result = __($result);
    return ($result);
}

/**
 * Renders compact Telegram delivery status for ticket reply.
 *
 * @param int $replyId
 * @param bool $isAdminReply
 *
 * @return string
 */
function web_TicketReplyCTTGState($replyId, $isAdminReply = false) {
    $result = '';
    $replyId = ubRouting::filters($replyId, 'int');
    if ($isAdminReply) {
        $tgStatus = zb_TicketCTTGMapGetStatusByReply($replyId);
        if (!empty($tgStatus['has_records'])) {
            $hasCriticalError = false;
            if ($tgStatus['send'] == 'fail' or $tgStatus['edit'] == 'fail') {
                $hasCriticalError = true;
            }
            $iconPath = 'skins/icon_telegram_16.png';
            if ($hasCriticalError) {
                $iconPath = 'skins/icon_telegram_black_16.png';
            }
            $title = 'Telegram: '.__('Sended').': ' . zb_TicketCTTGMapStatusLabel($tgStatus['send']);
            $title .= ', '.__('Edited').': ' . zb_TicketCTTGMapStatusLabel($tgStatus['edit']);
            $title .= ', '.__('Deleted').': ' . zb_TicketCTTGMapStatusLabel($tgStatus['delete']);
            $result .= wf_img($iconPath, $title);
        }
    }
    return ($result);
}

/**
 * Updates previously sent Telegram message for selected reply.
 *
 * @param int $replyId
 * @param string $newMessage
 *
 * @return array
 */
function zb_TicketCTTGMessageEdit($replyId, $newMessage) {
    global $ubillingConfig;
    $result = array(
        'ok' => false,
        'message_id' => 0,
        'raw_response' => '',
        'error' => ''
    );
    $replyId = ubRouting::filters($replyId, 'int');
    $newMessage = strip_tags($newMessage);
    $newMessage = ubRouting::filters($newMessage, 'safe');
    $mapData = zb_TicketCTTGMapGetCurrent($replyId);
    $ticketData = zb_TicketGetData($replyId);
    $threadId = 0;
    $userLoginSuffix = zb_TicketGetUserLoginSuffix($ticketData);
    if (!empty($ticketData)) {
        $threadId = ubRouting::filters($ticketData['replyid'], 'int');
    }

    if ($ubillingConfig->getAlterParam('CLAPTRAPBOT_HELPDESK_INTEGRATION')) {
        if (!empty($mapData) and !empty($mapData['chat_id']) and !empty($mapData['message_id'])) {
            $chatId = ubRouting::filters($mapData['chat_id'], 'int');
            $messageId = ubRouting::filters($mapData['message_id'], 'int');
            $editRequest = 'editMessageText:[' . $messageId . '@' . $chatId . ']' . $newMessage;
            $clapTrapBot = new ClapTrapBot($ubillingConfig->getAlterParam('CLAPTRAPBOT_TOKEN'));
            $editResultRaw = $clapTrapBot->directSendToChatId($chatId, $editRequest);
            $result = zb_TicketCTTGParseApiResult($editResultRaw);
            $result['raw_response'] = $editResultRaw;
            $result['message_id'] = $messageId;
            zb_TicketCTTGMapLogAction(
                $replyId,
                $threadId,
                $chatId,
                $messageId,
                'edit',
                ($result['ok']) ? 'ok' : 'fail',
                $newMessage,
                $editResultRaw,
                $result['error'],
                whoami(),
                1
            );
            if ($result['ok']) {
                log_register('TICKET TG EDIT OK REPLY [' . $replyId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` MSGID `' . $messageId . '` [' . $threadId . ']');
            } else {
                log_register('TICKET TG EDIT FAIL REPLY [' . $replyId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` MSGID `' . $messageId . '` [' . $threadId . ']');
            }
        } else {
            $result['error'] = 'Telegram message mapping not found';
            zb_TicketCTTGMapLogAction(
                $replyId,
                $threadId,
                0,
                0,
                'edit',
                'skip',
                $newMessage,
                '',
                $result['error'],
                whoami(),
                0
            );
        }
    }
    return ($result);
}

/**
 * Deletes previously sent Telegram message for selected reply.
 *
 * @param int $replyId
 *
 * @return array
 */
function zb_TicketCTTGMessageDelete($replyId) {
    global $ubillingConfig;
    $result = array(
        'ok' => false,
        'message_id' => 0,
        'raw_response' => '',
        'error' => ''
    );
    $replyId = ubRouting::filters($replyId, 'int');
    $mapData = zb_TicketCTTGMapGetCurrent($replyId);
    $ticketData = zb_TicketGetData($replyId);
    $threadId = 0;
    $userLoginSuffix = zb_TicketGetUserLoginSuffix($ticketData);
    if (!empty($ticketData)) {
        $threadId = ubRouting::filters($ticketData['replyid'], 'int');
    }

    if ($ubillingConfig->getAlterParam('CLAPTRAPBOT_HELPDESK_INTEGRATION')) {
        if (!empty($mapData) and !empty($mapData['chat_id']) and !empty($mapData['message_id'])) {
            $chatId = ubRouting::filters($mapData['chat_id'], 'int');
            $messageId = ubRouting::filters($mapData['message_id'], 'int');
            $deleteRequest = 'removeChatMessage:[' . $messageId . '@' . $chatId . ']';
            $clapTrapBot = new ClapTrapBot($ubillingConfig->getAlterParam('CLAPTRAPBOT_TOKEN'));
            $deleteResultRaw = $clapTrapBot->directSendToChatId($chatId, $deleteRequest);
            $result = zb_TicketCTTGParseApiResult($deleteResultRaw);
            $result['raw_response'] = $deleteResultRaw;
            $result['message_id'] = $messageId;
            zb_TicketCTTGMapLogAction(
                $replyId,
                $threadId,
                $chatId,
                $messageId,
                'delete',
                ($result['ok']) ? 'ok' : 'fail',
                '',
                $deleteResultRaw,
                $result['error'],
                whoami(),
                0
            );
            if ($result['ok']) {
                zb_TicketCTTGMapSetCurrent($replyId, 0);
                log_register('TICKET TG DELETE OK REPLY [' . $replyId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` MSGID `' . $messageId . '` [' . $threadId . ']');
            } else {
                log_register('TICKET TG DELETE FAIL REPLY [' . $replyId . '] ' . $userLoginSuffix . ' CHAT `' . $chatId . '` MSGID `' . $messageId . '` [' . $threadId . ']');
            }
        } else {
            $result['error'] = 'Telegram message mapping not found';
            zb_TicketCTTGMapLogAction(
                $replyId,
                $threadId,
                0,
                0,
                'delete',
                'skip',
                '',
                '',
                $result['error'],
                whoami(),
                0
            );
        }
    }
    return ($result);
}

/**
 * Marks ticket as closed in database
 * 
 * @param int $ticketid
 * 
 * @return void
 */
function zb_TicketSetDone($ticketid) {
    $ticketid = vf($ticketid, 3);
    simple_update_field('ticketing', 'status', '1', "WHERE `id`='" . $ticketid . "'");
    log_register("TICKET CLOSE [" . $ticketid . "]");
}

/**
 * Marks ticket as unreviewed in database
 * 
 * @param int $ticketid
 * 
 * @return void
 */
function zb_TicketSetUnDone($ticketid) {
    $ticketid = vf($ticketid, 3);
    simple_update_field('ticketing', 'status', '0', "WHERE `id`='" . $ticketid . "'");
    log_register("TICKET OPEN [" . $ticketid . "]");
}

/**
 * Renders available tickets list with controls
 * 
 * @global object $ubillingConfig
 * @return string
 */
function web_TicketsShow() {
    global $ubillingConfig;
    $alterconf = $ubillingConfig->getAlter();
    //pagination section
    $totalcount = zb_TicketsGetCount();
    $perpage = $alterconf['TICKETS_PERPAGE'];

    if (!isset($_GET['page'])) {
        $current_page = 1;
    } else {
        $current_page = vf($_GET['page'], 3);
    }

    if ($totalcount > $perpage) {
        $paginator = wf_pagination($totalcount, $perpage, $current_page, "?module=ticketing", 'ubButton', 16);
        $alltickets = zb_TicketsGetLimited($perpage * ($current_page - 1), $perpage);
    } else {
        $paginator = '';
        $alltickets = zb_TicketsGetAll();
    }


    $tablecells = wf_TableCell(__('ID'));
    $tablecells .= wf_TableCell(__('Date'));
    $tablecells .= wf_TableCell(__('From'));
    $tablecells .= wf_TableCell(__('Real Name'));
    $tablecells .= wf_TableCell(__('Full address'));
    $tablecells .= wf_TableCell(__('IP'));
    $tablecells .= wf_TableCell(__('Tariff'));
    $tablecells .= wf_TableCell(__('Balance'));
    $tablecells .= wf_TableCell(__('Credit'));
    $tablecells .= wf_TableCell(__('Processed'));
    $tablecells .= wf_TableCell(__('Actions'));
    $tablerows = wf_TableRow($tablecells, 'row1');

    if (!empty($alltickets)) {
        $allUserData=zb_UserGetAllDataCache();
        foreach ($alltickets as $io => $eachticket) {
            $tablecells = wf_TableCell($eachticket['id']);
            $tablecells .= wf_TableCell($eachticket['date']);
            $fromlink = wf_Link('?module=userprofile&username=' . $eachticket['from'], web_profile_icon() . ' ' . $eachticket['from']);
            $tablecells .= wf_TableCell($fromlink);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['realname']);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['fulladress']);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['ip']);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['Tariff']);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['Cash']);
            $tablecells .= wf_TableCell(@$allUserData[$eachticket['from']]['Credit']);
            $tablecells .= wf_TableCell(web_bool_led($eachticket['status']), '', '', 'sorttable_customkey="' . $eachticket['status'] . '"');
            $actionlink = wf_Link('?module=ticketing&showticket=' . $eachticket['id'], wf_img_sized('skins/icon_search_small.gif', '', '12') . ' ' . __('Show'), false, 'ubButton');
            $tablecells .= wf_TableCell($actionlink);
            $tablerows .= wf_TableRow($tablecells, 'row3');
        }
    }
    $result = wf_TableBody($tablerows, '100%', '0', 'sortable');
    $result .= $paginator;

    return ($result);
}

/**
 * Returns typical answer preset creation form
 * 
 * @return string
 */
function web_TicketsTAPAddForm() {
    $inputs = wf_HiddenInput('createnewtap', 'true');
    $inputs .= wf_TextArea('newtaptext', '', '', true, '60x10');
    $inputs .= wf_Submit(__('Create'));
    $result = wf_Form('', "POST", $inputs, 'glamour');
    return ($result);
}

/**
 * Returns typical answer preset edit form
 * 
 * @param string $keyname
 * @param string $text
 * 
 * @return string
 */
function web_TicketsTAPEditForm($keyname, $text) {
    $inputs = wf_HiddenInput('edittapkey', $keyname);
    $inputs .= wf_TextArea('edittaptext', '', $text, true, '60x10');
    $inputs .= wf_Submit(__('Save'));
    $result = wf_Form('', 'POST', $inputs, 'glamour');
    return ($result);
}

/**
 * Creates new typical answer preset in database
 * 
 * @param string $taptext
 * 
 * @return void
 */
function zb_TicketsTAPCreate($taptext) {
    $keyName = 'HELPDESKTAP_' . zb_rand_string(8);
    $storeData = base64_encode($taptext);
    zb_StorageSet($keyName, $storeData);
    log_register('TICKET TAP CREATE `' . $keyName . '`');
}

/**
 * Deletes existing typical answer preset from database
 * 
 * @param string $keyname
 * 
 * @return void
 */
function zb_TicketsTAPDelete($keyname) {
    $keyname = mysql_real_escape_string($keyname);
    $query = "DELETE from `ubstorage` WHERE `key`='" . $keyname . "'";
    nr_query($query);
    log_register('TICKET TAP DELETE `' . $keyname . '`');
}

/**
 * Changes existing typical answer preset data in database
 * 
 * @param string $key
 * @param string $text
 * 
 * @return void
 */
function zb_TicketsTAPEdit($key, $text) {
    $storeData = base64_encode($text);
    zb_StorageSet($key, $storeData);
    log_register('TICKET TAP CHANGE `' . $key . '`');
}

/**
 * Returns all available typical answer presets array
 * 
 * @return array
 */
function zb_TicketsTAPgetAll() {
    $result = array();
    $query = "SELECT * from `ubstorage` WHERE `key` LIKE 'HELPDESKTAP_%' ORDER BY `id` ASC";
    $all = simple_queryall($query);
    if (!empty($all)) {
        foreach ($all as $io => $each) {
            @$tmpData = base64_decode($each['value']);
            @$tmpData = str_replace("'", '`', $tmpData);
            $result[$each['key']] = $tmpData;
        }
    }
    return ($result);
}

/**
 * Renders available typical answer presets list with controls
 * 
 * @return string
 */
function web_TicketsTapShowAvailable() {
    $all = zb_TicketsTAPgetAll();

    $cells = wf_TableCell(__('Text'), '90%');
    $cells .= wf_TableCell(__('Actions'));
    $rows = wf_TableRow($cells, 'row1');

    if (!empty($all)) {
        foreach ($all as $io => $each) {
            $cells = wf_TableCell($each);
            $actlinks = wf_JSAlert('?module=ticketing&settings=true&deletetap=' . $io, web_delete_icon(), __('Removing this may lead to irreparable results'));
            $actlinks .= wf_modalAuto(web_edit_icon(), __('Edit'), web_TicketsTAPEditForm($io, $each), '');
            $cells .= wf_TableCell($actlinks);
            $rows .= wf_TableRow($cells, 'row3');
        }
    }

    $result = wf_TableBody($rows, '100%', '0', 'sortable');
    return ($result);
}

/**
 * Returns typical answer preset insertion form
 * 
 * @return string
 */
function web_TicketsTAPLister() {
    $result = '';
    $maxLen = 50;
    $allReplies = zb_TicketsTAPgetAll();
    if (!empty($allReplies)) {
        $result .= wf_delimiter() . wf_tag('h3') . __('Typical answers presets') . wf_tag('h3', true);
        $result .= wf_tag('ul', false);
        foreach ($allReplies as $io => $each) {
            $randId = wf_InputId();
            $rawText = trim($each);
            $result .= wf_tag('script', false, '', 'language="javascript" type="text/javascript"');
            $encodedReply = json_encode($rawText);
            $encodedReply = rtrim($encodedReply, '"');
            $encodedReply = ltrim($encodedReply, '"');
            $result .= '
                    function jsAddReplyText_' . $randId . '() {
                         var replytext=\'' . $encodedReply . '\';
                         $("#ticketreplyarea").val(replytext);
                    }
                    ';
            $result .= wf_tag('script', true);

            $linkText = htmlspecialchars($rawText);
            if (mb_strlen($linkText, 'UTF-8') > $maxLen) {
                $linkText = mb_substr($rawText, 0, $maxLen, 'UTF-8') . '..';
            } else {
                $linkText = $rawText;
            }

            $result .= wf_tag('li') . wf_tag('a', false, '', 'href="#" onClick="jsAddReplyText_' . $randId . '();"') . $linkText . wf_tag('a', true) . wf_tag('li', true);
        }
        $result .= wf_tag('ul', true);
    }
    return ($result);
}

/**
 * Returns ticket reply form with typical answer presets if its available
 * 
 * @param int $ticketid
 * @param int $clapTrapEnableFlag 0 - disabled, 1 - enabled, 2 - checkbox checked
 * @param int $claptrapChatId 
 * 
 * @return string
 */
function web_TicketReplyForm($ticketid, $clapTrapEnableFlag = 0, $claptrapChatId = 0) {
    $ticketid = vf($ticketid, 3);
    $ticketdata = zb_TicketGetData($ticketid);
    if (!empty($ticketdata)) {
    $ticketstate = $ticketdata['status'];
    if (!$ticketstate) {
        $replyinputs = wf_HiddenInput('postreply', $ticketid);
        $replyinputs .= wf_tag('textarea', false, '', 'name="replytext" cols="60" rows="10"  id="ticketreplyarea"') . wf_tag('textarea', true) . wf_tag('br');
        if ($clapTrapEnableFlag and !empty($claptrapChatId)) {
            $tgCheckBoxState = ($clapTrapEnableFlag == 2) ? true : false;
            $replyinputs .= wf_CheckInput('cttgsendmsg', __('Send also to Telegram'), true, $tgCheckBoxState);
            $replyinputs .= wf_HiddenInput('cttgchatid', $claptrapChatId);
        }
        $replyinputs .= wf_Submit('Reply');
        $replyform = wf_Form('', 'POST', $replyinputs, 'glamour');
        $replyform .= web_TicketsTAPLister();
    } else {
        $replyform = __('Ticket is closed');
    }

    //ajax background render
    if (wf_CheckGet(array('ajevents'))) {
        $currentTicketEvents = wf_tag('h3') . __('Events for ticket') . '  ' . $ticketid . wf_tag('h3', true);
        $currentTicketEvents .= getTicketEvents($ticketid, true);
        die($currentTicketEvents);
    }

    //previous ticket events
    $replyform .= wf_AjaxLoader();
    $replyform .= wf_delimiter();
    $replyform .= wf_AjaxLink('?module=ticketing&showticket=' . $ticketid . '&ajevents=true', wf_img('skins/log_icon_small.png') . ' ' . __('Show ticket events'), 'ajticketevents', false, 'ubButton');
    $replyform .= wf_AjaxContainer('ajticketevents', '', '');
    } else {
        $replyform = __('Something went wrong').': '.__('Not existing ticket');
    }
    return ($replyform);
}

/**
 * Returns reply edit form
 * 
 * @param int $replyid
 * 
 * @return string
 */
function web_TicketReplyEditForm($replyid) {
    $replyid = vf($replyid, 3);
    $ticketdata = zb_TicketGetData($replyid);
    $replytext = $ticketdata['text'];

    $inputs = wf_HiddenInput('editreply', $replyid);
    $inputs .= wf_TextArea('editreplytext', '', $replytext, true, '60x10');
    $inputs .= wf_Submit('Save');
    $form = wf_Form('', 'POST', $inputs, 'glamour');

    return ($form);
}



/**
 * Renders all ticket replies block
 * 
 * @param int $ticketid
 * @param array $ticketreplies
 * 
 * @return string
 */
function web_TicketReplies($ticketid, $ticketreplies = array()) {
    $result = '';
    $ticketid = ubRouting::filters($ticketid, 'int');
    $ticketdata = zb_TicketGetData($ticketid);
    @$employeeNames = unserialize(ts_GetAllEmployeeLoginsCached());
    $userRealName = __('Unknown');

    if (empty($ticketreplies)) {
        $ticketreplies = zb_TicketGetReplies($ticketid);
    }

    if (!empty($ticketdata)) {
        $userLogin = $ticketdata['from'];
      
        $allUserRealNames = zb_UserGetAllRealnamesCache();
        if (isset($allUserRealNames[$userLogin])) {
            $userRealName = $allUserRealNames[$userLogin];
        }

        if (!empty($ticketreplies)) {
            $result .= wf_tag('h2') . __('Replies') . wf_tag('h2', true);
            $result .= wf_CleanDiv();
            foreach ($ticketreplies as $io => $eachreply) {
                //reply
                $isAdminReply = (!empty($eachreply['admin'])) ? true : false;
                if ($eachreply['admin']) {
                    $adminRealName = (isset($employeeNames[$eachreply['admin']])) ? $employeeNames[$eachreply['admin']] : $eachreply['admin'];
                    $replyauthor = wf_tag('center') . wf_tag('b') . $adminRealName . wf_tag('b', true) . wf_tag('center', true);
                    $replyavatar = wf_tag('center') . FaceKit::getAvatar($eachreply['admin'], '64') . wf_tag('center', true);
                } else {
                    $replyauthor = wf_tag('center') . wf_tag('b') . $userRealName . wf_tag('b', true) . wf_tag('center', true);
                    $replyavatar = wf_tag('center') . wf_img('skins/userava.png') . wf_tag('center', true);
                }

                $replyactions = '';
                if ($isAdminReply) {
                    $replyactions = wf_tag('center');
                    $replyactions .= wf_JSAlert('?module=ticketing&showticket=' . $ticketdata['id'] . '&deletereply=' . $eachreply['id'], web_delete_icon(), 'Removing this may lead to irreparable results') . ' ';
                    $replyactions .= wf_JSAlert('?module=ticketing&showticket=' . $ticketdata['id'] . '&editreply=' . $eachreply['id'], web_edit_icon(), 'Are you serious').' ';
                    $tgStateControl = web_TicketReplyCTTGState($eachreply['id'], $isAdminReply);
                    if (!empty($tgStateControl)) {
                        $replyactions .= ' ' . $tgStateControl;
                    }
                    $replyactions .= wf_tag('center', true);
                } else {
                    $replyactions = wf_delimiter(0);
                }

                // reply body 
                if (ubRouting::checkGet('editreply')) {
                    if (ubRouting::get('editreply', 'int') == $eachreply['id'] and $isAdminReply) {
                        //is this reply editing?
                        $replytext = web_TicketReplyEditForm($eachreply['id']);
                    } else {
                        //not this ticket edit
                        $replytext = strip_tags($eachreply['text']);
                    }
                } else {
                    //normal text by default
                    $replytext = strip_tags($eachreply['text']);
                    $replytext = nl2br($replytext);
                }

                $replypanel = $replyauthor . wf_tag('br') . $replyavatar;
                if (!empty($replyactions)) {
                    $replypanel .= wf_tag('br') . $replyactions;
                }

                $tablecells = wf_TableCell('', '20%');
                $tablecells .= wf_TableCell($eachreply['date']);
                $tablerows = wf_TableRow($tablecells, 'row2');

                $tablecells = wf_TableCell($replypanel);
                $tablecells .= wf_TableCell($replytext);
                $tablerows .= wf_TableRow($tablecells, 'row3');

                $result .= wf_TableBody($tablerows, '100%', '0', 'glamour');
                $result .= wf_CleanDiv();
            }
        }
    }

    return ($result);
}

/**
 * Renders ticket, all of replies and all needed controls/forms for they
 * 
 * @param int $ticketid
 * 
 * @return string
 */
function web_TicketDialogue($ticketid) {
    global $ubillingConfig;
    $result = '';
    $ticketid = ubRouting::filters($ticketid, 'int');
    $ticketdata = zb_TicketGetData($ticketid);
    $result .= wf_tag('p', false, '', 'align="right"') . wf_BackLink('?module=ticketing', 'Back to tickets list', true) . wf_tag('p', true);

    if (!empty($ticketdata)) {
    $ticketreplies = zb_TicketGetReplies($ticketid);
    $dialog = array();
    $lastUserPrompt = '';
    $moreContextFlag = $ubillingConfig->getAlterParam('HIVE_MORE_CONTEXT', 0);
    $clapTrapEnableFlag = $ubillingConfig->getAlterParam('CLAPTRAPBOT_HELPDESK_INTEGRATION', 0);
    $clapTrapAuthData =array();
    $claptrapChatId = 0;
    $claptrapActive = 0;

    if ($clapTrapEnableFlag) {
        $botToken = $ubillingConfig->getAlterParam('CLAPTRAPBOT_TOKEN');
        $clapTrapBot = new ClapTrapBot($botToken);
        $allAuthData = $clapTrapBot->getAuthDataAll();
        if (isset($allAuthData[$ticketdata['from']])) {
            $clapTrapAuthData = $allAuthData[$ticketdata['from']];
            $claptrapChatId = $clapTrapAuthData['chatid'];
            $claptrapActive = $clapTrapAuthData['active'];
        }
    }

        $userLogin = $ticketdata['from'];
        //this data not used cache, to be 100% actual
        $userData = zb_UserGetAllData($userLogin);
        $userData = $userData[$userLogin];

        if (!empty($userData)) {
            $userAddress = $userData['fulladress'];
            $userRealName = $userData['realname'];
            $userIp = $userData['ip'];
            $userCredit = $userData['Credit'];
            $userCash = $userData['Cash'];
            $userTariff = $userData['Tariff'];
        } else {
            $userAddress = __('Unknown');
            $userRealName = __('Unknown');
            $userIp = __('Unknown');
            $userCredit = __('Unknown');
            $userCash = __('Unknown');
            $userTariff = __('Unknown');
        }


        if ($ticketdata['status']) {
            $actionlink = wf_Link('?module=ticketing&openticket=' . $ticketdata['id'], wf_img('skins/icon_unlock.png') . ' ' . __('Open'), false, 'ubButton');
        } else {
            $actionlink = wf_Link('?module=ticketing&closeticket=' . $ticketdata['id'], wf_img('skins/icon_lock.png') . ' ' . __('Close'), false, 'ubButton');
        }


        $tablecells = wf_TableCell(__('ID'));
        $tablecells .= wf_TableCell(__('Date'));
        $tablecells .= wf_TableCell(__('Login'));
        $tablecells .= wf_TableCell(__('Real Name'));
        $tablecells .= wf_TableCell(__('Full address'));
        $tablecells .= wf_TableCell(__('IP'));
        $tablecells .= wf_TableCell(__('Tariff'));
        $tablecells .= wf_TableCell(__('Balance'));
        $tablecells .= wf_TableCell(__('Credit'));
        if ($clapTrapEnableFlag) {
            $tablecells .= wf_TableCell(__('Telegram bot'));
        }

        $tablecells .= wf_TableCell(__('Processed'));
        $tablerows = wf_TableRow($tablecells, 'row1');

        $tablecells = wf_TableCell($ticketdata['id']);
        $tablecells .= wf_TableCell($ticketdata['date']);
        $profilelink = wf_Link('?module=userprofile&username=' . $ticketdata['from'], web_profile_icon() . ' ' . $ticketdata['from']);
        $tablecells .= wf_TableCell($profilelink);
        $tablecells .= wf_TableCell($userRealName);
        $tablecells .= wf_TableCell($userAddress);
        $tablecells .= wf_TableCell($userIp);
        $tablecells .= wf_TableCell($userTariff);
        $tablecells .= wf_TableCell($userCash);
        $tablecells .= wf_TableCell($userCredit);
        if ($clapTrapEnableFlag) {
            $tablecells .= wf_TableCell(web_bool_led($claptrapActive));
        }

        $tablecells .= wf_TableCell(web_bool_led($ticketdata['status']));
        $tablerows .= wf_TableRow($tablecells, 'row3');
        $result .= wf_TableBody($tablerows, '100%', '0');

        //ticket body
        $tickettext = strip_tags($ticketdata['text']);
        $tickettext = nl2br($tickettext);
        $tablecells = wf_TableCell('', '20%');
        $tablecells .= wf_TableCell($ticketdata['date']);
        $tablerows = wf_TableRow($tablecells, 'row2');

        $ticketauthor = wf_tag('center') . wf_tag('b') . $userRealName . wf_tag('b', true) . wf_tag('center', true);
        $ticketavatar = wf_tag('center') . wf_img('skins/userava.png') . wf_tag('center', true);
        $ticketpanel = $ticketauthor . wf_tag('br') . $ticketavatar;

        $tablecells = wf_TableCell($ticketpanel);
        $tablecells .= wf_TableCell($tickettext);
        $tablerows .= wf_TableRow($tablecells, 'row3');

        $result .= wf_TableBody($tablerows, '100%', '0', 'glamour');
        $result .= $actionlink;
        //pushing some context
        if ($moreContextFlag) {
            if (!empty($userData)) {
                $currency = $ubillingConfig->getAlterParam('TEMPLATE_CURRENCY', 'UAH');
                $userState = zb_UserIsAlive($userData);
                $stateLabel = __('Unknown');
                switch ($userState) {
                    case 0:
                        $stateLabel = __('Inactive');
                        break;
                    case 1:
                        $stateLabel = __('Active');
                        break;
                    case -1:
                        $stateLabel = __('User passive');
                        break;
                }
                $userContext = '';
                $userContext .= __('Also take into account these data') . ' ' . PHP_EOL;
                $userContext .= __('Here is some information about user') . ': ' . PHP_EOL;
                $userContext .= __('Real Name') . ': ' . $userData['realname'] . PHP_EOL;
                $userContext .= __('Address') . ': ' . $userData['fulladress'] . PHP_EOL;
                $userContext .= __('Account balance') . ': ' . $userData['Cash'] . ' ' . $currency . PHP_EOL;
                if ($userData['Credit']) {
                    $userContext .= __('Credit limit') . ': ' . $userData['Credit'] . ' ' . $currency . PHP_EOL;
                    $stgUserData = zb_UserGetStargazerData($userLogin);
                    if ($stgUserData['CreditExpire']) {
                        $expireLabel = date("Y-m-d", $stgUserData['CreditExpire']);
                        $userContext .= __('Credit until date') . ': ' . $expireLabel . PHP_EOL;
                    }
                }

                $userContext .= __('Account status') . ': ' . $stateLabel . PHP_EOL;
                $ispContextInfo = $ubillingConfig->getAlterParam('HIVE_ISP_INFO', '');
                $ispContextInfo = ubRouting::filters($ispContextInfo, 'safe');
                if (!empty($ispContextInfo)) {
                    $userContext .= __('Here some information about ISP') . ': ' . $ispContextInfo . PHP_EOL;
                }


                $dialog[] = array(
                    'role' => 'system',
                    'content' => $userContext
                );
            }
        }

        $lastUserPrompt = $ticketdata['text'];
        $dialog[] = array(
            'role' => 'user',
            'content' => $lastUserPrompt
        );
    


    if (!empty($ticketreplies)) {
        foreach ($ticketreplies as $io => $eachreply) {
            if ($eachreply['admin']) {
                $dialog[] = array(
                    'role' => 'assistant',
                    'content' => $eachreply['text']
                );
            } else {
                $lastUserPrompt = $eachreply['text'];
                $dialog[] = array(
                    'role' => 'user',
                    'content' => $eachreply['text']
                );
            }
        }
    }

    //replies list block
    if (!ubRouting::checkGet('editreply') and !empty($ticketreplies) and !$ticketdata['status']) {
        $ticketRepliesZen=new ZenFlow('ticketreplies_'.$ticketid, web_TicketReplies($ticketid),3000);
        $ticketRepliesZen->setSoundOnChange('modules/jsc/sounds/message.mp3');
        $result .= $ticketRepliesZen->render();
    } else {
        $result .= web_TicketReplies($ticketid, $ticketreplies);
    }
    

    // Add AI chat button and functionality
    if (!empty($userData)) {
        if ($ticketdata['status'] == 0) {
            if (sizeof($dialog) == 1) {
                $dialog = array();
            }

            $aiDialogCallback = array(
                'prompt' => $lastUserPrompt,
                'dialog' => $dialog,
            );

            $aiDialogCallback = json_encode($aiDialogCallback);
            $result .= web_TicketAIChatControls($aiDialogCallback);
        }
    }

    //reply form and previous tickets
    $allprevious = zb_TicketsGetAllByUser($ticketdata['from']);
    $previoustickets = '';
    if (!empty($allprevious)) {
        $previoustickets = wf_tag('h2') . __('All tickets by this user') . wf_tag('h2', true);
        foreach ($allprevious as $io => $eachprevious) {
            $tablecells = wf_TableCell($eachprevious['date']);
            $tablecells .= wf_TableCell(web_bool_led($eachprevious['status']));
            $prevaction = wf_Link('?module=ticketing&showticket=' . $eachprevious['id'], wf_img_sized('skins/icon_search_small.gif', '', '12') . ' ' . __('Show'), false, 'ubButton');
            $tablecells .= wf_TableCell($prevaction);
            $tablerows = wf_TableRow($tablecells, 'row3');
            $previoustickets .= wf_TableBody($tablerows, '100%', '0');
        }
    }

    if ($claptrapActive and !empty($claptrapChatId) and $clapTrapEnableFlag) {
        $ticketReplyForm = web_TicketReplyForm($ticketid, $clapTrapEnableFlag, $claptrapChatId);
    } else {
        $ticketReplyForm = web_TicketReplyForm($ticketid);
    }

    $tablecells = wf_TableCell($ticketReplyForm, '50%', '', 'valign="top"');
    $tablecells .= wf_TableCell($previoustickets, '50%', '', 'valign="top"');
    $tablerows = wf_TableRow($tablecells);

    $result .= wf_TableBody($tablerows, '100%', '0', 'glamour');
    $result .= wf_CleanDiv();
    } else {
      $messages = new UbillingMessageHelper();
      $result .= $messages->getStyledMessage(__('Something went wrong').': '.__('Not existing ticket'), 'error');
    }
    return ($result);
}


/**
 * Renders tickets calendar view widget
 * 
 * @return string
 */
function web_TicketsCalendar() {
    $query = "SELECT * from `ticketing` WHERE `to` IS NULL AND `replyid` IS NULL ORDER BY `date` ASC";
    $all = simple_queryall($query);
    $allAddress = zb_AddressGetFulladdresslistCached();
    $result = '';
    $calendarData = '';
    if (!empty($all)) {

        foreach ($all as $io => $each) {
            $timestamp = strtotime($each['date']);
            $date = date("Y, n-1, j", $timestamp);
            $rawTime = date("H:i:s", $timestamp);
            if ($each['status'] == 0) {
                $coloring = "className : 'undone',";
            } else {
                $coloring = '';
            }
            $calendarData .= "
                      {
                        title: '" . $rawTime . ' ' . @$allAddress[$each['from']] . "',
                        url: '?module=ticketing&showticket=" . $each['id'] . "',
                        start: new Date(" . $date . "),
                        end: new Date(" . $date . "),
                       " . $coloring . "     
                   },
                    ";
        }
    }
    $result = wf_FullCalendar($calendarData);
    return ($result);
}

/**
 * Returns previous ticket events parsed from log.
 * 
 * @param int $TicketID
 * @param bool $ReturnHTML
 * 
 * @return array|string
 */
function getTicketEvents($TicketID, $ReturnHTML = false) {
    $QResult = array();
    $HTMLStr = '';

    $tQuery = "SELECT * FROM `weblogs` WHERE `event` LIKE 'TICKET%[" . $TicketID . "]'  ORDER BY `date` DESC";
    $QResult = simple_queryall($tQuery);

    if ($ReturnHTML and !empty($QResult)) {
        $TableCells = wf_TableCell(__('ID'));
        $TableCells .= wf_TableCell(__('Date'));
        $TableCells .= wf_TableCell(__('Admin'));
        $TableCells .= wf_TableCell(__('IP'));
        $TableCells .= wf_TableCell(__('Event'));
        $TableRows = wf_TableRow($TableCells, 'row1');

        foreach ($QResult as $Rec) {
            $Event = htmlspecialchars($Rec['event']);

            $TableCells = wf_TableCell($Rec['id']);
            $TableCells .= wf_TableCell($Rec['date']);
            $TableCells .= wf_TableCell($Rec['admin']);
            $TableCells .= wf_TableCell($Rec['ip']);
            $TableCells .= wf_TableCell($Event);
            $TableRows .= wf_TableRow($TableCells, 'row3');
        }

        $HTMLStr .= wf_TableBody($TableRows, '100%', 0, 'sortable');
    }

    return (($ReturnHTML) ? $HTMLStr : $QResult);
}
