<?php

namespace QuickDebugToggle;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\WpConfigHelper;

class Module extends AbstractModule {

	private const MODULE_ID = 'quick-debug-toggle';
	private const MODULE_VERSION = '1.0.0';
	private const MARKER_BEGIN = '// WP PowerSuite Quick Debug Toggle - begin';
	private const MARKER_END   = '// WP PowerSuite Quick Debug Toggle - end';
	private const STOP_EDITING = "/* That's all, stop editing!";
	private const DEBUG_CONSTANTS = [ 'WP_DEBUG', 'WP_DEBUG_LOG', 'WP_DEBUG_DISPLAY' ];

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

	public function getName(): string {
		return __( 'Quick Debug Toggle', 'wp-powersuite' );
	}

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

	public function getDescription(): string {
		return __( 'Toggle WP_DEBUG-style logging from the admin for faster troubleshooting.', 'wp-powersuite' );
	}

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

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

	protected function registerHooks(): void {
		// No hooks; UI and AJAX handlers only.
	}

	public function getConfigPath(): ?string {
		return WpConfigHelper::resolveConfigPath();
	}

	public function isConfigWritable(): bool {
		$path = $this->getConfigPath();
		return $path && wp_is_writable( $path );
	}

	private function readConfigContent( string $path ): string|false {
		$read = WpConfigHelper::readFileContents( $path );
		return $read['success'] ? $read['content'] : false;
	}

	public function hasOurDebugBlock(): bool {
		$path = $this->getConfigPath();
		if ( ! $path ) {
			return false;
		}

		$content = $this->readConfigContent( $path );
		return $content !== false && strpos( $content, self::MARKER_BEGIN ) !== false;
	}

	public function hasExistingDebugDefines(): bool {
		$path = $this->getConfigPath();
		if ( ! $path ) {
			return false;
		}

		$content = $this->readConfigContent( $path );
		if ( $content === false ) {
			return false;
		}

		$without = preg_replace(
			'/\s*' . preg_quote( self::MARKER_BEGIN, '/' ) . '.*?' . preg_quote( self::MARKER_END, '/' ) . '\s*/s',
			'',
			$content
		);

		return preg_match( '/define\s*\(\s*[\'"](?:WP_DEBUG|WP_DEBUG_LOG|WP_DEBUG_DISPLAY)[\'"]\s*,/i', $without ?? '' ) === 1;
	}

	public function getDebugStatus(): array {
		return [
			'wp_debug'         => defined( 'WP_DEBUG' ) && WP_DEBUG,
			'wp_debug_log'     => defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG,
			'wp_debug_display' => defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY,
		];
	}

	public function enableDebug(): array {
		$path = $this->getConfigPath();
		if ( ! $path ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php not found.', 'wp-powersuite' ) ];
		}
		if ( ! $this->isConfigWritable() ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php is not writable.', 'wp-powersuite' ) ];
		}
		if ( $this->hasOurDebugBlock() ) {
			return [ 'success' => true, 'message' => __( 'Debug mode is already enabled by this module.', 'wp-powersuite' ) ];
		}
		if ( $this->hasExistingDebugDefines() ) {
			return [
				'success' => false,
				'message' => __( 'Debug constants already exist in wp-config.php. Use the per-constant toggles above to manage them, or remove them first to use automatic enable.', 'wp-powersuite' ),
			];
		}

		$content = $this->readConfigContent( $path );
		if ( $content === false ) {
			return [ 'success' => false, 'message' => __( 'Could not read wp-config.php.', 'wp-powersuite' ) ];
		}

		$block = "\n" . self::MARKER_BEGIN . "\n" . $this->getDebugBlock() . "\n" . self::MARKER_END . "\n";

		$pos = strpos( $content, self::STOP_EDITING );
		if ( $pos === false ) {
			$content = rtrim( $content ) . $block . "\n";
		} else {
			$content = substr_replace( $content, $block, $pos, 0 );
		}

		$write = WpConfigHelper::writeValidatedConfigSafely( $path, $content, 'debug' );
		if ( ! $write['success'] ) {
			return [
				'success' => false,
				'message' => $write['message'],
			];
		}

		return [ 'success' => true, 'message' => __( 'Debug mode enabled. A backup was created.', 'wp-powersuite' ) ];
	}

	public function disableDebug(): array {
		$path = $this->getConfigPath();
		if ( ! $path ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php not found.', 'wp-powersuite' ) ];
		}
		if ( ! $this->isConfigWritable() ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php is not writable.', 'wp-powersuite' ) ];
		}
		if ( ! $this->hasOurDebugBlock() ) {
			return [ 'success' => true, 'message' => __( 'Debug block was not added by this module. Nothing to remove.', 'wp-powersuite' ) ];
		}

		$content = $this->readConfigContent( $path );
		if ( $content === false ) {
			return [ 'success' => false, 'message' => __( 'Could not read wp-config.php.', 'wp-powersuite' ) ];
		}

		$pattern = '/\s*' . preg_quote( self::MARKER_BEGIN, '/' ) . '.*?' . preg_quote( self::MARKER_END, '/' ) . '\s*/s';
		$new     = preg_replace( $pattern, "\n", $content );
		if ( $new === null || $new === $content ) {
			return [ 'success' => false, 'message' => __( 'Could not remove debug block from wp-config.php.', 'wp-powersuite' ) ];
		}

		$write = WpConfigHelper::writeValidatedConfigSafely( $path, $new, 'debug' );
		if ( ! $write['success'] ) {
			return [
				'success' => false,
				'message' => $write['message'],
			];
		}

		return [ 'success' => true, 'message' => __( 'Debug mode disabled.', 'wp-powersuite' ) ];
	}

	public function getDebugBlock(): string {
		return "define( 'WP_DEBUG', true );\ndefine( 'WP_DEBUG_LOG', true );\ndefine( 'WP_DEBUG_DISPLAY', false );\n@ini_set( 'display_errors', 0 );";
	}

	public function toggleConstant( string $constant, bool $value ): array {
		if ( ! in_array( $constant, self::DEBUG_CONSTANTS, true ) ) {
			return [ 'success' => false, 'message' => __( 'Invalid constant.', 'wp-powersuite' ) ];
		}

		$path = $this->getConfigPath();
		if ( ! $path ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php not found.', 'wp-powersuite' ) ];
		}
		if ( ! $this->isConfigWritable() ) {
			return [ 'success' => false, 'message' => __( 'wp-config.php is not writable.', 'wp-powersuite' ) ];
		}

		if ( $this->hasOurDebugBlock() ) {
			return $this->replaceDebugConstant( $path, $constant, $value, true );
		}

		if ( $this->hasExistingDebugDefines() ) {
			return $this->replaceDebugConstant( $path, $constant, $value, false );
		}

		return [ 'success' => false, 'message' => __( 'No debug constants found in wp-config.php. Enable debug mode first.', 'wp-powersuite' ) ];
	}

	/**
	 * @return array{success: bool, message: string}
	 */
	private function replaceDebugConstant( string $path, string $constant, bool $value, bool $scoped_to_block ): array {
		$content = $this->readConfigContent( $path );
		if ( $content === false ) {
			return [ 'success' => false, 'message' => __( 'Could not read wp-config.php.', 'wp-powersuite' ) ];
		}

		$search  = $content;
		$pattern = '/define\s*\(\s*[\'"]' . preg_quote( $constant, '/' ) . '[\'"]\s*,\s*(?:true|false|1|0)\s*\)/i';

		if ( $scoped_to_block ) {
			if ( ! preg_match( '/\s*' . preg_quote( self::MARKER_BEGIN, '/' ) . '.*?' . preg_quote( self::MARKER_END, '/' ) . '\s*/s', $content, $block_match ) ) {
				return [ 'success' => false, 'message' => __( 'Debug block not found.', 'wp-powersuite' ) ];
			}
			$search = $block_match[0];
		}

		if ( ! preg_match( $pattern, $search ) ) {
			return [ 'success' => false, 'message' => __( 'Could not find the constant in wp-config.php.', 'wp-powersuite' ) ];
		}

		$replacement = "define( '" . $constant . "', " . ( $value ? 'true' : 'false' ) . " )";
		$new_search  = preg_replace( $pattern, $replacement, $search, 1 );
		if ( ! is_string( $new_search ) || $new_search === $search ) {
			return [ 'success' => false, 'message' => __( 'Could not update constant in wp-config.php.', 'wp-powersuite' ) ];
		}

		$new = $scoped_to_block
			? str_replace( $search, $new_search, $content )
			: $new_search;

		$write = WpConfigHelper::writeValidatedConfigSafely( $path, $new, 'debug' );
		if ( ! $write['success'] ) {
			return [
				'success' => false,
				'message' => $write['message'],
			];
		}

		return [
			'success' => true,
			'message' => $constant . ' ' . ( $value ? __( 'enabled', 'wp-powersuite' ) : __( 'disabled', 'wp-powersuite' ) ),
		];
	}

	public function protectDebugLog(): array {
		return WpConfigHelper::protectDebugLogAccess();
	}

	public function clearDebugLog(): array {
		$log_file = WpConfigHelper::resolveDebugLogPath();
		if ( ! file_exists( $log_file ) ) {
			return [ 'success' => false, 'message' => __( 'Debug log file not found.', 'wp-powersuite' ) ];
		}
		if ( ! wp_is_writable( $log_file ) ) {
			return [ 'success' => false, 'message' => __( 'Debug log file is not writable.', 'wp-powersuite' ) ];
		}

		if ( ! WpConfigHelper::atomicWriteFile( $log_file, '' ) ) {
			return [ 'success' => false, 'message' => __( 'Could not clear debug log.', 'wp-powersuite' ) ];
		}

		return [ 'success' => true, 'message' => __( 'Debug log cleared.', 'wp-powersuite' ) ];
	}

	/**
	 * @return array{path: string, exists: bool, size: string, protected: bool, is_default_path: bool}
	 */
	public function getDebugLogInfo(): array {
		$log_file       = WpConfigHelper::resolveDebugLogPath();
		$exists         = file_exists( $log_file );
		$size           = $exists && is_readable( $log_file ) ? size_format( filesize( $log_file ) ) : '0 B';
		$default_path   = wp_normalize_path( WP_CONTENT_DIR . '/debug.log' );
		$htaccess       = WP_CONTENT_DIR . '/.htaccess';
		$htaccess_body  = file_exists( $htaccess ) ? (string) file_get_contents( $htaccess ) : '';
		$is_protected   = str_contains( $htaccess_body, '# WP PowerSuite: deny direct access to debug.log' );

		return [
			'path'             => $log_file,
			'exists'           => $exists,
			'size'             => $size,
			'protected'        => $is_protected,
			'is_default_path'  => wp_normalize_path( $log_file ) === $default_path,
		];
	}

	/**
	 * @return array{success: bool, content?: string, message?: string, truncated?: bool, total_size?: int}
	 */
	public function getDebugLogContent( int $max_bytes = 102400 ): array {
		$log_file = WpConfigHelper::resolveDebugLogPath();
		if ( ! file_exists( $log_file ) ) {
			return [ 'success' => false, 'message' => __( 'Debug log file not found.', 'wp-powersuite' ) ];
		}
		if ( ! is_readable( $log_file ) ) {
			return [ 'success' => false, 'message' => __( 'Debug log file is not readable.', 'wp-powersuite' ) ];
		}

		$size      = (int) filesize( $log_file );
		$truncated = $size > $max_bytes;

		if ( $truncated ) {
			$handle = fopen( $log_file, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- tail read of debug.log.
			if ( $handle === false ) {
				return [ 'success' => false, 'message' => __( 'Could not open debug log.', 'wp-powersuite' ) ];
			}
			fseek( $handle, -$max_bytes, SEEK_END );
			$content = stream_get_contents( $handle );
			fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- pairs with fopen() above.
			$content = ( substr( $content, 0, 3 ) === "\xEF\xBB\xBF" ? substr( $content, 3 ) : $content );
			$content = preg_replace( '/^[^\n]*\n?/', '', $content );
		} else {
			$read = WpConfigHelper::readFileContents( $log_file );
			if ( ! $read['success'] ) {
				return [ 'success' => false, 'message' => $read['message'] ?? __( 'Could not read debug log.', 'wp-powersuite' ) ];
			}
			$content = $read['content'];
			$content = ( substr( $content, 0, 3 ) === "\xEF\xBB\xBF" ? substr( $content, 3 ) : $content );
		}

		return [
			'success'    => true,
			'content'    => $content,
			'truncated'  => $truncated,
			'total_size' => $size,
		];
	}
}
