<?php
/**
 * @brief		GraphQL: Follow type defintiion
 * @author		<a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
 * @copyright	(c) 2001 - 2016 Invision Power Services, Inc.
 * @license		http://www.invisionpower.com/legal/standards/
 * @package		IPS Community Suite
 * @since		3 Sept 2018
 * @version		SVN_VERSION_NUMBER
 */

namespace IPS\Api\GraphQL\Types;
use GraphQL\Type\Definition\ObjectType;
use IPS\Api\GraphQL\TypeRegistry;
use IPS\Content;
use IPS\Db;
use IPS\Member;
use UnderflowException;
use function defined;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
	header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
	exit;
}

/**
 * FollowType for GraphQL API
 */
class FollowType extends ObjectType
{
	/**
	 * Get root type
	 *
	 */
	public function __construct()
	{		 
		$config = [
			'name' => 'Follow',
			'description' => 'Returns the follow data for a node, item or member',
			'fields' => [
				'id' => [
					'type' => TypeRegistry::id(),
					'resolve' => function ($followData) {
						// This field allows Apollo to more effectively cache results. We don't include
						// the member ID in the MD5 here though because it will change when logging in.
						return md5( $followData['app'] . ';' . $followData['area'] . ';' . $followData['id'] );
					}
				],
				'followID' => [
					'type' => TypeRegistry::id(),
					'resolve' => function ($followData) {
						return md5( $followData['app'] . ';' . $followData['area'] . ';' . $followData['id'] . ';' .  Member::loggedIn()->member_id );
					}
				],
				'isFollowing' => [
					'type' => TypeRegistry::boolean(),
					'resolve' => function ($followData) {
						return Member::loggedIn()->following( $followData['app'], $followData['area'], $followData['id'] );
					}
				],
				'followType' => [
					'type' => TypeRegistry::eNum([
						'name' => 'core_Follow_followType',
						'values' => ['NOT_FOLLOWING', 'PUBLIC', 'ANONYMOUS']
					]),
					'resolve' => function ($followData) {
						if( !Member::loggedIn()->following( $followData['app'], $followData['area'], $followData['id'] ) )
						{
							return 'NOT_FOLLOWING';
						}

						try
						{
							$current = Db::i()->select( 'follow_is_anon', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=? AND follow_member_id=?', $followData['app'], $followData['area'], $followData['id'], Member::loggedIn()->member_id ), NULL, array() )->first();

							return $current['follow_is_anon'] ? 'ANONYMOUS' : 'PUBLIC';
						}
						catch ( UnderflowException $e )
						{
							return 'NOT_FOLLOWING';
						}
					}
				],
				'followCount' => [
					'type' => TypeRegistry::int(),
					'resolve' => function ($followData) {
						if( isset( $followData['node'] ) && isset( $followData['nodeClass'] ) )
						{
							$nodeClass = $followData['nodeClass'];
							return $nodeClass::$contentItemClass::containerFollowerCount( $followData['node'] );
						}
						elseif( isset( $followData['member'] ) )
						{
							return $followData['member']->followersCount();
						}
						else
						{
							return $followData['item']->followersCount();
						}
					}
				],
				'anonFollowCount' => [
					'type' => TypeRegistry::int(),
					'resolve' => function ($followData) {
						if( isset( $followData['node'] ) && isset( $followData['nodeClass'] ) )
						{
							$nodeClass = $followData['nodeClass'];
							return $nodeClass::$contentItemClass::containerFollowerCount( $followData['node'], $nodeClass::$contentItemClass::FOLLOW_ANONYMOUS );
						}
						elseif( isset( $followData['member'] ) )
						{
							return $followData['member']->followersCount( Content::FOLLOW_ANONYMOUS );
						}
						else
						{
							return $followData['item']->followersCount( Content::FOLLOW_ANONYMOUS );
						}
					}
				],
				'followers' => [
					'type' => TypeRegistry::listOf( \IPS\core\api\GraphQL\TypeRegistry::member() ),
					'args' => [
						'offset' => [
							'type' => TypeRegistry::int(),
							'defaultValue' => 0
						],
						'limit' => [
							'type' => TypeRegistry::int(),
							'defaultValue' => 25
						]
					],
					'resolve' => function ($followData, $args) {
						$offset = max( $args['offset'], 0 );
						$limit = min( $args['limit'], 50 );

						if( isset( $followData['node'] ) && isset( $followData['nodeClass'] ) )
						{
							$nodeClass = $followData['nodeClass'];
							$_followers = $nodeClass::$contentItemClass::containerFollowers( $followData['node'], $nodeClass::$contentItemClass::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( $offset, $limit ), 'name' );
						}
						elseif( isset( $followData['member'] ) )
						{
							$_followers = $followData['member']->followers( Content::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( $offset, $limit ), 'name' );
						}
						else
						{
							$itemClass = $followData['itemClass'];
							$_followers = $followData['item']->followers( $itemClass::FOLLOW_PUBLIC, array( 'none', 'immediate', 'daily', 'weekly' ), NULL, array( $offset, $limit ), 'name' );
						}

						if( $_followers === NULL )
						{
							return NULL;
						}

						$followers = array();

						foreach( $_followers as $followerRow )
						{
							$followers[] = Member::load( $followerRow['follow_member_id'] );
						}

						return $followers;
					}
				],
				// @todo this will need to be extensively modified when I add support for following clubs
				'followOptions' => [
					'type' => TypeRegistry::listOf( new ObjectType([
						'name' => 'core_Follow_Options',
						'fields' => [
							'type' => TypeRegistry::string(),
							'selected' => TypeRegistry::boolean(),
							'disabled' => TypeRegistry::boolean()
						],
						'resolveField' => function ($followOption, $args, $context, $info) {
							return $followOption[ $info->fieldName ];
						}
					]) ),
					'resolve' => function ($followData, $args) {
						// No options if we're a guest
						if( !Member::loggedIn()->member_id )
						{
							return array();
						}

						/* Do we follow it? */
						try
						{
							$current = Db::i()->select( '*', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_rel_id=? AND follow_member_id=?', $followData['app'], $followData['area'], $followData['id'], Member::loggedIn()->member_id ), NULL, array() )->first();
						}
						catch ( UnderflowException $e )
						{
							$current = FALSE;
						}

						// What kind of following are we doing?
						if( isset( $followData['node'] ) )
						{
							$followType = 'new_content';
						}
						elseif( isset( $followData['member'] ) )
						{
							$followType = 'follower_content';
						}
						else
						{
							$followType = 'new_comment';
						}

						$notificationConfiguration = Member::loggedIn()->notificationsConfiguration();
						$notificationConfiguration = $notificationConfiguration[$followType] ?? array();

						// Now figure out which options we can have
						$options = array( array(
							'type' => 'immediate', 
							'disabled' => empty( $notificationConfiguration ), // If notification configuration is empty, show but disable 'immediate'
							'selected' => ( $current && $current['follow_notify_freq'] === 'immediate' ) 
						) );

						if( !isset( $followData['member'] ) )
						{
							$options[] = array('type' => 'weekly', 'disabled' => false, 'selected' => ( $current && $current['follow_notify_freq'] === 'weekly' ) );
							$options[] = array('type' => 'daily', 'disabled' => false, 'selected' => ( $current && $current['follow_notify_freq'] === 'daily' ) );
							$options[] = array('type' => 'none', 'disabled' => false, 'selected' => ( $current && $current['follow_notify_freq'] === 'none' ) );
						}

						return $options;
					}
				]
			]
		];

		parent::__construct( $config );
	}
}
