<?php
/**
 * Debug Manager
 *
 * Centralized debug logging system with configurable PHP and JavaScript debugging.
 * Supports custom log paths and granular debug control.
 *
 * @package opti-behavior
 * @copyright 2025 Opti-User
 * @version 1.0.3
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Debug Manager Class
 *
 * Handles debug logging for PHP and JavaScript.
 *
 * @since 1.0.0
 */
class Opti_Behavior_Heatmap_Debug_Manager {

	/**
	 * Core instance.
	 *
	 * @since 1.0.0
	 * @var Opti_Behavior_Heatmap_Core
	 */
	private $core;

	/**
	 * Debug settings option name.
	 *
	 * @since 1.0.0
	 */
	const DEBUG_OPTION = 'opti_behavior_heatmap_debug_settings';

	/**
	 * Default log file name.
	 *
	 * @since 1.0.0
	 */
	const DEFAULT_LOG_FILE = 'opti-behavior-heatmap-debug.log';

	/**
	 * Debug settings.
	 *
	 * @since 1.0.0
	 * @var array
	 */
	private $settings;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 * @param Opti_Behavior_Heatmap_Core $core Core instance.
	 */
	public function __construct( $core ) {
		$this->core = $core;
		$this->load_settings();
	}

	/**
	 * Load debug settings.
	 *
	 * @since 1.0.0
	 */
	private function load_settings() {
		$defaults = array(
			'php_debug_enabled'    => false,
			'js_debug_enabled'     => false,
			'log_to_file'          => true,
			'log_to_console'       => false,
			'custom_log_path'      => '',
			'log_folder'           => 'opti-behavior-logs',
			'log_level'            => 'info', // error, warning, info, debug
			'max_log_size'         => 10, // MB
			'auto_cleanup'         => true,
			'cleanup_days'         => 30,
		);

		$this->settings = wp_parse_args( get_option( self::DEBUG_OPTION, array() ), $defaults );
	}

	/**
	 * Save debug settings.
	 *
	 * @since 1.0.0
	 * @param array $settings Settings to save.
	 */
	public function save_settings( $settings ) {
		$this->settings = wp_parse_args( $settings, $this->settings );
		update_option( self::DEBUG_OPTION, $this->settings );
	}

	/**
	 * Get debug settings.
	 *
	 * @since 1.0.0
	 * @return array Debug settings.
	 */
	public function get_settings() {
		return $this->settings;
	}

	/**
	 * Check if PHP debugging is enabled.
	 *
	 * @since 1.0.0
	 * @return bool True if enabled, false otherwise.
	 */
	public function is_php_debug_enabled() {
		return (bool) $this->settings['php_debug_enabled'];
	}

	/**
	 * Check if JavaScript debugging is enabled.
	 *
	 * @since 1.0.0
	 * @return bool True if enabled, false otherwise.
	 */
	public function is_js_debug_enabled() {
		return (bool) $this->settings['js_debug_enabled'];
	}

	/**
	 * Get log file path.
	 *
	 * @since 1.0.0
	 * @return string Log file path.
	 */
	public function get_log_file_path() {
		// Use custom path if specified
		if ( ! empty( $this->settings['custom_log_path'] ) ) {
			$base_path = trailingslashit( $this->settings['custom_log_path'] );
		} else {
			// Default to uploads directory (WordPress best practice)
			$upload_dir = wp_upload_dir();
			$base_path = trailingslashit( $upload_dir['basedir'] );
		}

		// Add log folder
		if ( ! empty( $this->settings['log_folder'] ) ) {
			$base_path .= trailingslashit( $this->settings['log_folder'] );
		}

		// Ensure directory exists
		if ( ! file_exists( $base_path ) ) {
			wp_mkdir_p( $base_path );
			
			// Add .htaccess to protect log files
			$htaccess_file = $base_path . '.htaccess';
			if ( ! file_exists( $htaccess_file ) ) {
				file_put_contents( $htaccess_file, "Deny from all\n" );
			}
		}

		return $base_path . self::DEFAULT_LOG_FILE;
	}

	/**
	 * Log a message.
	 *
	 * @since 1.0.0
	 * @param string $message Message to log.
	 * @param string $level   Log level (error, warning, info, debug).
	 * @param string $context Context/category.
	 */
	public function log( $message, $level = 'info', $context = 'general' ) {
		// Check if PHP debugging is enabled
		if ( ! $this->is_php_debug_enabled() ) {
			return;
		}

		// Check log level
		if ( ! $this->should_log_level( $level ) ) {
			return;
		}

		// Format message
		$formatted_message = $this->format_log_message( $message, $level, $context );

		// Log to file
		if ( $this->settings['log_to_file'] ) {
			$this->log_to_file( $formatted_message );
		}

		// Log to WordPress debug.log
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional logging to WordPress debug.log in debug manager
			error_log( $formatted_message );
		}

		// Log to browser console (for AJAX requests)
		if ( $this->settings['log_to_console'] && wp_doing_ajax() ) {
			$this->log_to_console( $message, $level, $context );
		}
	}

	/**
	 * Check if a log level should be logged.
	 *
	 * @since 1.0.0
	 * @param string $level Log level to check.
	 * @return bool True if should log, false otherwise.
	 */
	private function should_log_level( $level ) {
		$levels = array( 'error' => 1, 'warning' => 2, 'info' => 3, 'debug' => 4 );
		$current_level = isset( $levels[ $this->settings['log_level'] ] ) ? $levels[ $this->settings['log_level'] ] : 3;
		$message_level = isset( $levels[ $level ] ) ? $levels[ $level ] : 3;

		return $message_level <= $current_level;
	}

	/**
	 * Format log message.
	 *
	 * @since 1.0.0
	 * @param string $message Message.
	 * @param string $level   Level.
	 * @param string $context Context.
	 * @return string Formatted message.
	 */
	private function format_log_message( $message, $level, $context ) {
		$timestamp = current_time( 'Y-m-d H:i:s' );
		$level_upper = strtoupper( $level );
		return "[{$timestamp}] [{$level_upper}] [{$context}] {$message}";
	}

	/**
	 * Log to file.
	 *
	 * @since 1.0.0
	 * @param string $message Formatted message.
	 */
	private function log_to_file( $message ) {
		$log_file = $this->get_log_file_path();

		// Check file size and rotate if needed
		if ( file_exists( $log_file ) ) {
			$file_size_mb = filesize( $log_file ) / 1024 / 1024;
			if ( $file_size_mb > $this->settings['max_log_size'] ) {
				$this->rotate_log_file( $log_file );
			}
		}

		// Write to log file
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional file logging in debug manager (error_log with file parameter)
		error_log( $message . PHP_EOL, 3, $log_file );
	}

	/**
	 * Rotate log file.
	 *
	 * @since 1.0.0
	 * @param string $log_file Log file path.
	 */
	private function rotate_log_file( $log_file ) {
		$timestamp = current_time( 'Y-m-d-H-i-s' );
		$rotated_file = str_replace( '.log', "-{$timestamp}.log", $log_file );
		// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- File system operation for log rotation
		rename( $log_file, $rotated_file );

		// Auto-cleanup old logs if enabled
		if ( $this->settings['auto_cleanup'] ) {
			$this->cleanup_old_logs();
		}
	}

	/**
	 * Cleanup old log files.
	 *
	 * @since 1.0.0
	 */
	public function cleanup_old_logs() {
		$log_dir = dirname( $this->get_log_file_path() );
		$cleanup_time = time() - ( $this->settings['cleanup_days'] * DAY_IN_SECONDS );

		$files = glob( $log_dir . '/opti-behavior-heatmap-debug-*.log' );
		foreach ( $files as $file ) {
			if ( filemtime( $file ) < $cleanup_time ) {
				// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- File system operation for log cleanup
				unlink( $file );
			}
		}
	}

	/**
	 * Log to browser console.
	 *
	 * @since 1.0.0
	 * @param string $message Message.
	 * @param string $level   Level.
	 * @param string $context Context.
	 * @global array $GLOBALS['opti_behavior_console_logs'] Console logs array.
	 */
	private function log_to_console( $message, $level, $context ) {
		// Store console logs to be output in AJAX response
		if ( ! isset( $GLOBALS['opti_behavior_console_logs'] ) ) {
			$GLOBALS['opti_behavior_console_logs'] = array();
		}

		$GLOBALS['opti_behavior_console_logs'][] = array(
			'message' => $message,
			'level'   => $level,
			'context' => $context,
		);
	}

	/**
	 * Get JavaScript debug configuration.
	 *
	 * @since 1.0.0
	 * @return array Debug configuration.
	 */
	public function get_js_debug_config() {
		return array(
			'enabled'    => $this->is_js_debug_enabled(),
			'logLevel'   => $this->settings['log_level'],
			'prefix'     => '[optibehavior]',
			'timestamp'  => true,
			'stackTrace' => $this->settings['log_level'] === 'debug',
		);
	}

	/**
	 * Get log file contents.
	 *
	 * @since 1.0.0
	 * @param int $lines Number of lines to retrieve (default: 100).
	 * @return string Log contents.
	 */
	public function get_log_contents( $lines = 100 ) {
		$log_file = $this->get_log_file_path();

		if ( ! file_exists( $log_file ) ) {
			return 'No log file found.';
		}

		// Read last N lines
		$file = new SplFileObject( $log_file, 'r' );
		$file->seek( PHP_INT_MAX );
		$total_lines = $file->key();
		$start_line = max( 0, $total_lines - $lines );

		$file->seek( $start_line );
		$log_contents = '';
		while ( ! $file->eof() ) {
			$log_contents .= $file->fgets();
		}

		return $log_contents;
	}

	/**
	 * Clear log file.
	 *
	 * @since 1.0.0
	 */
	public function clear_log() {
		$log_file = $this->get_log_file_path();
		if ( file_exists( $log_file ) ) {
			file_put_contents( $log_file, '' );
		}
	}

	/**
	 * Get log file size.
	 *
	 * @since 1.0.0
	 * @return string Formatted file size.
	 */
	public function get_log_file_size() {
		$log_file = $this->get_log_file_path();
		if ( ! file_exists( $log_file ) ) {
			return '0 KB';
		}

		$size_bytes = filesize( $log_file );
		return size_format( $size_bytes, 2 );
	}
}

