<?php
/**
 * REST APIs class
 *
 * @author Jegstudio
 * @since 1.0.0
 * @package gutenverse
 */

namespace Gutenverse;

use Gutenverse\Block\Post_Block;
use Gutenverse\Block\Post_List;
use Gutenverse\Framework\Init;
use WP_REST_Response;

/**
 * Class Api
 *
 * @package gutenverse
 */
class Api {
	/**
	 * Instance of Gutenverse.
	 *
	 * @var Api
	 */
	private static $instance;

	/**
	 * Endpoint Path
	 *
	 * @var string
	 */
	const ENDPOINT = 'gutenverse-client/v1';

	/**
	 * Singleton page for Gutenverse Class
	 *
	 * @return Api
	 */
	public static function instance() {
		if ( null === static::$instance ) {
			static::$instance = new static();
		}

		return static::$instance;
	}

	/**
	 * Blocks constructor.
	 */
	private function __construct() {
		if ( did_action( 'rest_api_init' ) ) {
			$this->register_routes();
		}
	}

	/**
	 * Register Gutenverse APIs
	 */
	private function register_routes() {
		/** ----------------------------------------------------------------
		 * Frontend/Global Routes
		 */
		register_rest_route(
			self::ENDPOINT,
			'postblock/data',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'post_block_data' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			self::ENDPOINT,
			'postlist/data',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'post_list_data' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			self::ENDPOINT,
			'upgrade',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'upgrade_step' ),
				'permission_callback' => array( $this, 'can_install_plugin' ),
			)
		);

		register_rest_route(
			self::ENDPOINT,
			'base-theme/get',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'base_theme_get' ),
				'permission_callback' => function () {
					if ( ! current_user_can( 'manage_options' ) ) {
						return new \WP_Error(
							'forbidden_permission',
							esc_html__( 'Forbidden Access', 'gutenverse' ),
							array( 'status' => 403 )
						);
					}

					return true;
				},
			)
		);

		register_rest_route(
			self::ENDPOINT,
			'check/license',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'license_check' ),
				'permission_callback' => array( $this, 'is_user_logged_in' ),
			)
		);

		register_rest_route(
			self::ENDPOINT,
			'check/requirement',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'requirement_check' ),
				'permission_callback' => '__return_true',
			)
		);

		register_rest_route(
			'gutenverse/v1',
			'get-post-data',
			array(
				'methods'             => 'GET',
				'callback'            => array( $this, 'get_post_data' ),
				'permission_callback' => function () {
					return current_user_can( 'edit_posts' );
				},
			)
		);
	}

	/**
	 * Only for user who can install plugin.
	 */
	public function can_install_plugin() {
		if ( ! current_user_can( 'install_plugins' ) ) {
			return new \WP_Error(
				'forbidden_permission',
				esc_html__( 'Forbidden Access', 'gutenverse' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * Only for logged in users.
	 */
	public function is_user_logged_in() {
		if ( ! is_user_logged_in() ) {
			return new \WP_Error(
				'forbidden_permission',
				esc_html__( 'Forbidden Access', 'gutenverse' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * Upgrade Step.
	 *
	 * @param object $request Request Object.
	 *
	 * @return WP_Rest
	 */
	public function upgrade_step( $request ) {
		$step   = $request->get_param( 'step' );
		$result = true;

		switch ( $step ) {
			case 'form':
				$result = $this->install_plugin( 'gutenverse-form', 'gutenverse-form/gutenverse-form.php' );
				break;
			case 'icon':
				$result = Init::instance()->assets->download_font_icon();
				break;
			default:
				( new Upgrader() )->upgrader_page_upgrade_close();
				break;
		}

		return $result;
	}

	/**
	 * Install Plugin.
	 *
	 * @param string $path Plugin path.
	 *
	 * @return boolean
	 */
	public function is_plugin_installed( $path ) {
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
		$installed_plugins = get_plugins();
		return isset( $installed_plugins[ $path ] );
	}

	/**
	 * Install Plugin.
	 *
	 * @param string $slug Plugin slug.
	 * @param string $plugin plugin path.
	 */
	public function install_plugin( $slug, $plugin ) {
		$result = true;

		if ( ! $this->is_plugin_installed( $plugin ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
			require_once ABSPATH . 'wp-admin/includes/file.php';
			require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
			require_once ABSPATH . 'wp-admin/includes/plugin.php';

			$api = plugins_api(
				'plugin_information',
				array(
					'slug' => $slug,
				)
			);

			$plugin_upgrader = new \Plugin_Upgrader( new \WP_Ajax_Upgrader_Skin() );

			$result = $plugin_upgrader->install(
				$api->download_link,
				array(
					'clear_update_cache' => false,
				)
			);
		}

		if ( $result || ! is_wp_error( $result ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
			activate_plugin( $plugin, null, null, true );
		}

		return true;
	}

	/**
	 * Get Post Block Pagination Data
	 *
	 * @param WP_REST_Request $request .
	 */
	public function post_block_data( $request ) {
		$attributes = $request->get_param( 'attributes' );
		$post_data  = new Post_Block();

		if ( is_array( $attributes ) ) {
			$attributes['fromPagination'] = true;
		}

		$post_data->set_attributes( $attributes );

		$render = $post_data->render_frontend( false );

		return new WP_REST_Response(
			array(
				'rendered' => $render,
			),
			200
		);
	}

	/**
	 * Get Post List Pagination Data
	 *
	 * @param object $request .
	 */
	public function post_list_data( $request ) {
		$attributes = $request['attributes'];
		$post_data  = new Post_List();

		$post_data->set_attributes( $attributes );

		$render = $post_data->render_frontend();

		return array(
			'rendered' => $render,
		);
	}

	/**
	 * Get base theme data
	 *
	 * @param object $request .
	 *
	 * @return object
	 */
	public function base_theme_get( $request ) {

		/**Check if file exist */
		$upload_dir       = wp_upload_dir();
		$upload_base_path = $upload_dir['basedir'];
		$file_path        = $upload_base_path . '/gutenverse/base-themes/data.json';

		global $wp_filesystem;

		if ( ! function_exists( 'WP_Filesystem' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();
		}

		/**Check schedule fetch */
		$companion_data = get_option( 'gutenverse-companion-base-theme', false );
		$fetch_time     = null;
		$now            = time();
		if ( $companion_data ) {
			$fetch_time = $companion_data['fetch_time'];
		}
		/**Check if file exist */
		if ( ! $wp_filesystem->exists( $file_path ) ) {
			$updated = $this->update_basetheme_data( $request );
			if ( ! $updated ) {
				return new \WP_REST_Response(
					array(
						'message' => 'Unable to fetch demo data : Server Down! Please try again later.',
					),
					400
				);
			}
			$next_fetch = $now + ( 24 * 60 * 60 );
			update_option(
				'gutenverse-companion-base-theme',
				array(
					'fetch_time' => $next_fetch,
				),
				false
			);
		}
		if ( null === $fetch_time || $fetch_time < $now ) {
			/**Update demo data and fetch time */
			$updated = $this->update_basetheme_data( $request );
			if ( ! $updated ) {
				return new \WP_REST_Response(
					array(
						'message' => 'Unable to fetch demo data : Server Down! Please try again later.',
					),
					400
				);
			}
			$next_fetch = $now + ( 24 * 60 * 60 );
			update_option(
				'gutenverse-companion-base-theme',
				array(
					'fetch_time' => $next_fetch,
				),
				false
			);
		}

		$json_content = file_get_contents( $file_path );
		$data         = json_decode( $json_content, true );
		return rest_ensure_response( $data );
	}

	/**
	 * Update base theme Data
	 */
	public function update_basetheme_data() {

		$response = wp_remote_post(
			GUTENVERSE_FRAMEWORK_LIBRARY_URL . 'wp-json/gutenverse-server/v4/companion/base-theme',
			array(
				'body'    => '',
				'headers' => array(
					'Content-Type' => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
			return false;
		}

		$response_body = wp_remote_retrieve_body( $response );

		$data = json_decode( $response_body, true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			gutenverse_rlog( 'JSON Decode Error: ' . json_last_error_msg() );
			return false;
		}

		// filter show case.
		if ( isset( $data['companion'] ) && is_array( $data['companion'] ) ) {
			$data['companion'] = array_values(
				array_filter(
					$data['companion'],
					function ( $item ) {
						return isset( $item['slug'] ) && 'show-case' !== $item['slug'];
					}
				)
			);
		}

		$filtered_json = wp_json_encode( $data );

		if ( false === $filtered_json ) {
			gutenverse_rlog( 'JSON Encode Error' );
			return false;
		}

		/**Check if directory exist */
		$basedir   = wp_upload_dir()['basedir'];
		$directory = $basedir . '/gutenverse/base-themes';
		if ( ! is_dir( $directory ) ) {
			wp_mkdir_p( $directory );
		}
		$file_path = $directory . '/data.json';

		/**Save data to json file */
		require_once ABSPATH . 'wp-admin/includes/file.php';
		WP_Filesystem();
		global $wp_filesystem;
		$wp_filesystem->put_contents( $file_path, $filtered_json, FS_CHMOD_FILE );
		return true;
	}

	/**
	 * Check if license key is active.
	 *
	 * @param Array $param Array of Request.
	 *
	 * @return WP_Rest.
	 */
	public function license_check( $param ) {
		$response = wp_remote_post(
			GUTENVERSE_LICENSE_SERVER . '/wp-json/gutenverse-pro/v1/license/validate',
			array(
				'body'    => wp_json_encode( $param ),
				'headers' => array(
					'Content-Type' => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			return false;
		} else {
			return json_decode( wp_remote_retrieve_body( $response ) );
		}
	}

	/**
	 * Check if requirement fullfiled.
	 *
	 * @return bool.
	 */
	public function requirement_check() {
		$plugins        = array();
		$active_plugins = get_option( 'active_plugins' );

		foreach ( $active_plugins as $active ) {
			$plugins[] = explode( '/', $active )[0];
		}
		$is_complete = apply_filters( 'gutenverse_companion_base_theme', false ) && in_array( 'gutenverse-companion', $plugins, true );
		return $is_complete;
	}

	/**
	 * Get Post Data for Post Block
	 *
	 * @param object $request Request Object.
	 *
	 * @return WP_REST_Response
	 */
	public function get_post_data( $request ) {
		$attributes = $request->get_param( 'attributes' );

		if ( ! is_array( $attributes ) ) {
			return new \WP_REST_Response(
				array(
					'posts'       => array(),
					'total_pages' => 1,
				),
				200
			);
		}

		// Filter and prepare attributes.
		$attr = $this->filter_post_attributes( $attributes );

		// Build query arguments using the same logic as Post_Abstract.
		$include_category = array();
		$exclude_category = array();
		$args             = array();

		$args['post_type']   = isset( $attr['postType'] ) ? sanitize_text_field( $attr['postType'] ) : 'post';
		$args['post_status'] = 'publish';

		// Handle pagination.
		$paged = isset( $attr['editParam']['page'] ) ? intval( $attr['editParam']['page'] ) : 1;
		$paged = max( 1, $paged );

		$args['paged']               = $paged;
		$args['offset']              = isset( $attr['postOffset'] ) ? intval( $attr['postOffset'] ) : 0;
		$args['posts_per_page']      = isset( $attr['numberPost'] ) ? intval( $attr['numberPost'] ) : 5;
		$args['ignore_sticky_posts'] = 1;

		// Handle inherit query for archive/search pages.
		if ( isset( $attr['inheritQuery'] ) && ( true === $attr['inheritQuery'] || 'true' === $attr['inheritQuery'] ) ) {
			$search_query = get_search_query();
			$object_query = get_queried_object();

			$args['s'] = ! empty( $search_query ) ? esc_attr( $search_query ) : null;

			if ( ! empty( $object_query ) ) {
				if ( $object_query instanceof \WP_Term ) {
					switch ( $object_query->taxonomy ) {
						case 'category':
							$args['category_name'] = $object_query->slug;
							break;
						case 'post_tag':
							$args['tag'] = $object_query->slug;
							break;
					}
				}

				if ( $object_query instanceof \WP_User ) {
					$args['author'] = $object_query->ID;
				}
			}
		}

		// Handle post inclusion.
		if ( ! empty( $attr['includePost'] ) && is_array( $attr['includePost'] ) ) {
			$args['post__in'] = $this->filter_array( $attr['includePost'] );
		}

		// Handle post exclusion.
		if ( ! empty( $attr['excludePost'] ) && is_array( $attr['excludePost'] ) ) {
			$args['post__not_in'] = $this->filter_array( $attr['excludePost'] );
		}

		// Handle category inclusion with recursive children.
		if ( ! empty( $attr['includeCategory'] ) && is_array( $attr['includeCategory'] ) ) {
			$categories = $this->filter_array( $attr['includeCategory'] );
			$this->recursive_category( $categories, $include_category );
			$args['category__in'] = $include_category;
		}

		// Handle category exclusion with recursive children.
		if ( ! empty( $attr['excludeCategory'] ) && is_array( $attr['excludeCategory'] ) ) {
			$categories = $this->filter_array( $attr['excludeCategory'] );
			$this->recursive_category( $categories, $exclude_category );
			$args['category__not_in'] = $exclude_category;
		}

		// Handle author filter.
		if ( ! empty( $attr['includeAuthor'] ) && is_array( $attr['includeAuthor'] ) ) {
			$args['author__in'] = $this->filter_array( $attr['includeAuthor'] );
		}

		// Handle tag inclusion.
		if ( ! empty( $attr['includeTag'] ) && is_array( $attr['includeTag'] ) ) {
			$args['tag__in'] = $this->filter_array( $attr['includeTag'] );
		}

		// Handle tag exclusion.
		if ( ! empty( $attr['excludeTag'] ) && is_array( $attr['excludeTag'] ) ) {
			$args['tag__not_in'] = $this->filter_array( $attr['excludeTag'] );
		}

		// Handle sorting.
		$sort_by = isset( $attr['sortBy'] ) ? $attr['sortBy'] : 'latest';

		switch ( $sort_by ) {
			case 'latest':
				$args['orderby'] = 'date';
				$args['order']   = 'DESC';
				break;
			case 'oldest':
				$args['orderby'] = 'date';
				$args['order']   = 'ASC';
				break;
			case 'alphabet_asc':
				$args['orderby'] = 'title';
				$args['order']   = 'ASC';
				break;
			case 'alphabet_desc':
				$args['orderby'] = 'title';
				$args['order']   = 'DESC';
				break;
			case 'random':
				$args['orderby'] = 'rand';
				break;
			case 'random_week':
				$args['orderby']    = 'rand';
				$args['date_query'] = array(
					array(
						'after' => '1 week ago',
					),
				);
				break;
			case 'random_month':
				$args['orderby']    = 'rand';
				$args['date_query'] = array(
					array(
						'after' => '1 month ago',
					),
				);
				break;
			case 'most_comment':
				$args['orderby'] = 'comment_count';
				$args['order']   = 'DESC';
				break;
		}

		// Apply filter to allow customization.
		$args = apply_filters( 'gutenverse_default_query_args', $args, $attr );

		// Execute query.
		$query = new \WP_Query( $args );

		$posts = array();

		if ( $query->have_posts() ) {
			while ( $query->have_posts() ) {
				$query->the_post();
				$post_id = get_the_ID();

				// Get primary category.
				$primary_category = null;
				$categories       = get_the_category( $post_id );
				if ( ! empty( $categories ) ) {
					$category         = $categories[0];
					$primary_category = array(
						'name' => $category->name,
						'slug' => $category->slug,
						'url'  => get_category_link( $category->term_id ),
					);
				}

				// Get thumbnail data.
				$thumbnail = null;
				if ( has_post_thumbnail( $post_id ) ) {
					$thumbnail_id = get_post_thumbnail_id( $post_id );
					$image_data   = wp_get_attachment_image_src( $thumbnail_id, 'post-thumbnail' );
					if ( $image_data ) {
						$thumbnail = array(
							'url'    => $image_data[0],
							'width'  => $image_data[1],
							'height' => $image_data[2],
						);
					}
				}

				// Get excerpt.
				$excerpt = get_the_excerpt( $post_id );
				if ( empty( $excerpt ) ) {
					$excerpt = wp_trim_words( get_the_content(), 9999, '...' );
				}

				// Format dates based on user settings.
				$date_type          = isset( $attr['metaDateType'] ) ? $attr['metaDateType'] : 'published';
				$date_format        = isset( $attr['metaDateFormat'] ) ? $attr['metaDateFormat'] : 'default';
				$date_format_custom = isset( $attr['metaDateFormatCustom'] ) ? $attr['metaDateFormatCustom'] : '';

				// Get timestamps.
				$published_time = get_the_time( 'U', $post_id );
				$modified_time  = get_the_modified_time( 'U', $post_id );

				// Format based on date type and format.
				$formatted_date = '';

				if ( 'ago' === $date_format ) {
					// Relative time format.
					if ( 'modified' === $date_type ) {
						$formatted_date = human_time_diff( $modified_time, current_time( 'timestamp' ) ) . ' ago';
					} elseif ( 'both' === $date_type ) {
						$formatted_date = human_time_diff( $published_time, current_time( 'timestamp' ) ) . ' ago - Updated ' . human_time_diff( $modified_time, current_time( 'timestamp' ) ) . ' ago';
					} else {
						$formatted_date = human_time_diff( $published_time, current_time( 'timestamp' ) ) . ' ago';
					}
				} elseif ( 'custom' === $date_format && ! empty( $date_format_custom ) ) {
					// Custom format.
					if ( 'modified' === $date_type ) {
						$formatted_date = get_the_modified_date( $date_format_custom, $post_id );
					} elseif ( 'both' === $date_type ) {
						$formatted_date = get_the_date( $date_format_custom, $post_id ) . ' - Updated on ' . get_the_modified_date( $date_format_custom, $post_id );
					} else {
						$formatted_date = get_the_date( $date_format_custom, $post_id );
					}
				} else {
					// Default WordPress format.
					if ( 'modified' === $date_type ) {
						$formatted_date = get_the_modified_date( '', $post_id );
					} elseif ( 'both' === $date_type ) {
						$formatted_date = get_the_date( '', $post_id ) . ' - Updated on ' . get_the_modified_date( '', $post_id );
					} else {
						$formatted_date = get_the_date( '', $post_id );
					}
				}

				// Get author data.
				$author_id   = get_the_author_meta( 'ID' );
				$author_name = get_the_author_meta( 'display_name', $author_id );
				$author_url  = get_author_posts_url( $author_id );

				// Get comment data.
				$comment_count = get_comments_number( $post_id );
				$comment_url   = get_comments_link( $post_id );

				// Get post classes.
				$post_classes = implode( ' ', get_post_class( 'guten-post', $post_id ) );

				$posts[] = array(
					'id'               => $post_id,
					'title'            => get_the_title( $post_id ),
					'url'              => get_permalink( $post_id ),
					'excerpt'          => $excerpt,
					'thumbnail'        => $thumbnail,
					'primary_category' => $primary_category,
					'author_name'      => $author_name,
					'author_url'       => $author_url,
					'date'             => $formatted_date,
					'comment_count'    => $comment_count,
					'comment_url'      => $comment_url,
					'classes'          => $post_classes,
				);
			}
			wp_reset_postdata();
		}

		return new \WP_REST_Response(
			array(
				'posts'       => $posts,
				'total_pages' => $query->max_num_pages,
			),
			200
		);
	}

	/**
	 * Filter attributes array to extract values.
	 *
	 * @param array $attributes Attributes array.
	 *
	 * @return array
	 */
	private function filter_post_attributes( $attributes ) {
		$accepted = array(
			'postType',
			'numberPost',
			'postOffset',
			'includePost',
			'excludePost',
			'includeCategory',
			'excludeCategory',
			'includeAuthor',
			'includeTag',
			'excludeTag',
			'sortBy',
			'inheritQuery',
			'editParam',
			'metaDateType',
			'metaDateFormat',
			'metaDateFormatCustom',
		);

		$filtered = array();
		foreach ( $attributes as $key => $value ) {
			if ( in_array( $key, $accepted, true ) ) {
				$filtered[ $key ] = $value;
			}
		}

		return $filtered;
	}

	/**
	 * Filter inclusion/exclusion array.
	 *
	 * @param array $arr Inclusion/exclusion array.
	 *
	 * @return array
	 */
	private function filter_array( $arr ) {
		$result = array();

		if ( ! is_array( $arr ) ) {
			return $result;
		}

		foreach ( $arr as $item ) {
			if ( isset( $item['value'] ) ) {
				$result[] = $item['value'];
			}
		}

		return $result;
	}

	/**
	 * Get category and its children recursively.
	 *
	 * @param array $categories Category to be checked.
	 * @param array $result     Result for recursive category.
	 */
	private function recursive_category( $categories, &$result ) {
		foreach ( $categories as $category ) {
			if ( ! in_array( $category, $result, true ) ) {
				$result[] = $category;
				$children = get_categories( array( 'parent' => $category ) );

				if ( ! empty( $children ) ) {
					$child_id = array();
					foreach ( $children as $child ) {
						$child_id[] = $child->term_id;
					}
					$this->recursive_category( $child_id, $result );
				}
			}
		}
	}
}
