<?php
/**
 * Licensing Projects Component.
 *
 * @since 4.18.0
 *
 * @package LearnDash
 */

declare( strict_types=1 );

namespace LearnDash\Hub\Component;

use LearnDash\Hub\Framework\Base;

defined( 'ABSPATH' ) || exit;

/**
 *
 */
class Projects extends Base {
	/**
	 * Return all the current installed projects with metadata.
	 *
	 * @param array $api_data The get_projects API data.
	 *
	 * @return array
	 */
	public function get_installed_projects( array $api_data ): array {
		foreach ( $api_data as $slug => &$item ) {
			$plugin_slug = $this->get_plugin_slug( $slug, $item['name'] );

			// the learndash-hub always activated.
			if ( ! $plugin_slug && 'learndash-hub' !== $slug ) {
				unset( $api_data[ $slug ] );
				continue;
			}

			// check it is update available.
			$plugin_data = null;
			if ( 'learndash-hub' === $slug ) {
				$mu_path = WPMU_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'learndash-hub-mu.php';
				if ( file_exists( $mu_path ) ) {
					$plugin_data = get_plugin_data( $mu_path );
				}
			}

			$plugin_path = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_slug;
			if ( null === $plugin_data && is_file( $plugin_path ) ) {
				$plugin_data = get_plugin_data( $plugin_path );
			} elseif ( $plugin_data === null ) {
				$plugin_data = [];
			}

			if ( empty( $plugin_data['Version'] ) ) {
				$plugin_data['Version'] = '';
			}

			$item['has_update']  = $this->has_newer_version( $plugin_data['Version'], $item['latest_version'], '<' );
			$item['is_active']   = is_plugin_active( $plugin_slug );
			$item['version']     = $plugin_data['Version'];
			$item['folder_slug'] = $plugin_slug;
		}

		return $api_data;
	}

	/**
	 * Return the last check timestamp.
	 *
	 * @return int
	 */
	public function get_project_check_time() {
		$cached = get_option( 'learndash-hub-projects-api' );
		if ( ! isset( $cached['last_check'] ) ) {
			return time();
		}

		return $cached['last_check'];
	}

	/**
	 * Install, activate, deactivate or delete a plugin.
	 *
	 * @param string $action
	 * @param string $slug
	 *
	 * @return bool|int|true|\WP_Error|null
	 */
	public function handle_plugin( string $action, string $slug ) {
		switch ( $action ) {
			case 'install':
				return $this->install_plugin( $slug );
			case 'activate':
				return $this->activate_plugin( $slug );
			case 'deactivate':
				return $this->deactivate_plugin( $slug );
			case 'delete':
				return $this->delete_plugin( $slug );
			case 'update':
				return $this->update_plugin( $slug );
			default:
				return false;
		}
	}

	/**
	 * Delete our plugin.
	 *
	 * @param string $slug The plugin slug.
	 *
	 * @return bool|\WP_Error
	 */
	public function delete_plugin( string $slug ) {
		if ( 'learndash-hub' === $slug && file_exists( WPMU_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'learndash-hub-mu-plugin.php' ) ) {
			WP_Filesystem();
			global $wp_filesystem;

			$wp_filesystem->delete( WPMU_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'learndash-hub', true );
			$wp_filesystem->delete( WPMU_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'learndash-hub-mu-plugin.php' );

			return true;
		}

		$plugin_slug = $this->get_plugin_slug( $slug );
		if ( ! $plugin_slug ) {
			return new \WP_Error( 'err', __( 'Invalid plugin file.', 'learndash' ) );
		}
		$ret = delete_plugins( array( $plugin_slug ) );
		if ( null === $ret ) {
			return new \WP_Error( 'err', __( 'Filesystem credentials are required to proceed', 'learndash' ) );
		}
		// clear cache first.
		wp_cache_delete( 'plugins', 'plugins' );
		delete_site_transient( 'update_plugins' );

		return $ret;
	}

	/**
	 * Activate our plugin.
	 *
	 * @param string $slug The plugin slug.
	 *
	 * @return true|\WP_Error
	 */
	public function activate_plugin( string $slug ) {
		$plugin_slug = $this->get_plugin_slug( $slug );
		if ( ! $plugin_slug ) {
			return new \WP_Error( 'err', __( 'Invalid plugin file.', 'learndash' ) );
		}

		$ret = activate_plugin( $plugin_slug );
		if ( is_wp_error( $ret ) ) {
			return $ret;
		}

		return true;
	}

	/**
	 * Deactivate a plugin.
	 *
	 * @param string $slug
	 *
	 * @return bool|\WP_Error
	 */
	public function deactivate_plugin( string $slug ) {
		$plugin_slug = $this->get_plugin_slug( $slug );
		if ( ! $plugin_slug ) {
			return new \WP_Error( 'err', __( 'Invalid plugin file.', 'learndash' ) );
		}
		deactivate_plugins( $plugin_slug );

		return ! is_plugin_active( $plugin_slug );
	}

	/**
	 * Update our plugin.
	 *
	 * @param string $slug The plugin slug.
	 *
	 * @return true|\WP_Error
	 */
	public function update_plugin( string $slug ) {
		if ( ! function_exists( 'plugins_api' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
		}
		delete_site_option( 'learndash_hub_update_plugins_cache' );
		$api = plugins_api(
			'plugin_information',
			array(
				'slug' => $slug,
			)
		);
		if ( is_wp_error( $api ) ) {
			return $api;
		}

		$status = install_plugin_install_status( $api );
		if ( 'update_available' === $status['status'] ) {
			return $this->install( $slug, true );
		}

		return new \WP_Error( 'err', __( 'Plugin update failed.', 'learndash' ) );
	}

	/**
	 * Install our plugin.
	 *
	 * @param string $slug The plugin slug.
	 *
	 * @return true|\WP_Error
	 */
	public function install_plugin( string $slug ) {
		if ( ! function_exists( 'plugins_api' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
		}
		$api = plugins_api(
			'plugin_information',
			array(
				'slug' => $slug,
			)
		);

		if ( is_wp_error( $api ) ) {
			return $api;
		}

		$status = install_plugin_install_status( $api );
		if ( 'install' === $status['status'] ) {
			// install it.
			return $this->install( $slug );
		}

		return new \WP_Error( 'err1', __( 'Plugin install failed', 'learndash' ) );
	}

	/**
	 * Fetch the plugins category so we can use it for filter.
	 *
	 * @param array $api_data The get_projects API data.
	 *
	 * @return array
	 */
	public function get_projects_category( array $api_data ): array {
		$categories = array();
		foreach ( $api_data as $slug => $item ) {
			$tags       = explode( ',', $item['tags'] );
			$categories = array_merge( $categories, $tags );
		}

		$categories = array_unique( $categories );
		$categories = array_filter( $categories );

		return array_map( 'trim', $categories );
	}

	/**
	 * @param array $api_data The get_projects API data.
	 *
	 * @return array
	 */
	public function get_premium_projects( array $api_data ): array {
		$installed_projects = $this->get_installed_projects( $api_data );
		foreach ( $api_data as $slug => $item ) {
			if ( $item['product_type'] !== 'premium' ) {
				unset( $api_data[ $slug ] );
				continue;
			}

			// now check if the plugins is installed.
			$api_data[ $slug ]['is_installed'] = isset( $installed_projects[ $slug ] );
		}

		return $api_data;
	}

	/**
	 * @return array
	 */
	public function get_aff_projects() {
		$projects = $this->do_api_request( '/repo/aff_plugins' );
		if ( ! is_array( $projects ) ) {
			return array();
		}

		return $projects;
	}

	/**
	 * Return the projects that has not installed.
	 *
	 * @param array $api_data The get_projects API data.
	 *
	 * @return array
	 */
	public function get_projects( array $api_data ): array {
		$installed_projects = $this->get_installed_projects( $api_data );

		foreach ( $api_data as $slug => &$item ) {
			if ( $item['product_type'] !== 'standard' ) {
				unset( $api_data[ $slug ] );
				continue;
			}

			if ( isset( $installed_projects[ $slug ] ) ) {
				unset( $api_data[ $slug ] );
			}
		}

		return $api_data;
	}

	/**
	 * Look up a project object, this is use when pulling the plugin info.
	 *
	 * @param string $slug The project slug, eg: sfwd-lms.
	 * @param array  $api_data The plugins data, pulling from the api.
	 *
	 * @return false|array Return the project data as an array, or false if nothing found.
	 */
	public function look_project( string $slug, array $api_data ) {
		$installed = $this->get_installed_projects( $api_data );
		if ( isset( $installed[ $slug ] ) ) {
			return $installed[ $slug ];
		}

		$projects = $this->get_projects( $api_data );

		$project = $projects[ $slug ] ?? false;
		if ( false === $project ) {
			$premiums = $this->get_premium_projects( $api_data );
			$project  = $premiums[ $slug ] ?? false;
		}

		return $project;
	}

	/**
	 * Install a project
	 *
	 * @param string $slug The plugin folder name.
	 * @param bool   $is_update Update instead of install.
	 *
	 * @return bool|\WP_Error
	 */
	public function install( string $slug, bool $is_update = false ) {
		// prepare for install.
		include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
		include_once ABSPATH . 'wp-admin/includes/theme-install.php';
		include_once ABSPATH . 'wp-admin/includes/plugin.php';
		include_once ABSPATH . 'wp-admin/includes/file.php';

		$skin = new \WP_Ajax_Upgrader_Skin();

		$api = plugins_api(
			'plugin_information',
			array(
				'slug'   => sanitize_key( $slug ),
				'fields' => array( 'sections' => false ),
			)
		);

		if ( is_wp_error( $api ) ) {
			return false;
		}

		$upgrader = new \Plugin_Upgrader( $skin );
		if ( $is_update ) {
			$plugin_slug = $this->get_plugin_slug( $slug );
			$result      = $upgrader->upgrade( $plugin_slug );
		} else {
			$result = $upgrader->install( $api->download_url );
		}

		if ( true === $result ) {
			$this->install_translation( $api );
			// delete the update cache.
			delete_site_option( 'learndash_hub_fetch_projects' );
			delete_site_option( 'learndash_hub_update_plugins_cache' );
			if ( $is_update ) {
				$this->activate_plugin( $api->slug );
			}

			return true;
		}

		return new \WP_Error( 'err', implode( PHP_EOL, $upgrader->skin->get_upgrade_messages() ) );
	}

	/**
	 * Pull the translation from glotpress
	 *
	 * @param mixed $api
	 */
	private function install_translation( $api ) {
		// get plugin text domain.
		$slug = $this->get_plugin_slug( $api->slug );

		$wp_installed_languages = get_available_languages();
		if ( ! in_array( 'en_US', $wp_installed_languages, true ) ) {
			$wp_installed_languages = array_merge( array( 'en_US' ), $wp_installed_languages );
		}
		if ( ! empty( $slug ) ) {
			$plugin_data = get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug );
			if ( is_array( $plugin_data ) && isset( $plugin_data['TextDomain'] ) && ! empty( $plugin_data['TextDomain'] ) ) {
				// loop through the languages and download the locale files.
				$text_domain = $plugin_data['TextDomain'];
				$url         = add_query_arg(
					array(
						'ldlms-glotpress' => 1,
						'action'          => 'translation_sets',
						'project'         => $api->slug,
					),
					trailingslashit( 'https://translations.learndash.com/' )
				);
				$response    = wp_remote_get( $url );
				if ( ( is_array( $response ) ) && ( wp_remote_retrieve_response_code( $response ) === 200 ) ) {
					$response_body = wp_remote_retrieve_body( $response );

					if ( ! empty( $response_body ) ) {
						$ld_translation_sets = json_decode( $response_body, true );

						if ( ! $ld_translation_sets || ! isset( $ld_translation_sets[ $api->slug ] ) ) {
							return;
						}

						foreach ( $ld_translation_sets[ $api->slug ] as $key => $translate ) {
							foreach ( $wp_installed_languages as $locale ) {
								if ( $translate['wp_locale'] === $locale ) {
									foreach ( $translate['links'] as $ext => $link ) {
										$tmp_name = download_url( $link );
										if ( ! is_wp_error( $tmp_name ) ) {
											$language_dir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $api->slug . DIRECTORY_SEPARATOR . 'languages';
											if ( ! is_dir( $language_dir ) ) {
												wp_mkdir_p( $language_dir );
											}
											$path = $language_dir . DIRECTORY_SEPARATOR . $api->slug . '-' . $locale . '.' . $ext;
											rename( $tmp_name, $path );
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Get the plugin slug from folder name.
	 *
	 * @param string $slug The plugin folder.
	 * @param string $plugin_name The plugin name.
	 *
	 * @return string|bool
	 */
	public function get_plugin_slug( string $slug, string $plugin_name = '' ) {
		$plugins = get_plugins();

		// lookup the installed plugins.
		foreach ( $plugins as $folder_slug => $plugin ) {
			$base = dirname( $folder_slug );
			if ( $slug === $base ) {
				return $folder_slug;
			}
		}

		// normally it should not here, however if we are development then it will be chances
		// we use different slug.
		if ( ! defined( 'LEARNDASH_HUB_DEVELOPMENT' ) || empty( $plugin_name ) ) {
			return false;
		}

		// if nothing return, means the plugin is not installed, or use different path.
		foreach ( $plugins as $folder_slug => $plugin ) {
			if ( strtolower( $plugin['Author'] ) !== 'learndash' ) {
				continue;
			}

			if ( strtolower( $plugin_name ) === strtolower( $plugin['Name'] ) ) {
				return $folder_slug;
			}

			if ( $slug === 'learndash-hub' && $plugin['Name'] === 'LearnDash Licensing & Management' ) {
				return $folder_slug;
			}
		}

		return false;
	}

	/**
	 * @param string $plugin_version
	 * @param string $repo_version
	 *
	 * @return bool
	 */
	public function has_newer_version( string $plugin_version, string $repo_version ): bool {
		// since plugin version always sematic.
		$parts = explode( '.', $plugin_version );
		if ( count( $parts ) === 2 ) {
			// this means the plugins not PHP standard.
			$parts[]        = '0';
			$plugin_version = implode( '.', $parts );
		}

		return version_compare( $plugin_version, $repo_version, '<' );
	}
}
