<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package clearFusionCMS
 * @copyright Copyright (c) 2016 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * cfFacebook Class
 *
 * Class to implement common Facebook integration functionality.
 *
 * @package clearFusionCMS
 */
class cfFacebook {

	/**
	 * The API Version being used.
	 */
	const APIVER = 'v2.8';

	/**
	 * Include the Facebook SDK into the page.
	 *
	 * @param flBufferCode $codebuffer The output code buffer to add the SDK to.
	 * @throws clearFusionCMSException
	 */
	static function incudeSDK($codebuffer) {
		static $included = false;

		// Only include once
		if (!$included) {
			$included = true;
			$codebuffer->bufferBodyJS("
	window.fbAsyncInit = function() {
		FB.init({
			appId      : '" . cfGetApiKey::facebookAppID() . "',
		    cookie     : true, 
			xfbml      : true,
			version    : '" . self::APIVER . "'
		});
	};

	(function(d, s, id){
		var js, fjs = d.getElementsByTagName(s)[0];
		if (d.getElementById(id)) {return;}
		js = d.createElement(s); js.id = id;
		js.src = \"//connect.facebook.net/en_US/sdk.js\";
		fjs.parentNode.insertBefore(js, fjs);
	}(document, 'script', 'facebook-jssdk'));
");
		}
	}

	/**
	 * Create a facebook connect button and JavaScript.
	 *
	 * @param cfTemplateEngine $tplEngine The template engine.
	 * @param string $onConnect The URI call via AJAX on connect.
	 * @param string $scope The scope of permissions to request
	 * @param string $label Optional label for the login button or null.
	 * @return string The login label html
	 */
	static function getConnect($tplEngine, $onConnect, $scope = 'public_profile,email', $label = null) {
		static $first = true;

		if($first) {
			$first = false;

			// Include Facebook API
			cfFacebook::incudeSDK($tplEngine->getCodeBuffer());

			// Test for email scope
			$emailScope = strpos($scope, 'email') !== false;

			$tplEngine->getCodeBuffer()
				->bufferBodyJS('system/app/js/cfFacebook.js')
				->bufferBodyJS("
cfFacebookConnect('.login-with-facebook', '$scope', '$onConnect', '" . ($emailScope ? __('You need to grant us permission to access and use your email address.', 'cfBackend') : '') . "');
");
		}

		return '<span class="button primary login-with-facebook">' . ($label ? $label : __('<i class="fa fa-facebook"></i> Sign in with Facebook', 'cfBackend')) . '</span>';
	}

	/**
	 * Get the information for a user.
	 *
	 * @param string $token The auth token to use.
	 * @return cfFacebookUser|null The user information or null.
	 */
	static function getUserInfo($token) {
		$client = new flHttpClient('https://graph.facebook.com/' . self::APIVER . '/me?fields=id%2Cname%2Cemail&access_token=' . $token);
		$client->sendRequest();

		$r = null;
		if ($client->getResponseCode() == 200) {
			$d = json_decode($client->getBody(), true);
			$r = new cfFacebookUser();
			$r->id = $d['id'];
			$r->name = $d['name'];
			$r->email = $d['email'];
		}

		return $r;
	}

	/**
	 * Exchange a short lived token for a long lived token.
	 *
	 * @param string $token The short lived token to exchange.
	 * @return cfFacebookToken|null The token.
	 */
	static function exchangeForLLToken($token) {
		// Convert short lived token to long lived
		$client = new flHttpClient('https://graph.facebook.com/' . self::APIVER . '/oauth/access_token?grant_type=fb_exchange_token&client_id=' . cfGetApiKey::facebookAppID() . '&client_secret=' . cfGetApiKey::facebookAppSecret() . '&fb_exchange_token=' . $token);
		$client->sendRequest();

		$r = null;
		if($client->getResponseCode() == 200) {
			$d = json_decode($client->getBody(), true);
			if(!isset($d['expires_in']))
				$d['expires_in'] = 14400;

			$r = new cfFacebookToken();
			$r->token = $d['access_token'];
			$r->lifespan = $d['expires_in'];
			$r->expires = time() + $d['expires_in'];
		}

		return $r;
	}

	/**
	 * Get the pages available to a user account.
	 *
	 * @param string $userID ID of the user.
	 * @param string $token The access token.
	 * @return array The list of page data.
	 */
	static function getPages($userID, $token) {
		$client = new flHttpClient('https://graph.facebook.com/' . self::APIVER . '/' . $userID . '/accounts?access_token=' . $token);
		$client->sendRequest();

		$r = null;
		if($client->getResponseCode() == 200) {
			$d = json_decode($client->getBody(), true);
			if(isset($d['data'])) {
				foreach($d['data'] as $itm) {
					$r[$itm['id']] = $itm;
				}
			}
		}

		return $r;
	}

	/**
	 * Post an instant article.
	 *
	 * @param string $pageID The ID of the page.
	 * @param string $token The access token.
	 * @param string $article The HTML article data.
	 * @param bool true if article should be published; else false.
	 * @param bool $devMode true if in development mode; else false.
	 * @return string Import status ID if article posted; else false.
	 */
	static function postInstantArticles($pageID, $token, $article, $published, $devMode = false) {
		$client = new flHttpClient('https://graph.facebook.com/' . self::APIVER . '/' . $pageID . '/instant_articles');
		$client->setPostData([
			'access_token' => $token,
			'development_mode' => $devMode,
			'published' => $devMode ? false : $published, // Force to false if in dev mode
			'html_source' => $article
		]);
		$client->sendRequest('POST');

		if($client->getResponseCode() == 200) {
			$d = json_decode($client->getBody(), true);
			return $d['id'];
		}

		return false;
	}

	/**
	 * Get the import status of an article.
	 *
	 * @param string $importID The import ID of the article.
	 * @param string $token The access token.
	 * @return array The status information or false if not available.
	 */
	static function getArticleImportStatus($importID, $token) {
		$client = new flHttpClient('https://graph.facebook.com/' . $importID . '?fields=status,errors&access_token=' . $token);
		$client->sendRequest();
		if($client->getResponseCode() == 200) {
			$d = json_decode($client->getBody(), true);
			return $d;
		}
		return false;
	}
}
