<?php
/**
 * @link https://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license https://www.yiiframework.com/license/
 */

namespace yii\authclient\widgets;

use yii\authclient\clients\GoogleHybrid;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\web\View;

/**
 * GooglePlusButton renders Google+ sign-in button.
 * This widget is designed to interact with [[GoogleHybrid]].
 *
 * @see GoogleHybrid
 * @see https://developers.google.com/+/web/signin/
 *
 * @property string $callback Callback JavaScript function name. Note that the type of this property differs
 * in getter and setter. See [[getCallback()]] and [[setCallback()]] for details.
 *
 * @author Paul Klimov <klimov.paul@gmail.com>
 * @since 2.0.4
 */
class GooglePlusButton extends AuthChoiceItem
{
    /**
     * @var array button tag HTML options, which will be merged with the default ones.
     */
    public $buttonHtmlOptions = [];

    /**
     * @var string|array name of the JavaScript function, which should be used as sign-in callback.
     * If blank default one will be generated: it will redirect page to the auth action using auth result
     * as GET parameters.
     * You may pass an array configuration of the URL here, which will be used creating such
     * default callback.
     */
    private $_callback;


    /**
     * @param string|array $callback callback JavaScript function name or URL config.
     */
    public function setCallback($callback)
    {
        $this->_callback = $callback;
    }

    /**
     * @return string callback JavaScript function name.
     */
    public function getCallback()
    {
        if (empty($this->_callback)) {
            $this->_callback = $this->generateCallback();
        } elseif (is_array($this->_callback)) {
            $this->_callback = $this->generateCallback($this->_callback);
        }
        return $this->_callback;
    }

    /**
     * Initializes the widget.
     */
    public function init()
    {
        if (!($this->client instanceof GoogleHybrid)) {
            throw new InvalidConfigException('"' . $this->className() . '::client" must be instance of "' . GoogleHybrid::className() . '"');
        }
    }

    /**
     * Runs the widget.
     */
    public function run()
    {
        $this->registerClientScript();
        return $this->renderButton();
    }

    /**
     * Generates JavaScript callback function, which will be used to handle auth response.
     * @param array $url auth callback URL.
     * @return string JavaScript function name.
     */
    protected function generateCallback($url = [])
    {
        if (empty($url)) {
            $url = $this->authChoice->createClientUrl($this->client);
        } else {
            $url = Url::to($url);
        }
        if (strpos($url, '?') === false) {
            $url .= '?';
        } else {
            $url .= '&';
        }

        $callbackName = 'googleSignInCallback' . md5($this->id);
        $js = <<<JS
function $callbackName(authResult) {
    var urlParams = [];

    if (authResult['code']) {
        urlParams.push('code=' + encodeURIComponent(authResult['code']));
    } else if (authResult['error']) {
        if (authResult['error'] == 'immediate_failed') {
            return;
        }
        urlParams.push('error=' + encodeURIComponent(authResult['error']));
        urlParams.push('error_description=' + encodeURIComponent(authResult['error_description']));
    } else {
        for (var propName in authResult) {
            var propValue = authResult[propName];
            if (typeof propValue != 'object') {
                urlParams.push(encodeURIComponent(propName) + '=' + encodeURIComponent(propValue));
            }
        }
    }

    window.location = '$url' + urlParams.join('&');
}
JS;
        $this->view->registerJs($js, View::POS_END, __CLASS__ . '#' . $this->id);

        return $callbackName;
    }

    /**
     * Registers necessary JavaScript.
     */
    protected function registerClientScript()
    {
        $js = <<<JS
(function() {
    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/client:plusone.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
JS;
        $this->view->registerJs($js, View::POS_END, __CLASS__);
    }

    /**
     * Renders sign-in button.
     * @return string button HTML.
     */
    protected function renderButton()
    {
        $buttonHtmlOptions = array_merge(
            [
                'class' => 'g-signin',
                'data-callback' => $this->getCallback(),
                'data-clientid' => $this->client->clientId,
                'data-cookiepolicy' => 'single_host_origin',
                'data-requestvisibleactions' => null,
                'data-scope' => $this->client->scope,
                'data-accesstype' => 'offline',
                'data-width' => 'iconOnly',
            ],
            $this->buttonHtmlOptions
        );
        return Html::tag('span', Html::tag('span', '', $buttonHtmlOptions), ['id' => 'signinButton']);
    }
}