<?php

/**
 * @am_plugin_api 6.0
*/
class Am_Plugin_Telegram extends Am_Plugin
{
    const PLUGIN_STATUS = self::STATUS_BETA;
    const PLUGIN_COMM = self::COMM_COMMERCIAL;

    const DATA_USER_ID = 'telegram-user-id';
    const DATA_USER_NAME = 'telegram-username';

    protected $_configPrefix = 'misc.';

    function _initSetupForm(\Am_Form_Setup $form)
    {
        $form->addText('bot_login')->setLabel(___('Telegram Bot login'));
        $form->addText('bot_token', ['class' => 'am-el-wide'])->setLabel(___('Telegram bot key'))
            ->addRule('callback2', '', function ($val) {
                try {
                    $resp = $this->apiRequest('getMe', null, $val);
                } catch (Exception $ex) {
                    return ___('Token is not correct. Unable to get info about bot');
                }

                if (!@$resp['ok']) {
                    return ___('Token is not correct. Unable to get info about bot');
                }
            });
        $form->addText('channel')->setLabel(___("Telegram Channel ID\n".
            "Specify channel id here only if you want to add all active users to this channel regardless of exact product, " .
            "otherwise you need to set channel id within settings of corresponding product"));

        $form->addTextArea("message", ['class' => 'am-el-wide', 'rows' => 5])->setLabel(___('Invite Message
        In order to invite user into channel plugin will send message to user directly
        you can use these placeholders in message: %link% - invoice link, %user.% any user variable'));
        $form->setDefault('message', "Hi, %user.name_f%, please accept invitation to our private channel: %link%");

        $form->addTextArea('expire_message', ['class' => 'am-el-wide', 'rows' => 5])->setLabel(___('Expiration Notification
        User will be notified about product expiration 3 days before expire,
        In template you can use %user.% and %product.% placeholders'));
        $form->setDefault('expire_message', 'Hi, %user.name_f% your subscription to "%product.title%" will expire in 3 day, remember to renew your subscription.');

        $form->addAdvCheckbox('debug')->setLabel('Log Debug Info');
    }

    function isConfigured()
    {
        return $this->getConfig('bot_login') && $this->getConfig('bot_token');
    }

    function onInitFinished()
    {
        $this->getDi()->productTable->customFields()->add(new Am_CustomFieldText('telegram_channel',
            'Telegram Channel'));

        $url = $this->getDi()->url('misc/telegram/auth');

        if($this->getDi()->auth->getUser() && $this->getAllowedChannelsForUser($this->getDi()->auth->getUser())) {
            $this->getDi()->blocks->add(
                'member/main/right',
                new Am_Block_Base(___('Link Telegram Account'), 'telegram_account', $this, function () use ($url)
                {
                    return <<<WIDGET
<script async src="https://telegram.org/js/telegram-widget.js?5"
data-telegram-login="{$this->getConfig('bot_login')}"
data-size="medium"
data-request-access="write"
data-auth-url="{$url}"
></script>
<script type="text/javascript">
function onTelegramAuth(user) {
  alert('Logged in as ' + user.first_name + ' ' + user.last_name + ' (' + user.id + (user.username ? ', @' + user.username : '') + ')');
}
</script>

WIDGET;
                }),
                200);
        }
    }

    function directAction($request, $response, $invokeArgs)
    {
        $this->getDi()->auth->requireLogin();

        if ($request->getActionName() == 'auth') {
            // Check hash
            $hash = $request->getParam('hash');

            if (empty($hash)) {
                throw new Am_Exception_InternalError('Hash parameter is missing in request');
            }

            $data = $_GET;
            unset($data['hash']);
            $data_arr = [];
            foreach ($data as $key => $value) {
                $data_arr[] = $key . '=' . $value;
            }
            sort($data_arr);
            $data_str = implode("\n", $data_arr);

            $secret = hash('sha256', $this->getConfig('bot_token'), true);
            if (hash_hmac('sha256', $data_str, $secret) != $hash) {
                throw new Am_Exception_InternalError('Security check failed!');
            }

            $user = $this->getDi()->auth->getUser();
            $user->data()
                ->set(self::DATA_USER_ID, $request->getParam('id'))
                ->set(self::DATA_USER_NAME, $request->getParam('username'))
                ->update();

            $this->addUserToChannels($user, $this->getAllowedChannelsForUser($user));

            return $response->setRedirect($this->getDi()->url('member'));
        } else {
            return parent::directAction($request, $response, $invokeArgs);
        }
    }

    function getAllowedChannelsForUser(User $user)
    {
        $channels = [];
        if (!$user->isActive()) {
            return;
        }

        if ($this->getConfig('channel')) {
            $channels[] = $this->getConfig('channel');
        }

        foreach ($user->getActiveProducts() as $product) {
            if ($product->data()->get('telegram_channel')) {
                $_ = explode(" ", $product->data()->get('telegram_channel'));
                $channels = array_merge($channels, $_);
            }
        }
        return $channels;
    }

    function apiRequest($method, $data = [], $token = null)
    {
        $token = $token ?: $this->getConfig('bot_token');
        $req = new Am_HttpRequest($url = "https://api.telegram.org/bot{$token}/{$method}");
        if (!empty($data)) {
            $req->setMethod(Am_HttpRequest::METHOD_POST);
            $req->setHeader('Content-type', 'application/json');
            $req->setBody(json_encode($data));
        } else {
            $req->setMethod(Am_HttpRequest::METHOD_GET);
        }

        $resp = $req->send();
        if ($this->getConfig('debug'))
        {
            $l = $this->getDi()->invoiceLogRecord;
            $l->paysys_id = $this->getId();
            $l->title = $method;
            $l->add($req);
            $l->add($resp);
        }
        return json_decode($resp->getBody(), true);
    }

    function getInviteLink($channel_id)
    {
        $res = $this->apiRequest('getChat', ['chat_id' => $channel_id]);

        if (!$res['ok']) {
            return null;
        }

        if (empty($res['result']['invite_link'])) {
            // Link is empty export it;
            $invite_link = $this->apiRequest('exportChatInviteLink', ['chat_id' => $channel_id]);
            return $invite_link['result'];
        }
        return $res['result']['invite_link'];
    }

    function onSubscriptionAdded(Am_Event_SubscriptionAdded $event)
    {
        if ($telegram_user_id = $event->getUser()->data()->get(self::DATA_USER_ID)) {
            $channels = [];

            if ($channel = $event->getProduct()->data()->get('telegram_channel')) {
                $channels  =  explode(" ", $channel);

            }

            if ($channel = $this->getConfig('channel')) {
                $channels[] = $channel;
            }

            if (!empty($channels)) {
                $this->addUserToChannels($event->getUser(), $channels);
            }
        }
    }

    function addUserToChannels(User $user, array $channels)
    {
        $telegram_user_id = $user->data()->get(self::DATA_USER_ID);

        foreach ($channels as $channel) {
            $res = $this->apiRequest('getChatMember', ['chat_id' => $channel, 'user_id' => $telegram_user_id]);

            if ($res['ok'] && in_array($res['result']['status'], ['member', 'administrator', 'creator'])) {
                continue;
            }

            if (in_array($res['result']['status'], ['restricted', 'kicked'])) {
                $this->apiRequest('unbanChatMember', ['chat_id' => $channel, 'user_id' => $telegram_user_id]);
            }

            $invite_link = $this->getInviteLink($channel);
            if ($invite_link) {
                $t = new Am_SimpleTemplate();
                $t->assign('user', $user);
                $t->assign('link', $invite_link);
                $message = $t->render($this->getConfig('message'));

                $res = $this->apiRequest('sendMessage', ['chat_id' => $telegram_user_id, 'text' => $message]);
                if (!$res['ok']) {
                    $this->logDebug('Unable to send message to user. Got: ' . print_r($res, true));
                }
            }
        }
    }

    function onSubscriptionDeleted(Am_Event_SubscriptionDeleted $event)
    {
        if ($telegram_user_id = $event->getUser()->data()->get(self::DATA_USER_ID)) {

            $channels = [];

            if ($channel = $event->getProduct()->data()->get('telegram_channel')) {
                $channels = explode(" ", $channel);
            }

            if (!$event->getUser()->isActive() && $this->getConfig('channel')) {
                $channels[] = $this->getConfig('channel');
            }

            foreach ($channels as $channel) {
                $res = $this->apiRequest('kickChatMember', ['chat_id' => $channel, 'user_id' => $telegram_user_id]);
            }
        }
    }

    function sendExpires()
    {
        $date = date('Y-m-d', strtotime('+3 days', $this->getDi()->time));

        $q = $this->getDi()->accessTable->queryExpirations([$date]);

        $sent = []; // user_id => array('tpl_id')

        while ($row = $this->getDi()->db->fetchRow($q))
        {
            $user = $this->getDi()->userTable->createRecord($row);
            $product = $this->getDi()->productTable->load($row['_product_id']);

            if (!empty($sent[$user->user_id])) continue;

            if ($telegram_user_id = $user->data()->get(self::DATA_USER_ID)) {

                if ($channel = $product->data()->get('telegram_channel')) {
                    $t = new Am_SimpleTemplate();
                    $t->assign('user', $user);
                    $t->assign('product', $product);
                    $message = $t->render($this->getConfig('expire_message'));
                    $this->apiRequest('sendMessage', ['chat_id' => $telegram_user_id, 'text' => $message]);
                }
            }

            $sent[$user->user_id] = true;
        }
    }

    function onDaily(Am_Event $e)
    {
        $this->sendExpires();
    }
}