<?php declare(strict_types=1);

/** @var User $AUTH_USER */

 
include "./api_setup.php";

$action = strval(get('action'));
if (!$action)
{
    throw new Exception("no action");
}
$action = strtolower($action);
$format = strval(get('format'));
if (!$format){ $format = XML_FORMAT; }


$error = null;

if ($AUTH_USER->is_admin() and get('impersonate'))
{
	$impersonate_id = intval(get('impersonate'));
    $AUTH_USER = User::load($impersonate_id);
    if (!$AUTH_USER)
    {
    	throw new Exception("user not found");
    }
}

// API should fail on unknown methods

$ALLOWED_METHODS = array('suggest', 'recent', 'dates', 'get', 'delete', 'add', 'all', 'update');

if (!in_array($action,$ALLOWED_METHODS))
{
    //stats::increment("api.posts.unknown");
    header("HTTP/1.1 400 Bad Request");
    print "400 Bad Request.  This API action is not supported."; 
    exit;
}

/*
   RATE LIMITING
    
    Define per-call rate caps and refuse those who do not obey with a 429 error
*/
$THROTTLE_RULES = array( 'all'      => 60,
                        'update'    => 5,
                        'add'       => 2,
                        'recent'    => 30);

$THROTTLING_EXCEPTIONS = array('all' => array('results', 'tag'),
                               'recent' => array('tag'));


$tto = intval(getv($THROTTLE_RULES, $action));
$exceptions = getv_arr($THROTTLING_EXCEPTIONS, $action);
#assert(is_array($exceptions));
$exceptions = array();                  

$heads = apache_request_headers();
$servo = strval(getv($_SERVER, 'HTTP_USER_AGENT'));

if (preg_match('/Instapaper/', $servo) === 1)
{
    error_log("instapaper! no need to throttle...");
}
else
{
    if (($tto > 0) && throttle($AUTH_USER, $action, $tto, $exceptions))
    {
        $error = " [BLOCK]";
        header("HTTP/1.1 429 Too Many Requests");
        print "429 Too Many Requests.  Wait $tto seconds before fetching posts/$action again."; 
        //stats::increment("api.posts.throttled.$action");
        $logger->log($AUTH_USER->id . ':' . $AUTH_USER->login . " $action" . " [BLOCK]");
        exit;
    }
}

/*

    HANDLE REQUESTS

*/


switch ($action)
{
    case 'update':
        // return the last update time for a user
        api_render_header($format);
        print  api_last_update($AUTH_USER->id, $format);
        break;
    
    case 'all':
    	$count = intval(get('results')); 
    	$toread = strval(get('toread'));
    	$meta = strval(get('meta'));
    	
    	
       
        if ($count < 0) { $count = 0; }
        $toread 	= strtolower($toread) === 'yes' ? true : false;
        $show_meta  = strtolower($meta) === 'yes' ? true : false;
        $show_meta  = true; // actual del behavior
        
        $fromdt = get('fromdt');
        $todt   = get('todt');
        $start  = intval(get('start'));
        $tags = parse_tag_string(get('tag'));
        api_render_header($format);
        
        $out_format = $format === 'json' ? 'api_format_json' : 'api_format';
        if ($format != JSON_FORMAT)
        {
            $uname = cook($AUTH_USER->login);
            print "<posts user=\"$uname\">\n";
        }
        else
        {
            print '[';
        }
        export_by_chunks($AUTH_USER->id, $out_format, $count, $tags, $toread, $start, $fromdt, $todt, $show_meta);
        if ($format != 'json')
        {
            print "</posts>\n";
        }
        else
        {
            print ']';
        }
        break;
    
        
    case 'add': 
    
        // add a new bookmark, possibly overwriting a previous one
        // test required parameters
        $title = strval(scrub_title(get('description')));
        $url =   strval(scrub_url(get('url')));
        $toread = strval(get('toread'));
        
        if (strlen($url) == 0)
        {
            api_render_confirmation("missing url", $format); 
            exit;
        }
        if (strlen($title) == 0) 
        {
            api_render_confirmation("must provide title", $format);
            exit;
        }
        
        $b = new Bookmark();
        $b->url         = $url;
        $b->url_id      = get_or_create_url_id($b->url);
        $b->tags        = parse_tag_string(get('tags'));
        $b->description = scrub_description(get('extended'));
        $b->title       = $title;
        $b->toread      = strtolower($toread) == 'yes' ? true : false;
        
        # privacy - if no explicit attribute is set, make sure we
        # honor the default-private user preference
        if (get('shared'))
        {
            $b->private = strtolower(get('shared')) == 'no' ? true : false;
        }
        else
        {
            $b->private = $AUTH_USER->has_setting(DEFAULT_PRIVATE) ? true : false;
        }
        
        # PRIVACY LOCK overrides even explicit attempts to share links
        if ($AUTH_USER->has_setting(PRIVACY_LOCK))
        {
            $b->private = true;
        }
        $b->user_id = $AUTH_USER->id;
        $b->source = SRC_API;
        
        // set special source attributes on requests coming directly from 
        // instapaper or readability
        if (get('source') === 'instapaper') {$b->source = SRC_INSTAPAPER_API; }
        if (get('source') === 'readability'){$b->source = SRC_READABILITY_API; }
        
        $dt = strval(get('dt'));
        
        if ($dt)
        {
            $pd = parse_iso_date_to_timestamp($dt);
            // reset timestamps more than 10 minutes in future to server time
            if ((time() - $pd) < (10 * 60 * -1)) {  $pd = time();  }
            if (!$pd) { $pd = null; } // convert false -> null
            $b->created = date("Y-m-d H:i:s", $pd);
        }
        $old = get_bookmark_by_url($AUTH_USER->id, $b->url);
        if ($old)
        {
            if (strtolower(strval(get('replace'))) === "no")
            {
                api_render_confirmation("item already exists", $format);
                exit;
            } else {
                $b->id = $old->id;
                $b->update();
               // stats::increment('api.bookmarks.update');
                api_render_confirmation("done", $format);
                exit();
            }
        } 
        $b->store_new();     
       // stats::increment('api.bookmarks.create');
        api_render_confirmation("done", $format);
        break;
    
    case 'delete':
        $url = scrub_url(get('url'));
        $b = get_bookmark_by_url($AUTH_USER->id, $url);
        if ($b !== FAIL)
        {
            $b->delete();
         //   stats::increment('api.bookmarks.delete');
            api_render_confirmation("done", $format);
            exit();
            
        } else {
            api_render_confirmation("item not found", $format);
        }
        break;
    
    case 'get':	    
     /* options are: 
        tag (one or multiple)
        dt = date in CCYY-MM-DDThh:mm:ssZ format
        url
        hashes
        meta           */
        $url  = strval(scrub_url(get('url')));
        $tags = parse_tag_string(get('tag'));
        // TODO scrub date parameter!
        $start  = get('dt') ? parse_iso_date_to_timestamp(get('dt')) : time();
        $start = intval($start);
        $toread = strtolower(strval(get('toread'))) == 'yes' ? true : false;
        $show_meta = strtolower(strval(get('meta'))) === 'yes' ? true : false;
        
        $posts = api_get_request($AUTH_USER->id, $url, $toread, $start); // returns bookmark set
        if (count($posts) == 0)
        {
            $disp_tmstmp =  _get_most_recent_bookmark_timestamp($AUTH_USER->id, null, true); 
        } else {
            $disp_tmstmp = $posts->first()->timestamp();
            
			$lctags = array_map('strtolower', $tags->get_string_array());
			$keep = new BookmarkSet();
			foreach ($posts as $p)
			{
				// TODO make case-insensitive
				// keep only posts that contain all specified tags
				$pt = array_map('strtolower', $p->tags->get_string_array());
				if (count(array_intersect($pt, $lctags)) === count($tags))
				{ 
					$keep->add_bookmark($p);
				}
			}
			$posts = $keep;

        }
        assert($disp_tmstmp != null);
        $formatted_date = api_format_date($disp_tmstmp);
        assert($formatted_date != false);
        api_render_posts($posts, $AUTH_USER, $formatted_date , $format, $show_meta);
        break;
    
    case 'dates':
        $tag = parse_tag_string(get('tag'));
        $list = get_bookmark_counts_by_date($AUTH_USER->id, $tag->get_string_array());
        assert(count($tag) == 1);
        $arr = $tag->get_string_array();
        
        api_render_dates($list, $AUTH_USER, $arr[0], $format);
        break;
        
    case 'recent':
        $tag    = parse_tag_string(get('tag'));
        $count  = (int)get('count');
        $toread = strtolower(strval(get('toread'))) == 'yes' ? true : false;
        
        if (!$count){ $count = 15; }
        if ($count > 100) { $count = 100; }
        if ($count < 1) { $count = 1; }
        if (count($tag)){ $conditions['tag'] = $tag;  }
        $conditions['user_id'] = $AUTH_USER->id;
        $conditions['show_private'] = 1;
        $conditions['toread'] = 1;
        
        if ($toread)
        {
            $conditions['show_only_toread'] = true;
        }
        
        $posts = get_bookmarks($conditions, $count);
        $tmstmp = time();
        if (count($posts)){ $tmstmp = $posts->first()->timestamp();}
        assert($tmstmp != false);
        $formatted_date = api_format_date($tmstmp);
        assert($formatted_date != false);
        api_render_posts($posts, $AUTH_USER, $formatted_date, $format);
        break;
    
    case 'suggest':
        $url = strval(scrub_url(get('url')));
        $id = get_url_id($url);
        # TODO hack!!!
        if ($id == FAIL){
            $id = get_url_id($url ."/");
        }
       // print "URL id is $url -> $id\n";
        $popular = get_url_tags($id, 10);
        $poplist = new TagList();
        foreach ($popular as $pop)
        {
            $poplist->add_tag($pop); 
        }
        $rec = get_suggested_usertags($AUTH_USER, $id); 
        api_render_suggested($poplist, $rec, $format);
        break;
}

// LOG

$elapsed = (microtime(true) - $start_time) * 1000;
if ($action == 'all') { $action = 'all.all'; }
//stats::increment("api.posts.$action");
//stats::timing("api.posts.$action", $elapsed);     
$logger->log($AUTH_USER->id . ':' . $AUTH_USER->login . " $action" . "$error");
#$ua = $_SERVER['HTTP_USER_AGENT'];
$ua = getv($_SERVER, 'HTTP_USER_AGENT');
/*
$newtags = '';
if ($action == 'add')
{
    $newtags = implode(",", $b->tags);
}
$logline = sprintf("%10d %20s %20s %10s %50s %40s", $AUTH_USER->id, $AUTH_USER->login, $action, $error, $ua, $newtags);
#$logger2->log($logline);
*/
