<?php

defined( 'ABSPATH' ) || exit;

/**
 * Server Info module.
 *
 * Read-only environment dump for developers: server software, PHP build,
 * WordPress version + key constants, database, filesystem, and object-cache
 * status. No settings, no hooks — the module just renders a settings-tab
 * with six grouped tables and a "Copy summary" button that snapshots the
 * info into a plain-text block on the clipboard for pasting into a support
 * ticket or Slack thread.
 *
 * The tab is only ever rendered for users with `manage_options` (the
 * Settings → BW Dev page is already gated on that capability), so the
 * detailed environment info isn't exposed to lower-privilege users.
 *
 * @package BW_Dev
 */

class BW_Dev_Module_Server_Info implements BW_Dev_Module_Interface {

	public function slug(): string {
		return 'server_info';
	}

	public function label(): string {
		return __( 'Server Info', 'bw-dev' );
	}

	public function group(): string {
		return 'core';
	}

	public function default_settings(): array {
		return array();
	}

	public function sanitize( array $data ): array {
		return array();
	}

	public function register(): void {
		// No-op — the module is purely a settings-tab display.
	}

	/* ---------------------------------------------------------------------
	 * Data collection
	 * ------------------------------------------------------------------- */

	private function collect(): array {
		global $wpdb;

		$upload      = wp_upload_dir( null, false );
		$uploads_dir = isset( $upload['basedir'] ) ? (string) $upload['basedir'] : '';
		$wp_content  = defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : '';

		$disk_free  = function_exists( 'disk_free_space' ) ? @disk_free_space( ABSPATH ) : false; // phpcs:ignore WordPress.PHP.NoSilencedErrors
		$disk_total = function_exists( 'disk_total_space' ) ? @disk_total_space( ABSPATH ) : false; // phpcs:ignore WordPress.PHP.NoSilencedErrors

		$theme        = wp_get_theme();
		$active_pl    = (array) get_option( 'active_plugins', array() );
		$opcache_info = $this->opcache_summary();

		$themes_list  = $this->collect_themes();
		$plugins_list = $this->collect_plugins();

		return array(
			'server' => array(
				__( 'Server software', 'bw-dev' )       => isset( $_SERVER['SERVER_SOFTWARE'] ) ? (string) wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) : __( 'unknown', 'bw-dev' ),
				__( 'Hostname', 'bw-dev' )              => (string) gethostname(),
				__( 'Server name', 'bw-dev' )           => isset( $_SERVER['SERVER_NAME'] ) ? (string) wp_unslash( $_SERVER['SERVER_NAME'] ) : '—',
				__( 'OS', 'bw-dev' )                    => function_exists( 'php_uname' ) ? php_uname( 's' ) . ' ' . php_uname( 'r' ) . ' (' . php_uname( 'm' ) . ')' : PHP_OS,
				__( 'Current time (site tz)', 'bw-dev' ) => wp_date( 'Y-m-d H:i:s T' ),
			),
			'php' => array(
				__( 'PHP version', 'bw-dev' )           => PHP_VERSION,
				__( 'PHP SAPI', 'bw-dev' )              => php_sapi_name(),
				__( 'Memory limit', 'bw-dev' )          => (string) ini_get( 'memory_limit' ),
				__( 'Memory in use', 'bw-dev' )         => size_format( (int) memory_get_usage( true ), 2 ),
				__( 'Memory peak (this request)', 'bw-dev' ) => size_format( (int) memory_get_peak_usage( true ), 2 ),
				__( 'Max execution time', 'bw-dev' )    => (string) ini_get( 'max_execution_time' ) . 's',
				__( 'Max input vars', 'bw-dev' )        => (string) ini_get( 'max_input_vars' ),
				__( 'Upload max filesize', 'bw-dev' )   => (string) ini_get( 'upload_max_filesize' ),
				__( 'Post max size', 'bw-dev' )         => (string) ini_get( 'post_max_size' ),
				__( 'display_errors', 'bw-dev' )        => (string) ini_get( 'display_errors' ),
				__( 'OPcache', 'bw-dev' )               => $opcache_info,
				__( 'Default timezone', 'bw-dev' )      => date_default_timezone_get(),
			),
			'wordpress' => array(
				__( 'WordPress version', 'bw-dev' )     => get_bloginfo( 'version' ),
				__( 'Site URL', 'bw-dev' )              => site_url(),
				__( 'Home URL', 'bw-dev' )              => home_url(),
				__( 'Locale', 'bw-dev' )                => get_locale(),
				__( 'Charset', 'bw-dev' )               => (string) get_option( 'blog_charset' ),
				__( 'Permalink structure', 'bw-dev' )   => (string) get_option( 'permalink_structure' ) ?: __( '(plain)', 'bw-dev' ),
				__( 'Multisite', 'bw-dev' )             => is_multisite() ? __( 'yes', 'bw-dev' ) : __( 'no', 'bw-dev' ),
				__( 'Active theme', 'bw-dev' )          => $theme instanceof WP_Theme ? $theme->get( 'Name' ) . ' v' . $theme->get( 'Version' ) : __( 'unknown', 'bw-dev' ),
				__( 'Active plugins', 'bw-dev' )        => (string) count( $active_pl ),
				__( 'BW Dev version', 'bw-dev' )        => defined( 'BW_DEV_VERSION' ) ? BW_DEV_VERSION : __( 'unknown', 'bw-dev' ),
				'WP_DEBUG'                              => defined( 'WP_DEBUG' ) && WP_DEBUG ? 'true' : 'false',
				'WP_DEBUG_LOG'                          => defined( 'WP_DEBUG_LOG' ) ? var_export( WP_DEBUG_LOG, true ) : 'undefined', // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
				'WP_DEBUG_DISPLAY'                      => defined( 'WP_DEBUG_DISPLAY' ) ? var_export( WP_DEBUG_DISPLAY, true ) : 'undefined', // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
				'WP_MEMORY_LIMIT'                       => defined( 'WP_MEMORY_LIMIT' ) ? (string) WP_MEMORY_LIMIT : __( 'undefined', 'bw-dev' ),
				'WP_MAX_MEMORY_LIMIT'                   => defined( 'WP_MAX_MEMORY_LIMIT' ) ? (string) WP_MAX_MEMORY_LIMIT : __( 'undefined', 'bw-dev' ),
				'DISALLOW_FILE_EDIT'                    => defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ? 'true' : 'false',
				'AUTOMATIC_UPDATER_DISABLED'            => defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED ? 'true' : 'false',
				'DISABLE_WP_CRON'                       => defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ? 'true' : 'false',
			),
			'themes'   => $themes_list,
			'plugins'  => $plugins_list,
			'database' => array(
				__( 'Database host', 'bw-dev' )         => defined( 'DB_HOST' ) ? DB_HOST : '—',
				__( 'Database name', 'bw-dev' )         => defined( 'DB_NAME' ) ? DB_NAME : '—',
				__( 'Server version', 'bw-dev' )        => $wpdb instanceof wpdb ? (string) $wpdb->db_version() : '—',
				__( 'Charset', 'bw-dev' )               => $wpdb instanceof wpdb ? (string) $wpdb->charset : '—',
				__( 'Collation', 'bw-dev' )             => $wpdb instanceof wpdb ? (string) $wpdb->collate : '—',
				__( 'Table prefix', 'bw-dev' )          => $wpdb instanceof wpdb ? (string) $wpdb->prefix : '—',
			),
			'filesystem' => array(
				__( 'ABSPATH', 'bw-dev' )               => ABSPATH,
				__( 'wp-content path', 'bw-dev' )       => $wp_content,
				__( 'Uploads path', 'bw-dev' )          => $uploads_dir,
				__( 'wp-content writable', 'bw-dev' )   => '' !== $wp_content && wp_is_writable( $wp_content ) ? __( 'yes', 'bw-dev' ) : __( 'no', 'bw-dev' ),
				__( 'Uploads writable', 'bw-dev' )      => '' !== $uploads_dir && wp_is_writable( $uploads_dir ) ? __( 'yes', 'bw-dev' ) : __( 'no', 'bw-dev' ),
				__( 'Disk free (mount)', 'bw-dev' )     => false !== $disk_free ? size_format( (int) $disk_free, 2 ) : __( 'unavailable', 'bw-dev' ),
				__( 'Disk total (mount)', 'bw-dev' )    => false !== $disk_total ? size_format( (int) $disk_total, 2 ) : __( 'unavailable', 'bw-dev' ),
			),
			'cache' => array(
				__( 'External object cache', 'bw-dev' ) => wp_using_ext_object_cache() ? __( 'yes', 'bw-dev' ) : __( 'no', 'bw-dev' ),
				__( 'Persistent cache class', 'bw-dev' ) => $this->object_cache_class(),
				__( 'wp-cron active', 'bw-dev' )         => ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) ? __( 'no (system cron)', 'bw-dev' ) : __( 'yes (WP-Cron)', 'bw-dev' ),
			),
		);
	}

	private function opcache_summary(): string {
		if ( ! function_exists( 'opcache_get_status' ) ) {
			return __( 'unavailable', 'bw-dev' );
		}
		$status = @opcache_get_status( false ); // phpcs:ignore WordPress.PHP.NoSilencedErrors
		if ( ! is_array( $status ) || empty( $status['opcache_enabled'] ) ) {
			return __( 'disabled', 'bw-dev' );
		}
		$used   = isset( $status['memory_usage']['used_memory'] ) ? (int) $status['memory_usage']['used_memory'] : 0;
		$free   = isset( $status['memory_usage']['free_memory'] ) ? (int) $status['memory_usage']['free_memory'] : 0;
		$total  = $used + $free;
		$cached = isset( $status['opcache_statistics']['num_cached_scripts'] ) ? (int) $status['opcache_statistics']['num_cached_scripts'] : 0;
		return sprintf(
			/* translators: 1: used memory, 2: total memory, 3: cached script count */
			__( 'enabled — %1$s / %2$s used, %3$d scripts cached', 'bw-dev' ),
			size_format( $used, 1 ),
			size_format( $total, 1 ),
			$cached
		);
	}

	/**
	 * Build a [ stylesheet-slug => "Name vVersion — status" ] map for every
	 * installed theme. Active theme listed first, then its parent (for child
	 * themes), then everything else alphabetically by name.
	 */
	private function collect_themes(): array {
		$all              = wp_get_themes();
		$active           = wp_get_theme();
		$active_slug      = $active instanceof WP_Theme ? $active->get_stylesheet() : '';
		$parent_slug      = $active instanceof WP_Theme ? $active->get_template() : '';
		$has_parent       = $active_slug !== $parent_slug;

		$rows = array();
		foreach ( $all as $slug => $theme ) {
			if ( ! ( $theme instanceof WP_Theme ) ) {
				continue;
			}
			if ( $slug === $active_slug ) {
				$status = __( 'active', 'bw-dev' );
			} elseif ( $has_parent && $slug === $parent_slug ) {
				$status = __( 'active (parent)', 'bw-dev' );
			} else {
				$status = __( 'installed', 'bw-dev' );
			}
			$key   = '[' . $status . '] ' . $slug;
			$value = $theme->get( 'Name' ) . ' v' . $theme->get( 'Version' );
			$rows[ $key ] = array(
				'sort_status' => $slug === $active_slug ? 0 : ( $has_parent && $slug === $parent_slug ? 1 : 2 ),
				'sort_name'   => strtolower( (string) $theme->get( 'Name' ) ),
				'key'         => $key,
				'value'       => $value,
			);
		}

		uasort(
			$rows,
			static function ( $a, $b ) {
				if ( $a['sort_status'] !== $b['sort_status'] ) {
					return $a['sort_status'] <=> $b['sort_status'];
				}
				return strcmp( $a['sort_name'], $b['sort_name'] );
			}
		);

		$out = array();
		foreach ( $rows as $r ) {
			$out[ $r['key'] ] = $r['value'];
		}
		return $out;
	}

	/**
	 * Build a [ "[status] plugin-file" => "Name vVersion" ] map for every
	 * installed plugin. Active plugins first, then inactive, alphabetised
	 * by name within each group.
	 */
	private function collect_plugins(): array {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		$all    = get_plugins();
		$active = (array) get_option( 'active_plugins', array() );

		// Network-active plugins on multisite.
		$network = is_multisite() ? array_keys( (array) get_site_option( 'active_sitewide_plugins', array() ) ) : array();

		$rows = array();
		foreach ( $all as $file => $data ) {
			if ( in_array( $file, $network, true ) ) {
				$status     = __( 'network-active', 'bw-dev' );
				$sort_order = 0;
			} elseif ( in_array( $file, $active, true ) ) {
				$status     = __( 'active', 'bw-dev' );
				$sort_order = 1;
			} else {
				$status     = __( 'inactive', 'bw-dev' );
				$sort_order = 2;
			}
			$name        = isset( $data['Name'] ) ? (string) $data['Name'] : $file;
			$version     = isset( $data['Version'] ) ? (string) $data['Version'] : '?';
			$key         = '[' . $status . '] ' . $file;
			$value       = $name . ' v' . $version;
			$rows[ $key ] = array(
				'sort_order' => $sort_order,
				'sort_name'  => strtolower( $name ),
				'key'        => $key,
				'value'      => $value,
			);
		}

		uasort(
			$rows,
			static function ( $a, $b ) {
				if ( $a['sort_order'] !== $b['sort_order'] ) {
					return $a['sort_order'] <=> $b['sort_order'];
				}
				return strcmp( $a['sort_name'], $b['sort_name'] );
			}
		);

		$out = array();
		foreach ( $rows as $r ) {
			$out[ $r['key'] ] = $r['value'];
		}
		return $out;
	}

	private function object_cache_class(): string {
		if ( ! wp_using_ext_object_cache() ) {
			return __( 'WP_Object_Cache (in-memory only)', 'bw-dev' );
		}
		global $wp_object_cache;
		if ( is_object( $wp_object_cache ) ) {
			return get_class( $wp_object_cache );
		}
		return __( 'unknown', 'bw-dev' );
	}

	/* ---------------------------------------------------------------------
	 * Settings tab
	 * ------------------------------------------------------------------- */

	public function render_tab(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		$sections = $this->collect();

		$section_labels = array(
			'server'     => __( 'Server', 'bw-dev' ),
			'php'        => __( 'PHP', 'bw-dev' ),
			'wordpress'  => __( 'WordPress', 'bw-dev' ),
			'themes'     => __( 'Themes', 'bw-dev' ),
			'plugins'    => __( 'Plugins', 'bw-dev' ),
			'database'   => __( 'Database', 'bw-dev' ),
			'filesystem' => __( 'Filesystem', 'bw-dev' ),
			'cache'      => __( 'Object cache + cron', 'bw-dev' ),
		);
		?>
		<p class="description">
			<?php esc_html_e( 'Read-only environment snapshot. Useful for diagnostics and support tickets. No settings — purely a display.', 'bw-dev' ); ?>
		</p>

		<?php foreach ( $section_labels as $key => $label ) : ?>
			<h3 style="margin-top:24px;"><?php echo esc_html( $label ); ?></h3>
			<table class="widefat striped" style="max-width:960px;">
				<tbody>
					<?php foreach ( $sections[ $key ] as $row_label => $value ) : ?>
						<tr>
							<th scope="row" style="width:240px;vertical-align:top;padding:8px 12px;"><?php echo esc_html( $row_label ); ?></th>
							<td style="padding:8px 12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;color:#1d2327;word-break:break-all;"><?php echo esc_html( (string) $value ); ?></td>
						</tr>
					<?php endforeach; ?>
				</tbody>
			</table>
		<?php endforeach; ?>

		<h3 style="margin-top:30px;"><?php esc_html_e( 'Copy as plain text', 'bw-dev' ); ?></h3>
		<p class="description"><?php esc_html_e( 'Paste into a support ticket or Slack thread. The textarea below holds the same data in a one-line-per-row format.', 'bw-dev' ); ?></p>
		<?php $summary = $this->summary_text( $sections, $section_labels ); ?>
		<textarea id="bw-dev-server-info-summary" readonly rows="14" style="width:100%;max-width:960px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;"><?php echo esc_textarea( $summary ); ?></textarea>
		<p style="margin-top:8px;">
			<button type="button" class="button button-secondary" id="bw-dev-server-info-copy"><?php esc_html_e( 'Copy to clipboard', 'bw-dev' ); ?></button>
			<span id="bw-dev-server-info-copy-status" style="margin-left:10px;color:#00a32a;font-weight:500;display:none;"><?php esc_html_e( 'Copied!', 'bw-dev' ); ?></span>
		</p>

		<script>
		(function () {
			var btn  = document.getElementById('bw-dev-server-info-copy');
			var ta   = document.getElementById('bw-dev-server-info-summary');
			var note = document.getElementById('bw-dev-server-info-copy-status');
			if (!btn || !ta) return;
			btn.addEventListener('click', function (e) {
				e.preventDefault();
				ta.select();
				ta.setSelectionRange(0, ta.value.length);
				var ok = false;
				try {
					if (navigator.clipboard && navigator.clipboard.writeText) {
						navigator.clipboard.writeText(ta.value);
						ok = true;
					} else {
						ok = document.execCommand('copy');
					}
				} catch (err) {
					ok = false;
				}
				if (ok && note) {
					note.style.display = 'inline';
					setTimeout(function () { note.style.display = 'none'; }, 1800);
				}
			});
		})();
		</script>
		<?php
	}

	private function summary_text( array $sections, array $labels ): string {
		$lines   = array();
		$lines[] = '# BW Dev — Server Info — ' . wp_date( 'Y-m-d H:i:s T' );
		$lines[] = '';
		foreach ( $labels as $key => $label ) {
			$lines[] = '## ' . $label;
			foreach ( $sections[ $key ] as $row_label => $value ) {
				$lines[] = $row_label . ': ' . (string) $value;
			}
			$lines[] = '';
		}
		return implode( "\n", $lines );
	}

	public function uninstall(): void {
		// No persisted state.
	}
}
