<?php
/**
 * Comment API
 * 
 * Provides methods related to API module
 * 
 * @package linea21\modules\api
 * @author $Author$ - Simon Georget <simon@linea21.com>
 * @version $Id$ 
 * @access public
 * @license http://opensource.org/licenses/gpl-3.0.html
 * Content Management
 */

class API {

	private $params = array();

	protected $dispatcher = null;
    private $allowedMethods = ['indicator' => ['list', 'info', 'info-full', 'info-values', 'values', 'last-value'],
        // 'project' => ['list', 'info'],
        'level' => ['list'],
    ];
    private $permissions = [];



	public function __construct($getMethods = false) 	{
		$this->dispatcher = $GLOBALS['dispatcher'];

        // if user is going to use API we check permissions
        if(!$getMethods) {

            $uri = parse_url($_SERVER['REQUEST_URI']); // we get data from URI

            if(!isset($uri['query'])) $this->renderDocumentation();

            parse_str($uri['query'], $this->params); // we retrieve query string parameters

            if(isset($this->params['id']) && !is_int((int) $this->params['id'])) die('no :-(');

            $this->isAllowed();
        }

	}

    public function getallowedMethods(): array
    {
        return $this->allowedMethods;
    }

	public function __call($method, $arguments)
	{
		$event = $this->dispatcher->notifyUntil(new sfEvent($this, 'api.extensible_function', array(
				'method'    => $method,
				'arguments' => $arguments
		)));
		if (!$event->isProcessed())
		{
			throw new Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
		}

		return $event->getReturnValue();
	}


    /**
     * Remove '-' in action
     * @param $actions
     * @return array
     */
    private function formatAllowedAction($actions) {

        $tmp = [];

        foreach ($actions as $action) {
            array_push($tmp, str_replace('-', '', $action));
        }

        return $tmp;
    }


    /**
     * @return true|void
     */
    private function isAllowed() {

        // Notify the beginning of the current method
        $this->dispatcher->notify(new sfEvent($this, 'api.is_allowed', $this->params));

        // if API_PROVIDE is disabled we return an error
        if(!defined('API_PROVIDE') || API_PROVIDE === 0) $this->error_output(_t('api', 'is_disabled'));

        // if URL syntax is incorrect, we return an error
        if(!isset($this->params['m']) || !isset($this->params['a']) || !in_array(($this->params['m']), array_keys($this->allowedMethods)))  $this->error_output(_t('api', '404_error'));

        // we replace '-' by nothing
        $this->params['a'] = str_replace('-', '', $this->params['a']);

        // if action is not allowed, we return an error
        if(!in_array($this->params['a'], $this->formatAllowedAction($this->allowedMethods[$this->params['m']]))) {
            $this->error_output(sprintf(_t('api', 'method_not_defined'), $this->params['a']));
        }

        // check if free access is granted for all
        if(defined('API_PROVIDE') && API_PROVIDE === "full-access") return true; // free access is granted for all

        // finally we check permissions from config file if needed
        if(defined('API_PROVIDE') && API_PROVIDE === "limited-access") {

            $filepaths = glob(THEME_PUBLIC_PATH.'override/api/api-permissions*.json');

            if($filepaths === false) {
                $this->error_output(_t('api', 'not_allowed'));
                logfile(LOG_MAINFILE, array('[api] ERROR', 'configuration file is missing in '. THEME_PUBLIC_PATH.'override/api/ folder', 'API_PROVIDE='. API_PROVIDE));
            }
            if(basename($filepaths[0]) == 'api-permissions.json') logfile(LOG_MAINFILE, array('[api] SECURITY WARNING', 'naming api-permissions.json without random chars is very unsafe ! Expected syntax : api-permissions-[A-Aa-z0-9].json', 'API_PROVIDE='. API_PROVIDE));

            // we finally get settings from file
            $json = file_get_contents($filepaths[0]);
            $this->permissions = json_decode($json, true); // we open the configuration file


            // unable to find token
            if(!isset($this->permissions[$this->params['token']])) {
                $this->error_output(_t('api', 'not_allowed'));
            } else {
                // do the user has permissions on following module / action ?
                $userPermissions = $this->formatAllowedAction( $this->permissions[$this->params['token']]['permissions'][$this->params['m']]);
                if(!in_array($this->params['a'], $userPermissions) && !in_array('all', $userPermissions)) $this->error_output(_t('api', 'not_allowed'));
                else return true;
            }

        }

	}

    /**
     * @param $msg
     * @param $errorcode
     * @return void
     */
    private function renderDocumentation() {

        $doc = '';
        $header  = '<h1 class="pll">API Documentation</h1>'. PHP_EOL;
        $header .= '<div class="api-token pll">'. PHP_EOL;
        if(API_PROVIDE == 'limited-access') $header .= '<label for="api-token">Token : </label><input name="api-token" type="text" value="" />'. PHP_EOL;
        $header .= '<label for="api-id">ID : </label><input name="api-id" type="text" value="1" maxlength="5" />'. PHP_EOL;
        $header .= '</div>'. PHP_EOL;
        if(!defined('API_PROVIDE') || API_PROVIDE == 0) $doc .= '<div class="error">'._t('api', 'is_disabled').'</div>';

        foreach ($this->allowedMethods as $module => $v) {
            $doc .= '<h2 class="api-module">'.$module.'</h2>'. PHP_EOL;
            // $doc .= '<p class="api-module-desc">'.$module.' description</p>'. PHP_EOL;

            foreach($v as $a) {
                $link = SITE_ROOT_URL.'api/index.php?m='.$module.'&a='.$a;
                if($a != 'list') $link .= '&id=1';
                if(API_PROVIDE == 'limited-access') $link .= '&token=linea21-token'. PHP_EOL;
                $doc .= '<div class="flex-container">';
                $doc .= '<div class="w25 prm"><h3 class="api-action">'.$module . ' / ' .$a.'</h3></div>';
                // $doc .= '<p class="api-action-desc">'.$module . ' / ' .  $a.' description</p>'. PHP_EOL;
                $doc .= '<div class="fluid-item prm api-get-cont"><p><a href="'.$link.'" class="api-get">'.$link.'</a></p></div>'. PHP_EOL;
                $doc .= '</div>'. PHP_EOL;

            }
        }

        $doc = $header. '<div class="flex-container"><div class="w65 pal">'. $doc . '</div><div class="item-fluid" id="api-result"><pre></pre></div></div>';
        echo generateHTMLPage($doc, 'public', array('css/knacss/css/knacss.css', 'css/style.css'), array('lib/js/functions.js'));
        exit;
    }

    /**
     * @param $msg
     * @param $errorcode
     * @return void
     */
    private function error_output($msg, $errorcode = 1) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['error' => $errorcode, 'message' => $msg], JSON_PRETTY_PRINT);
        exit;
    }

    /**
     * @param $data
     * @return void
     */
    private function render_data($data) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK);
        exit;
    }

    /**
     * @return void
     */
    public function serveData() {

        global $sql_object;

		// Notify the end of the current method
		$this->dispatcher->notify(new sfEvent($this, 'api.serveData'));

        /**
        print_r($this->params);
        echo "<hr>";
        echo API_PROVIDE;
        echo "<hr>";
         * */

        if($_SERVER['REQUEST_METHOD'] ==  'GET') {
            if($this->params['m'] == 'indicator') {
                $obj = new sdi();
            }
            if($this->params['m'] == 'project') {
                $obj = new project();
            }
            if($this->params['m'] == 'level') {
                $obj = new level();
            }

            $classMethod = 'api_'. $this->params['a'];

            if(!isset($this->params['id'])) $this->params['id'] = null;

            $status = 'default';
            if(defined('API_PROVIDE') && API_PROVIDE == 'limited-access') $status = $this->permissions[$this->params['token']]['permissions']['status'];

            $data = $obj->{$classMethod}($sql_object, $this->params['id'], $status); // we call wanted method
            logfile(LOG_MAINFILE, array('[api] serveData() call', 'API_PROVIDE='. API_PROVIDE, "referer=". get_referer(), "params=" . implode('/', $this->params)));
            $this->render_data($data);

        }
	}

}

?>