<?php
set_time_limit(300);
// error reporting again
ini_set('display_errors',1);
error_reporting(E_ALL);
date_default_timezone_set('America/Phoenix');

require_once('inc/vip.common.php'); // needed to load domain-specific config vars for database connection etc
require_once('inc/common.db.php');
require_once('inc/common.util.php');
require_once 'controlcenter/inc.siteoption_import.lib.php';
require_once('controlcenter/inc/admin.util.php'); // needed to load domain-specific config vars for database connection etc
require_once('controlcenter/inc/admin.triggeraction.php'); // for loggging

use \Common\DB;
use \Common\Util;

$output_mode = "email";
$log_output = "";
$update_count = 0;
$starttime = time();

$ret = array();

// from OAuth2 examples, docs, and source here -  https://github.com/bshaffer/oauth2-server-php/tree/v0.9
require_once('inc/external/oauth2-server/src/OAuth2/Autoloader.php');
OAuth2_Autoloader::register();

$storage = new OAuth2_Storage_Pdo(DB::dbh());
$server = new OAuth2_Server($storage);
//debug("operation=" . Request('op') . "\r\n");
$operation = Request('op');
$format = Request('format','json');

function debug($msg) {
  //file_put_contents("/Users/Greg/Downloads/API_log.txt", $msg, FILE_APPEND);
}

//debug("API REQUEST=" . print_r($_REQUEST,TRUE));
$operation = Request('op');

if ($operation=="authtoken") {
//debug("AUTHTOKEN REQUESTED\r\n");
// AUTHTOKEN

    $server->addGrantType(new OAuth2_GrantType_ClientCredentials($storage)); // or any grant type you like!
    $request = OAuth2_Request::createFromGlobals();
    $response = new OAuth2_Response();
    $server->handleTokenRequest($request, $response);//->send();

    // save vipamdin logged-in user pass-thru for future API filtering
//debug("Request=" . print_r($_REQUEST, TRUE));
    $tmp  = $response->getParameters();
//debug("Request=" . print_r($_REQUEST, TRUE));
//debug("OAuth returns=" . print_r($tmp,TRUE));
    strip_numeric_keys($tmp);
//debug("stripped=" . print_r($tmp,TRUE));


    if (array_key_exists('access_token',$tmp) && Request('client_id')=='vipadminapiuser@contradovip.com' && Request('session_user_id')!='') {
        // save local UserID in new oauth_vipadmin_session.api_session_user_id field -  the end-user /vipadmin/ session is not the same and variables do not automagically get copied
        $sql = "INSERT INTO oauth_vipadmin_session SET access_token='" . addslashes($tmp['access_token']) . "', api_session_user_id=" . (int)Request('session_user_id');
//debug("sql=" . $sql . "\r\n");
        $db = DB::dbh();
    	$stmt = $db->prepare($sql);
    	$stmt->execute();
    }
    if ($format=='xml') $tmp2 = xml_encode($tmp);
    else $tmp2 = json_encode($tmp);
    echo $tmp2;
//debug("tmp2=" . $tmp2 . "\r\n");

} else {


//debug("validating AUTH TOKEN (" . Request('access_token','') . ")\r\n");
    // verify caller supplied a valid authtoken with the request
    if (!$server->verifyResourceRequest(OAuth2_Request::createFromGlobals(), new OAuth2_Response())) {
        $server->getResponse()->send();
        die(-1);
//debug("INVALID AUTH TOKEN (" . Request('access_token','') . ")\r\n");
//die("INVALID AUTH TOKEN (" . print_r($_REQUEST,TRUE) . ")");
    }
//debug("IS VALID\r\n");
    // get oauth_client and corresponding User from token - revalidates access token on every API call
    $db = DB::dbh();
    if (defined('DB_API_REQUIRES_COLLATE') && DB_API_REQUIRES_COLLATE=="TRUE") $collate = " COLLATE utf8_general_ci";
    else $collate = "";
    $headers = getAuthorizationHeader();
    $sql = "SELECT User.*, oauth_vipadmin_session.api_session_user_id
                , GROUP_CONCAT(DISTINCT `Client`.ClientID) AS ClientID
                , GROUP_CONCAT(DISTINCT `Client`.ClientDisplayName) AS ClientDisplayName
                , GROUP_CONCAT(IF(UserProjectPermissions.Access=1,UserProjectPermissions.ProjectID,NULL)) AS PermsAccess
                , GROUP_CONCAT(IF(UserProjectPermissions.Notification=1,UserProjectPermissions.ProjectID,NULL)) AS PermsNotifications
                , UserType.UserTypeID, UserType.UserTypeCode
                , IF(ClientPromoEndDate IS NOT NULL AND ClientPromoEndDate>CURDATE() AND ClientPromoLevel!='Disabled', ClientPromoLevel, ClientSubscriptionLevel) AS ClientSubscriptionLevel
            FROM oauth_access_tokens
            INNER JOIN oauth_clients ON oauth_clients.client_id=oauth_access_tokens.client_id " . $collate . "
            LEFT JOIN oauth_vipadmin_session ON oauth_vipadmin_session.access_token=oauth_access_tokens.access_token " . $collate . "
            INNER JOIN User ON User.UserEmail=oauth_clients.client_id " . $collate . "
            INNER JOIN UserType ON UserType.UserTypeID=User.UserTypeID
            LEFT JOIN UserProjectPermissions ON UserProjectPermissions.UserID=User.UserID
            LEFT JOIN ClientUser ON ClientUser.UserID=User.UserID
            LEFT JOIN `Client` ON ClientUser.ClientID=Client.ClientID
            WHERE oauth_access_tokens.access_token='" . addslashes(str_ireplace("Bearer ", "", $headers)) . "'
            GROUP BY User.UserID";
            // NOTE 'COLLATE utf8_general_ci' required after 'oauth_clients.client_id ' above on some servers
	if (DB::fetchAll($users, $sql, array()) === 0) {
		if (count($users) === 1) {
		    $user = $users[0]; // should only ever be one record, even if User is tied to multiple clients due to 'group by' and group_concat in query - ClientID may be a CSV
		    if ($user['UserTypeCode']=='superadmin' || $user['UserTypeCode']=='admin' || $user['UserTypeCode']=='manager') $user['ClientSubscriptionLevel']='Platinum';
		} else {
		    // user can get an access_token not tied to ANY oauth_client by providing EMPTY username and password params in authorization request (!)
		    // however, this could also happen if we have oauth_client records that are not properly linked to a User record (via email address).
		    die('Your access token is valid but it is not linked to any account.  Please contact Contrado API support.');
		}
	} else {
	   die('Failed fetching user information.  Please contact Contrado API support.');
	}

//echo("user=" . print_r($user,TRUE));
//echo("Session User ID: " . $user['api_session_user_id']  ."\r\n");
    // effect the pass-thru of /vipadmin/ logged-in user by overwriting the internal vipadminapiuser@contradovip.com user account variables
    if ((int)$user['api_session_user_id']!=0) {
        $_SESSION['User_UserID'] = $user['api_session_user_id']; // _SESSIONS are not maintained with curl calls so we have to restore from DB every time
        $sql = "SELECT User.*, Client.ClientID
                , GROUP_CONCAT(DISTINCT Client.ClientID) AS ClientID
                , GROUP_CONCAT(DISTINCT Client.ClientDisplayName) AS ClientName
                , GROUP_CONCAT(IF(UserProjectPermissions.Access=1,UserProjectPermissions.ProjectID,NULL)) AS PermsAccess
                , GROUP_CONCAT(IF(UserProjectPermissions.Notification=1,UserProjectPermissions.ProjectID,NULL)) AS PermsNotifications
                , UserType.UserTypeID, UserType.UserTypeCode
                , IF(ClientPromoEndDate IS NOT NULL AND ClientPromoEndDate>CURDATE() AND ClientPromoLevel!='Disabled', ClientPromoLevel, ClientSubscriptionLevel) AS ClientSubscriptionLevel
            FROM User
            INNER JOIN UserType ON UserType.UserTypeID=User.UserTypeID
            LEFT JOIN UserProjectPermissions ON UserProjectPermissions.UserID=User.UserID
            LEFT JOIN ClientUser ON ClientUser.UserID=User.UserID
            LEFT JOIN Client ON ClientUser.ClientID=Client.ClientID
            WHERE User.UserID='" . $_SESSION['User_UserID']  . "'
            GROUP BY User.UserID ";
//debug("sql=" . $sql . "\r\n");
    }
    $client_id = 0;
	if (DB::fetchAll($users, $sql, array()) === 0) {
		if (count($users) === 1) {
		    $user = $users[0]; // should only ever be one record, even if User is tied to multiple clients due to 'group by' and group_concat in query
            $client_id = $user['ClientID']; //may be a CSV
		}
    }
//echo("user=" . print_r($user,TRUE));

    $ClientCredentials = AdminUtil::GetClientCredentials($client_id, "SiteOption", 'contradovip');
//echo "ClientCredentials=<pre>" . print_r($ClientCredentials, TRUE) . "</pre>";
    if (array_key_exists("ErrorEncountered", $ClientCredentials)) return $ClientCredentials;
    $fieldname_map = $ClientCredentials['FieldnameMap'];

    $sql = "SELECT * FROM Client WHERE ClientID IN(" . $client_id . ")";
//echo "sql=". $sql;
    if (DB::fetchAll($recs, $sql, array()) === 0) {
        if (count($recs)>=1) {
            $ClientRec = $recs[0];
        }
    }
//debug("user=" . print_r($user,TRUE));

	if ($user['PermsAccess']=='' && $user['UserTypeCode']=='client') {
    	$user['PermsAccess'] = '0'; // SQL syntax fixer
    }
	if ($user['PermsNotifications']=='' && $user['UserTypeCode']=='client') {
    	$user['PermsNotifications'] = '0'; // SQL syntax fixer
    }

//debug("_REQUEST=" . print_r($_REQUEST,TRUE) . "\r\n");


// SIMILAR PROGRAMMING TO SITEPLAN_STATUS_IMPORT.PHP ONLY INGESTING JSON DATA (IGNORING POSSIBLE PSV) AND ADDING SOMETHING FOR LAST TOUCHED TIMESTAMP FIELD
$fileContent = file_get_contents('php://input');

$is_json = FALSE;
$json = json_decode($fileContent, TRUE);
$output = array();
$rowcount = 0;

// setup default fieldname mapping
$mapped_fieldname = array('SitePlanID'=>'sitePlanId'
                         ,'ProjectBuilderName'=>'projectId'
                         ,'LotNum'=>'lotNumId'
                         ,'StatusID'=>'statusId'
                         ,'SelectedPlan'=>'selectedPlanId'
                         ,'SelectedElevation'=>'selectedElevation'
                         ,'SelectedOrientation'=>'selectedOrientation'
                         ,'LotPremium'=>'lotPremium'
                         ,'SalesPrice'=>'salesPrice'
                         ,'Notes'=>'notes'
                         ,'DisplayName'=>'lotDesc'
                         ,'OptionTitle1'=>'subhead'
                         ,'AvailablePlans'=>'availablePlans'
                         );

//echo "fieldname map=" . $fieldname_map . "<br/>";
// parse integration specific fieldname map if it exists
if (trim($fieldname_map)!="") {
    $map = explode('|',$fieldname_map);
    $mapped_fieldname = array();
    // replace mapped fieldnames into array
    if (count($map)>0) foreach($map AS $k=>$v) {
        $tmp = explode('=',$v);
        $key = trim($tmp[0]);
        $value = trim($tmp[1]);
        if (!array_key_exists($key, $mapped_fieldname)) $mapped_fieldname[$key] = $value;
    }
}
//echo "mapped_fieldnames=<pre>" . print_r($mapped_fieldname, TRUE) . "</pre>";
if (json_last_error()==JSON_ERROR_NONE) {
    // parse json and reformat into array as PSV would be
    $is_json = TRUE;
    $archive_extension = ".json";
    foreach($json AS $option) {
//echo "option=<pre>" . print_r($option, TRUE) . "</pre>";
        $output[$rowcount] = array();
        if (isset($option[$mapped_fieldname['SitePlanID']])) $output[$rowcount]['SitePlanID']=$option[$mapped_fieldname['SitePlanID']];
        if (isset($option[$mapped_fieldname['ProjectBuilderName']])) $output[$rowcount]['ProjectBuilderName']=$option[$mapped_fieldname['ProjectBuilderName']];
        if (isset($option[$mapped_fieldname['LotNum']])) $output[$rowcount]['LotNum']=$option[$mapped_fieldname['LotNum']];
        if (isset($option[$mapped_fieldname['StatusID']])) $output[$rowcount]['StatusID']=$option[$mapped_fieldname['StatusID']];
        if (isset($option[$mapped_fieldname['SelectedPlan']])) $output[$rowcount]['SelectedPlan']=$option[$mapped_fieldname['SelectedPlan']];
        if (isset($option[$mapped_fieldname['SelectedElevation']])) $output[$rowcount]['SelectedElevation']=$option[$mapped_fieldname['SelectedElevation']];
        if (isset($option[$mapped_fieldname['SelectedOrientation']])) $output[$rowcount]['SelectedOrientation']=$option[$mapped_fieldname['SelectedOrientation']];
        if (isset($option[$mapped_fieldname['LotPremium']])) $output[$rowcount]['LotPremium']= str_replace(array('$',','),array("",""),$option[$mapped_fieldname['LotPremium']]);
        if (isset($option[$mapped_fieldname['SalesPrice']])) $output[$rowcount]['SalesPrice']= str_replace(array('$',','),array("",""),$option[$mapped_fieldname['SalesPrice']]);
        if (isset($option[$mapped_fieldname['Notes']])) $output[$rowcount]['Notes']=$option[$mapped_fieldname['Notes']];
        if (isset($option[$mapped_fieldname['DisplayName']])) $output[$rowcount]['DisplayName']=$option[$mapped_fieldname['DisplayName']];
        if (isset($option[$mapped_fieldname['OptionTitle1']])) $output[$rowcount]['OptionTitle1']=$option[$mapped_fieldname['OptionTitle1']];
        if (isset($option[$mapped_fieldname['AvailablePlans']])) $output[$rowcount]['AvailablePlans']=$option[$mapped_fieldname['AvailablePlans']];
        $rowcount++;
    }
//echo "output=<pre>" . print_r($output, TRUE) . "</pre>";

} else {
    // error: not json
    errorOutput($output_mode, "{\"status\": \"JSON error or incorrectly formatted JSON\"}");
    die();
}

//echo "sites=<pre>" . print_r($SiteRecs, TRUE) . "</pre>";

/*
$api_params = array('access_token'=>$_SESSION['admintoken']);
if ($selected_client_id!='') $api_params['client_id'] = (int)$selected_client_id;
$api_params['json'] = urlencode(json_encode($output));
//echo "Calling API..";
$ret = call_api('import_plan_option_pricing', $api_params);
*/

// parse and import data locally instead of via the API for easier fieldmapping, reporting of exceptions, and handling of ignored exceptions
$recs = $output;

process_siteoption_recs($client_id, $recs, "email", $is_json);

//echo "log_output=" . $log_output . "<br/>";

// archive the uploaded data in case there were warnings and we need to manually re-import after fixing the unmatched site/option names
$target_dir = realpath($_SERVER["DOCUMENT_ROOT"]);
$epos = strrpos($target_dir,'/');
$target_dir = substr($target_dir, 0, $epos) . "/client_uploads";
if (!file_exists($target_dir)) mkdir($target_dir, 0777, TRUE);
$target_dir .= "/";
//echo "target_dir=" . $target_dir;
/*
$target_dir = $ClientRec['ClientPath'];
if ($target_dir=='' || stripos($target_dir, "clients/")===FALSE) $target_dir = "/clients/" . strtolower(str_replace(" ","_",$ClientRec['ClientDisplayName'])) . "/";
$target_dir = str_replace("..","", $target_dir); // safety measure
// create client path if it doesn't exist
if (!file_exists($_SERVER["DOCUMENT_ROOT"].$target_dir)) mkdir($_SERVER["DOCUMENT_ROOT"].$target_dir);
// add trailing slash to make it easier to append filenames below
$target_dir = realpath($_SERVER["DOCUMENT_ROOT"].$target_dir) . "/";
*/
// archve uploaded PSV/JSON data
$errmsg = "";
$archived_filename = $target_dir . strtolower(str_replace(" ","_",$ClientRec['ClientDisplayName'])) . "_" . 'SiteplanStatusUpdate' . date('m-d-Y-hisa') . $archive_extension;

if (!file_put_contents($archived_filename, $fileContent)) $errmsg = 'Error! Error archiving import file (' . $archived_filename . ').';

if ($errmsg!="") die($errmsg);

$endtime = time();
$elapsed = $endtime - $starttime;

$ret['Status'] = 'SUCCESS';
$ret['DataSent'] = 'STATUS.. Total Records: ' . $total_count . ', Matches: ' . $match_count . ', Updates: ' . $update_count . ', Unmatched: ' . ($total_count-$match_count);
$ret['DataReceived'] = $fileContent;
$ret['RecordCount'] = $update_count;
$ret['ErrorEncountered'] = 0;

// prefix log output with status as well so it gets included in email status update
$log_output = $ret['DataSent'] . "<br/>\r\n" . $log_output;

// set based on $log_output containing the word 'error' or 'not found' anywhere
if (stripos($log_output,"error")!==FALSE) $ret['ErrorEncountered'] = 1;
if (stripos($log_output,"not found")!==FALSE) $ret['ErrorEncountered'] = 1;

//echo "ret=<pre>" . print_r($ret, TRUE) . "</pre>";

// enter into integration log
$sql = "SELECT * FROM TriggerAction WHERE TriggerActionID=" . $ClientCredentials['TriggerActionID'];
if (DB::fetchAll($action, $sql, array()) === 0) {
    if (count($action) === 1) $action = $action[0];
}
TriggerAction::logTriggerAction($ClientCredentials['TriggerScheduleID'],0,0,$ret['DataSent'],$ret['DataReceived'],$ret['RecordCount'],$ret['ErrorEncountered'],$log_output,$action, $ClientCredentials['VerboseLogging'],$elapsed);
// update the LastExecutedValue
$sql = "UPDATE TriggerSchedule
        SET TriggerSchedule.TriggerScheduleLastExecuted=NOW()
        WHERE TriggerScheduleID=" . (int)$ClientCredentials['TriggerScheduleID'];
$stmt = $db->prepare($sql);
$stmt->execute();


//echo "base_plan_updated=<pre>" . print_r($sites_updated,TRUE) . "</pre>";

if (isset($ret['Status'])) {
    errorOutput($output_mode, "{\"status\": \"" . $ret['Status'] . "\"}");
} else {
    errorOutput($output_mode, "\"status\": \"JSON error or incorrectly formatted JSON\"}>");
    die();
}

if ($output_mode=="email") {
    $util = new Util();
    //    emailSend($sender, $recipients, $bcc_recipients, $subject, $body, $purpose = 0, $attachments = false)
    $util->emailSend('donotreply@thecontradogroup.com', 'craig.hall@thecontradogroup.com, gregorylee@contractweb.net', "", $ClientRec['ClientDisplayName'] . " siteplan status update results", "<HTML><HEAD></HEAD><BODY>" . $log_output . "</BODY></HTML>", 2, false);
    // also output/return error messages to the caller
    echo $log_output;
}

// NOTE THAT SITEPLAN_STATUS_IMPORT.PHP ALSO INCLUDES inc.siteoption_import.lib.php AND CALLS process_siteoption_recs(); to actually process the data (REFORMATTED INTO ARRAYS)

// THIS IS ALSO VERY SIMILAR TO WHAT INC.GOODLESHEETSAPI.PHP DOES using process_siteoption_recs to process the data
}

// helper function gets _REQUEST var if it exists, else default
function Request($fieldname, $default=NULL)
{
    if (array_key_exists($fieldname,$_REQUEST)) return $_REQUEST[$fieldname];
    else return $default;
}

function getAuthorizationHeader() {
    $headers = null;
    if (isset($_SERVER['Authorization'])) {
        $headers = trim($_SERVER["Authorization"]);
    } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) { // Nginx or FastCGI
        $headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
    } elseif (function_exists('apache_request_headers')) {
        $requestHeaders = apache_request_headers();
        // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
        $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
        if (isset($requestHeaders['Authorization'])) {
            $headers = trim($requestHeaders['Authorization']);
        }
    }
    return $headers;
}

// helper function strips out numeric field indexes from within each record
function strip_numeric_keys(&$recs)
{
    if (is_array($recs)) foreach($recs AS $count => $rec) {
        if (is_array($rec)) foreach($rec AS $k => $v) if (is_numeric($k)) unset($recs[$count][$k]);
    }
}
/*
// helper function to generate xml string from array
function xml_encode($arr)
{
    // create instance of SimpleXMLElement
    $xml_result = new SimpleXMLElement("<?xml version=\"1.0\"?><root></root>");

    // convert array to xml
    array_to_xml($arr,$xml_result);

    // return generated xml as a string
    return $xml_result->asXML();
}

// function defination to convert array to xml
function array_to_xml($arr, &$xml_result) {
    foreach($arr as $key => $value) {
        if (is_array($value)) {
            if (!is_numeric($key)) {
                $subnode = $xml_result->addChild("$key");
                array_to_xml($value, $subnode);
            } else {
                $subnode = $xml_result->addChild("item$key");
                array_to_xml($value, $subnode);
            }
        } else {
            $xml_result->addChild("$key",htmlspecialchars("$value"));
        }
    }
}

// debug helper
function pdo_sql_debug($sql,$placeholders) {
    foreach($placeholders as $k => $v){
        $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
    }
    return $sql;
}


$escapedbh = NULL;

// DB lib replacement without deprecated mysql library calls.
function escapeString($theString, $host = NULL, $user = NULL, $password = NULL, $port=NULL)
{
    global $escapedbh;
    // this function is problematic with
    return addslashes($theString);

	if (!isset($host) && isset($_SESSION['DB_HOST'])) $host = $_SESSION['DB_HOST'];
	if (!isset($host) && defined('DB_HOST')) $host = DB_HOST;
	if (!isset($host)) throw new Exception('No database host defined.');

	if (!isset($user) && isset($_SESSION['DB_USER'])) $user = $_SESSION['DB_USER'];
	if (!isset($user) && defined('DB_USER')) $user = DB_USER;
	if (!isset($user)) throw new Exception('No database user defined.');

	if (!isset($password) && isset($_SESSION['DB_PASSWORD'])) $password = $_SESSION['DB_PASSWORD'];
	if (!isset($password) && defined('DB_PASSWORD')) $password = DB_PASSWORD;
	if (!isset($password)) throw new Exception('No database password defined.');

	if (!isset($port) && isset($_SESSION['DB_PORT'])) $port = $_SESSION['DB_PORT'];
	if (!isset($port) && defined('DB_PORT')) $port = DB_PORT;

    if (is_null($escapedbh)) $escapedbh = mysqli_connect($host, $user, $password, NULL, $port)  ;
	return mysqli_real_escape_string($escapedbh, $theString);
}
*/
