<?php
/**
 * OrokuBlocks - GamiPress Integration
 * 
 * @package OrokuBlocks
 * @author OrokuBlocks (https://orokublocks.com/)
 * 
 * @since 1.0.0
 * 
 */

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
  exit;
}

if ( ! class_exists( 'OrokuBlocks_Gamipress' ) ) {
  final class OrokuBlocks_Gamipress {
    /**
     * Prevent more than one instance from being created
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function __construct() {}

    /**
     * Prevent cloning
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function __clone() {}

    /**
     * Returns achievement, point or rank thumbnail url.
     * 
     * @since 1.0.0
     * 
     * @param int     $post_id            Id of the achievement, point or rank to get the thumbnail of.
     * @return string $thumbnail_url      Achievement, point or rank thumbnail url.
     */
    public static function postThumbnailUrlGet( $post_id ) {
      $thumbnail_url = '';

      if ( is_multisite() ) {
        $site_id = gamipress_is_network_wide_active() ? get_main_site_id() : get_current_blog_id();

        switch_to_blog( $site_id );
      }

      $thumbnail_post_id = get_post_thumbnail_id( $post_id );

      // if achievement, point or rank doesn't have an image assigned, check if default image exists
      if ( ! $thumbnail_post_id ) {
        // get achievement, point or rank type
        $achievement = get_page_by_path( gamipress_get_post_type( $post_id ), OBJECT, 'achievement-type' );

        if ( is_object( $achievement ) ) {
          // get achievement, point or rank type thumbnail id
          $thumbnail_post_id = get_post_thumbnail_id( $achievement->ID );
        }
      }

      // if thumbnail exists
      if ( $thumbnail_post_id !== 0 ) {
        $thumbnail_url = OrokuBlocks_Blog::thumbnailUrlGet( $thumbnail_post_id );
      }

      if ( is_multisite() ) {
        restore_current_blog();
      }

      return $thumbnail_url;
    }

    /**
     * Returns user amount of points.
     * 
     * @since 1.0.0
     * 
     * @param string  $point_slug             Slug of the point to get data from.
     * @return int    $points_amount          Amount of points the user has.   
     */
    public static function userPointsGet( $point_slug ) {
      $points_amount = 0;

      $points_amount = gamipress_get_user_points( get_current_user_id(), $point_slug );

      return $points_amount;
    }

    /**
     * Returns point data by slug.
     * 
     * @since 1.0.0
     * 
     * @param string      $point_slug         Slug of the point to get data from.
     * @return array|bool $point_data         Point data or false if not found.   
     */
    public static function pointGet( $point_slug ) {
      $point_data = false;

      $point_types = gamipress_get_points_types();

      if ( array_key_exists( $point_slug, $point_types ) ) {
        $point = $point_types[$point_slug];

        $point_id = $point['ID'];

        $point_data = [
          'id'            => $point_id,
          'singular_name' => $point['singular_name'],
          'plural_name'   => $point['plural_name'],
          'slug'          => $point_slug,
          'image_url'     => self::postThumbnailURLGet( $point_id )
        ];
      }

      return $point_data;
    }

    /**
     * Returns point modifiers (awards / deducts) data by slug.
     * 
     * @since 1.0.0
     * 
     * @param string  $point_slug           Slug of the point to get data from.
     * @param string  $point_modifier       Point modifier, one of: 'awards', 'deducts'.
     * @return array  $point_modifiers      Point modifiers.   
     */
    public static function pointModifiersGet( $point_slug, $point_modifier = '' ) {
      $point_modifiers = [];

      switch ( $point_modifier ) {
        case 'deducts':
          $modifier_posts = gamipress_get_points_type_points_deducts( $point_slug );
          break;
        case 'awards':
          $modifier_posts = gamipress_get_points_type_points_awards( $point_slug );
          break;
        default:
          $modifier_posts = false;
          break;
      }

      if ( is_array( $modifier_posts ) ) {
        foreach ( $modifier_posts as $modifier_post ) {
          $point_modifiers[] = [
            'id'      => $modifier_post->ID,
            'title'   => $modifier_post->post_title,
            'points'  => intval( gamipress_get_post_meta( $modifier_post->ID, '_gamipress_points' ) ),
            'limit'   => [
              'type'    => gamipress_get_post_meta( $modifier_post->ID, '_gamipress_limit_type' ),
              'amount'  => intval( gamipress_get_post_meta( $modifier_post->ID, '_gamipress_limit' ) )
            ]
          ];
        }
      }

      return $point_modifiers;
    }

    /**
     * Returns the data of points that are awarded when completing an achievement.
     * 
     * @since 1.0.0
     * 
     * @param int         $achievement_id           Id of the achievement.
     * @return array|bool $point_data               Data of the points awarded by the achievement.
     */
    public static function achievementPointsGet( $achievement_id ) {
      $points_awarded_slug = gamipress_get_post_meta( $achievement_id, '_gamipress_points_type' );
      $points_awarded_amount = intval( gamipress_get_post_meta( $achievement_id, '_gamipress_points' ) );

      $points_awarded = [
        'amount'    => $points_awarded_amount,
        'image_url' => ''
      ];

      $point_data = self::pointGet( $points_awarded_slug );

      if ( $point_data ) {
        $points_awarded = array_merge( $points_awarded , $point_data );
      }

      return $points_awarded;
    }

    /**
     * Returns steps that need to be completed to earn an achievement.
     * 
     * @since 1.0.0
     * 
     * @param int     $achievement_id           Id of the achievement.
     * @return array  $achievement_steps        Achievement steps.
     */
    public static function achievementStepsGet( $achievement_id ) {
      $achievement_steps = [];
      $steps = gamipress_get_achievement_steps( $achievement_id );

      foreach ( $steps as $step ) {
        $achievement_steps[] = [
          'id'        => $step->ID,
          'title'     => $step->post_title,
          'complete'  => gamipress_has_user_earned_achievement( $step->ID, get_current_user_id() )
        ];
      }

      return $achievement_steps;
    }

    /**
     * Returns how many users have earned an achievement and the data of the most recent $limit earners.
     * 
     * @since 1.0.0
     * 
     * @param int     $achievement_id           Id of the achievement.
     * @param int     $limit                    Amount of last earners to retrieve user data of.
     * @return array  $achievement_earners      Achievement earners user data.
     */
    public static function achievementEarnersGet( $achievement_id, $limit = 5 ) {
      $achievement_earners = [
        'count' => count( gamipress_get_achievement_earners( $achievement_id ) ),
        'users' => []
      ];

      $achievement_earners_users = gamipress_get_achievement_earners( $achievement_id, [ 'limit' => $limit ] );

      foreach ( $achievement_earners_users as $achievement_earners_user ) {
        $achievement_earners['users'][] = [
          'id'            => $achievement_earners_user->ID,
          'user_login'    => $achievement_earners_user->user_login,
          'user_nicename' => $achievement_earners_user->user_nicename,
          'display_name'  => $achievement_earners_user->display_name,
          'image_url'     => get_avatar_url( $achievement_earners_user->ID )
        ];
      }

      return $achievement_earners;
    }

    /**
     * Return achievement data by slug or false if not found.
     * 
     * @since 1.0.0
     * 
     * @param string      $achievement_slug         Achievement slug.
     * @return array|bool $achievement_data         Achievement data or false if not found.
     */
    public static function achievementGet( $achievement_slug ) {
      $achievement_data = false;

      $args = [
        'post_name__in' => [ $achievement_slug ]
      ];

      $achievements = gamipress_get_achievements( $args );

      if ( count( $achievements ) > 0 ) {
        $achievement = $achievements[0];

        /**
         * Get achievement points
         */
        $points_awarded = self::achievementPointsGet( $achievement->ID );

        /**
         * Get achievement steps
         */
        $achievement_steps = self::achievementStepsGet( $achievement->ID );

        /**
         * Get achievement earners
         */
        $achievement_earners = self::achievementEarnersGet( $achievement->ID );

        $achievement_data = [
          'id'              => $achievement->ID,
          'slug'            => $achievement_slug,
          'title'           => $achievement->post_title,
          'content'         => $achievement->post_content,
          'image_url'       => self::postThumbnailUrlGet( $achievement->ID ),
          'points_awarded'  => $points_awarded,
          'earners'         => $achievement_earners,
          'complete'        => gamipress_has_user_earned_achievement( $achievement->ID, get_current_user_id() ),
          'steps'           => $achievement_steps
        ];
      }

      return $achievement_data;
    }

    /**
     * Returns steps that need to be completed to earn a rank.
     * 
     * @since 1.0.0
     * 
     * @param int     $rank_id            Id of the rank.
     * @return array  $rank_steps         Rank steps.
     */
    public static function rankStepsGet( $rank_id ) {
      $rank_steps = [];
      $steps = gamipress_get_rank_requirements( $rank_id );

      foreach ( $steps as $step ) {
        $rank_steps[] = [
          'id'        => $step->ID,
          'title'     => $step->post_title,
          'complete'  => gamipress_has_user_earned_achievement( $step->ID, get_current_user_id() )
        ];
      }

      return $rank_steps;
    }

    /**
     * Returns how many users have earned a rank and the data of the most recent $limit earners.
     * 
     * @since 1.0.0
     * 
     * @param int     $rank_id            Id of the rank.
     * @param int     $limit              Amount of last earners to retrieve user data of.
     * @return array  $rank_earners       Rank earners user data.
     */
    public static function rankEarnersGet( $rank_id, $limit = 5 ) {
      $rank_earners = [
        'count' => count( gamipress_get_rank_earners( $rank_id ) ),
        'users' => []
      ];

      $rank_earners_users = gamipress_get_rank_earners( $rank_id, [ 'limit' => $limit ] );

      foreach ( $rank_earners_users as $rank_earners_user ) {
        $rank_earners['users'][] = [
          'id'            => $rank_earners_user->ID,
          'user_login'    => $rank_earners_user->user_login,
          'user_nicename' => $rank_earners_user->user_nicename,
          'display_name'  => $rank_earners_user->display_name,
          'image_url'     => get_avatar_url( $rank_earners_user->ID )
        ];
      }

      return $rank_earners;
    }

    /**
     * Return rank data by slug or false if not found.
     * 
     * @since 1.0.0
     * 
     * @param string      $rank_slug         Rank slug.
     * @return array|bool $rank_data         Rank data or false if not found.
     */
    public static function rankGet( $rank_slug ) {
      $rank_data = false;

      $args = [
        'post_name__in' => [ $rank_slug ]
      ];

      $ranks = gamipress_get_ranks( $args );

      if ( count( $ranks ) > 0 ) {
        $rank = $ranks[0];

        /**
         * Get rank steps
         */
        $rank_steps = self::rankStepsGet( $rank->ID );

        /**
         * Get rank earners
         */
        $rank_earners = self::rankEarnersGet( $rank->ID );

        $rank_data = [
          'id'              => $rank->ID,
          'slug'            => $rank_slug,
          'title'           => $rank->post_title,
          'content'         => $rank->post_content,
          'image_url'       => self::postThumbnailUrlGet( $rank->ID ),
          'earners'         => $rank_earners,
          'complete'        => gamipress_is_lowest_priority_rank( $rank->ID ) || gamipress_has_user_earned_rank( $rank->ID, get_current_user_id() ),
          'steps'           => $rank_steps
        ];
      }

      return $rank_data;
    }
  }
}