<?php

namespace RemoveUnusedThemesPlugins;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\NetworkScope;

/**
 * Remove Unused Themes and Plugins module.
 * Scan for inactive themes/plugins and remove them with confirmation.
 */
class Module extends AbstractModule {

	private const MODULE_ID      = 'remove-unused-themes-plugins';
	private const MODULE_VERSION = '1.0.0';

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Remove Unused Themes and Plugins', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'One-click removal of inactive plugins and themes to declutter and harden the install.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'admin', 'ajax' ];
	}

	public function getDependencies(): array {
		return [];
	}

	protected function registerHooks(): void {
		if ( ! is_admin() && ! wp_doing_ajax() ) {
			return;
		}
		add_action( 'wp_ajax_wpps_ru_scan_inactive', [ $this, 'ajaxScanInactive' ] );
		add_action( 'wp_ajax_wpps_ru_remove_inactive', [ $this, 'ajaxRemoveInactive' ] );
	}

	/**
	 * Plugin files active on any site in the network (or current site on single-site).
	 *
	 * @return array<int, string>
	 */
	private function getActivePluginFiles(): array {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$active = [];

		if ( is_multisite() ) {
			$network_active = (array) get_site_option( 'active_sitewide_plugins', [] );
			$active         = array_merge( $active, array_keys( $network_active ) );

			$site_ids = get_sites(
				[
					'number' => 0,
					'fields' => 'ids',
				]
			);
			foreach ( $site_ids as $blog_id ) {
				switch_to_blog( (int) $blog_id );
				$active = array_merge( $active, (array) get_option( 'active_plugins', [] ) );
				restore_current_blog();
			}
		} else {
			$active = (array) get_option( 'active_plugins', [] );
		}

		return array_values( array_unique( $active ) );
	}

	/**
	 * Theme slugs in use on any site (stylesheet + template for child themes).
	 *
	 * @return array<int, string>
	 */
	private function getActiveThemeSlugs(): array {
		$slugs = [];

		if ( is_multisite() ) {
			$site_ids = get_sites(
				[
					'number' => 0,
					'fields' => 'ids',
				]
			);
			foreach ( $site_ids as $blog_id ) {
				switch_to_blog( (int) $blog_id );
				$slugs[] = get_stylesheet();
				$slugs[] = get_template();
				restore_current_blog();
			}
		} else {
			$slugs[] = get_stylesheet();
			$slugs[] = get_template();
		}

		return array_values( array_unique( array_filter( $slugs ) ) );
	}

	/**
	 * Inactive plugin files required by currently active plugins (RequiresPlugins header / WP API).
	 *
	 * @param array<int, string> $active_files
	 * @return array<int, string>
	 */
	private function getRequiredPluginFiles( array $active_files ): array {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$required  = [];
		$all_files = array_keys( get_plugins() );

		if ( class_exists( 'WP_Plugin_Dependencies' ) ) {
			if ( method_exists( 'WP_Plugin_Dependencies', 'initialize' ) ) {
				\WP_Plugin_Dependencies::initialize();
			}
			foreach ( $active_files as $file ) {
				if ( ! is_string( $file ) || $file === '' ) {
					continue;
				}
				if ( ! method_exists( 'WP_Plugin_Dependencies', 'get_dependencies' ) ) {
					break;
				}
				$slugs = \WP_Plugin_Dependencies::get_dependencies( $file );
				if ( ! is_array( $slugs ) ) {
					continue;
				}
				foreach ( $slugs as $slug ) {
					if ( ! is_string( $slug ) || $slug === '' ) {
						continue;
					}
					if ( method_exists( 'WP_Plugin_Dependencies', 'get_dependency_filepath' ) ) {
						$dep_file = \WP_Plugin_Dependencies::get_dependency_filepath( $slug );
						if ( is_string( $dep_file ) && $dep_file !== '' ) {
							$required[] = $dep_file;
							continue;
						}
					}
					foreach ( $all_files as $candidate ) {
						$candidate_slug = dirname( $candidate );
						if ( $candidate_slug === '.' ) {
							$candidate_slug = basename( $candidate, '.php' );
						}
						if ( $candidate_slug === $slug ) {
							$required[] = $candidate;
							break;
						}
					}
				}
			}
		}

		foreach ( $active_files as $file ) {
			if ( ! is_string( $file ) || $file === '' ) {
				continue;
			}
			$plugin_path = WP_PLUGIN_DIR . '/' . $file;
			if ( ! is_readable( $plugin_path ) ) {
				continue;
			}
			$headers  = get_plugin_data( $plugin_path, false, false );
			$requires = isset( $headers['RequiresPlugins'] ) ? (string) $headers['RequiresPlugins'] : '';
			if ( $requires === '' ) {
				continue;
			}
			$slugs = array_filter( array_map( 'trim', explode( ',', $requires ) ) );
			foreach ( $all_files as $candidate ) {
				$slug = dirname( $candidate );
				if ( $slug === '.' ) {
					$slug = basename( $candidate, '.php' );
				}
				if ( in_array( $slug, $slugs, true ) ) {
					$required[] = $candidate;
				}
			}
		}

		return array_values( array_unique( $required ) );
	}

	/**
	 * Get list of inactive plugins safe to remove.
	 *
	 * @return array<int, array{file: string, name: string, version: string, author: string}>
	 */
	public function getInactivePlugins(): array {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$all_plugins  = get_plugins();
		$active_files = $this->getActivePluginFiles();
		$protected    = array_unique(
			array_merge(
				$active_files,
				$this->getRequiredPluginFiles( $active_files )
			)
		);

		$inactive = [];
		foreach ( $all_plugins as $file => $data ) {
			if ( in_array( $file, $protected, true ) ) {
				continue;
			}
			$inactive[] = [
				'file'    => $file,
				'name'    => isset( $data['Name'] ) ? $data['Name'] : $file,
				'version' => isset( $data['Version'] ) ? $data['Version'] : '',
				'author'  => isset( $data['AuthorName'] ) ? $data['AuthorName'] : ( $data['Author'] ?? '' ),
			];
		}

		return $inactive;
	}

	/**
	 * Get list of inactive themes safe to remove.
	 *
	 * @return array<int, array{slug: string, name: string, version: string, author: string}>
	 */
	public function getInactiveThemes(): array {
		$all_themes = wp_get_themes();
		$protected  = $this->getActiveThemeSlugs();
		$inactive   = [];

		foreach ( $all_themes as $slug => $theme ) {
			if ( in_array( $slug, $protected, true ) ) {
				continue;
			}
			if ( ! is_object( $theme ) ) {
				continue;
			}
			$inactive[] = [
				'slug'    => $slug,
				'name'    => $theme->get( 'Name' ) ?: $slug,
				'version' => $theme->get( 'Version' ) ?: '',
				'author'  => $theme->get( 'Author' ) ?: '',
			];
		}

		return $inactive;
	}

	/**
	 * @return true|\WP_Error
	 */
	private function assertCanUseModule(): bool|\WP_Error {
		if ( ! current_user_can( NetworkScope::adminCapability() ) ) {
			return new \WP_Error(
				'insufficient_permissions',
				__( 'You do not have permission to manage WP PowerSuite.', 'wp-powersuite' )
			);
		}
		if ( ! current_user_can( 'delete_plugins' ) && ! current_user_can( 'delete_themes' ) ) {
			return new \WP_Error(
				'insufficient_permissions',
				__( 'You do not have permission to delete plugins or themes.', 'wp-powersuite' )
			);
		}
		return true;
	}

	/**
	 * @param 'plugins'|'themes'|'all'|'selected' $type
	 * @param array<int, string>                  $plugin_files
	 * @param array<int, string>                  $theme_slugs
	 * @return true|\WP_Error
	 */
	private function assertCanDeleteType( string $type, array $plugin_files = [], array $theme_slugs = [] ): bool|\WP_Error {
		$needs_plugins = in_array( $type, [ 'all', 'plugins' ], true )
			|| ( $type === 'selected' && $plugin_files !== [] );
		$needs_themes  = in_array( $type, [ 'all', 'themes' ], true )
			|| ( $type === 'selected' && $theme_slugs !== [] );

		if ( $needs_plugins && ! current_user_can( 'delete_plugins' ) ) {
			return new \WP_Error(
				'insufficient_permissions',
				__( 'You do not have permission to delete plugins.', 'wp-powersuite' )
			);
		}
		if ( $needs_themes && ! current_user_can( 'delete_themes' ) ) {
			return new \WP_Error(
				'insufficient_permissions',
				__( 'You do not have permission to delete themes.', 'wp-powersuite' )
			);
		}
		return true;
	}

	/**
	 * AJAX: Scan for inactive items.
	 */
	public function ajaxScanInactive(): void {
		check_ajax_referer( 'wpps_ru_scan', 'nonce' );
		if ( ! $this->isEnabled() ) {
			wp_send_json_error( [ 'message' => __( 'Module is not enabled.', 'wp-powersuite' ) ] );
		}

		$access = $this->assertCanUseModule();
		if ( is_wp_error( $access ) ) {
			wp_send_json_error( [ 'message' => $access->get_error_message() ] );
		}

		try {
			@set_time_limit( 120 ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Multisite scans can be slow.
			$plugins = current_user_can( 'delete_plugins' ) ? $this->getInactivePlugins() : [];
			$themes  = current_user_can( 'delete_themes' ) ? $this->getInactiveThemes() : [];
		} catch ( \Throwable $e ) {
			wp_send_json_error(
				[
					'message' => __( 'Scan failed. Please try again or check the error log.', 'wp-powersuite' ),
					'code'    => 'scan_failed',
				]
			);
		}

		wp_send_json_success(
			[
				'inactive_plugins' => $plugins,
				'inactive_themes'  => $themes,
			]
		);
	}

	/**
	 * AJAX: Remove inactive items (all, plugins only, themes only, or selected by list).
	 */
	public function ajaxRemoveInactive(): void {
		check_ajax_referer( 'wpps_ru_remove', 'nonce' );
		if ( ! $this->isEnabled() ) {
			wp_send_json_error( [ 'message' => __( 'Module is not enabled.', 'wp-powersuite' ) ] );
		}

		$access = $this->assertCanUseModule();
		if ( is_wp_error( $access ) ) {
			wp_send_json_error( [ 'message' => $access->get_error_message() ] );
		}

		$type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
		if ( ! in_array( $type, [ 'all', 'plugins', 'themes', 'selected' ], true ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid removal type.', 'wp-powersuite' ) ] );
		}

		$plugin_files = [];
		$theme_slugs  = [];

		if ( $type === 'selected' ) {
			$plugin_files = isset( $_POST['plugin_files'] ) && is_array( $_POST['plugin_files'] )
				? array_map( 'sanitize_text_field', array_map( 'wp_unslash', $_POST['plugin_files'] ) )
				: [];
			$theme_slugs = isset( $_POST['theme_slugs'] ) && is_array( $_POST['theme_slugs'] )
				? array_map( 'sanitize_text_field', array_map( 'wp_unslash', $_POST['theme_slugs'] ) )
				: [];
			$plugin_files = array_values( array_filter( $plugin_files ) );
			$theme_slugs  = array_values( array_filter( $theme_slugs ) );
			$plugin_files = array_values(
				array_filter(
					$plugin_files,
					static function ( $f ) {
						return strpos( $f, '..' ) === false && strpos( $f, '\\' ) === false && strpos( $f, '/' ) !== 0;
					}
				)
			);
			$theme_slugs = array_values(
				array_filter(
					$theme_slugs,
					static function ( $s ) {
						return preg_match( '/^[a-z0-9\-_]+$/i', $s ) === 1;
					}
				)
			);
			if ( $plugin_files === [] && $theme_slugs === [] ) {
				wp_send_json_error( [ 'message' => __( 'No items selected.', 'wp-powersuite' ) ] );
			}
		}

		$delete_access = $this->assertCanDeleteType( $type, $plugin_files, $theme_slugs );
		if ( is_wp_error( $delete_access ) ) {
			wp_send_json_error( [ 'message' => $delete_access->get_error_message() ] );
		}

		$deleted_plugins = [];
		$deleted_themes  = [];
		$failed          = [];

		$allowed_plugins = [];
		foreach ( $this->getInactivePlugins() as $p ) {
			$allowed_plugins[ $p['file'] ] = $p;
		}
		$allowed_themes = [];
		foreach ( $this->getInactiveThemes() as $t ) {
			$allowed_themes[ $t['slug'] ] = $t;
		}

		if ( $type === 'selected' ) {
			foreach ( $plugin_files as $file ) {
				if ( ! isset( $allowed_plugins[ $file ] ) ) {
					$failed[] = [
						'type'    => 'plugin',
						'name'    => $file,
						'message' => __( 'Plugin is not inactive or is protected from deletion.', 'wp-powersuite' ),
					];
					continue;
				}
				$p      = $allowed_plugins[ $file ];
				$result = $this->deletePlugin( $file );
				if ( $result === true ) {
					$deleted_plugins[] = $p['name'];
				} else {
					$failed[] = [
						'type'    => 'plugin',
						'name'    => $p['name'],
						'message' => is_wp_error( $result ) ? $result->get_error_message() : __( 'Deletion failed or filesystem credentials required.', 'wp-powersuite' ),
					];
				}
			}
			foreach ( $theme_slugs as $slug ) {
				if ( ! isset( $allowed_themes[ $slug ] ) ) {
					$failed[] = [
						'type'    => 'theme',
						'name'    => $slug,
						'message' => __( 'Theme is not inactive or is protected from deletion.', 'wp-powersuite' ),
					];
					continue;
				}
				$t      = $allowed_themes[ $slug ];
				$result = $this->deleteTheme( $slug );
				if ( $result === true ) {
					$deleted_themes[] = $t['name'];
				} else {
					$failed[] = [
						'type'    => 'theme',
						'name'    => $t['name'],
						'message' => is_wp_error( $result ) ? $result->get_error_message() : __( 'Deletion failed or filesystem credentials required.', 'wp-powersuite' ),
					];
				}
			}
		} else {
			if ( $type === 'all' || $type === 'plugins' ) {
				foreach ( $allowed_plugins as $p ) {
					$result = $this->deletePlugin( $p['file'] );
					if ( $result === true ) {
						$deleted_plugins[] = $p['name'];
					} else {
						$failed[] = [
							'type'    => 'plugin',
							'name'    => $p['name'],
							'message' => is_wp_error( $result ) ? $result->get_error_message() : __( 'Deletion failed or filesystem credentials required.', 'wp-powersuite' ),
						];
					}
				}
			}

			if ( $type === 'all' || $type === 'themes' ) {
				foreach ( $allowed_themes as $t ) {
					$result = $this->deleteTheme( $t['slug'] );
					if ( $result === true ) {
						$deleted_themes[] = $t['name'];
					} else {
						$failed[] = [
							'type'    => 'theme',
							'name'    => $t['name'],
							'message' => is_wp_error( $result ) ? $result->get_error_message() : __( 'Deletion failed or filesystem credentials required.', 'wp-powersuite' ),
						];
					}
				}
			}
		}

		wp_send_json_success(
			[
				'deleted_plugins' => $deleted_plugins,
				'deleted_themes'  => $deleted_themes,
				'failed'          => $failed,
				'total_deleted'   => count( $deleted_plugins ) + count( $deleted_themes ),
			]
		);
	}

	/**
	 * Delete a single plugin by its file path (relative to wp-content/plugins).
	 *
	 * @param string $plugin_file Plugin file path e.g. 'akismet/akismet.php'.
	 * @return true|\WP_Error|null True on success, WP_Error on failure, null if filesystem credentials needed.
	 */
	private function deletePlugin( string $plugin_file ) {
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		$result = delete_plugins( [ $plugin_file ] );
		if ( $result === null ) {
			return null;
		}
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		return true;
	}

	/**
	 * Delete a single theme by slug.
	 *
	 * @param string $slug Theme stylesheet slug.
	 * @return true|\WP_Error|null True on success, WP_Error on failure, null if filesystem credentials needed.
	 */
	private function deleteTheme( string $slug ) {
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/theme.php';
		$result = delete_theme( $slug );
		if ( $result === null ) {
			return null;
		}
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		return true;
	}
}
