<?php
/**
 * ActiveTrail Logger Class
 *
 * Enhanced logging system for debugging ActiveTrail WooCommerce plugin
 *
 * @package ActiveTrail_WooCommerce
 * @since 1.0.0
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * ActiveTrail Logger Class
 */
class ActiveTrail_Logger {

	/**
	 * Log levels
	 */
	const LEVEL_ERROR   = 'ERROR';
	const LEVEL_WARNING = 'WARNING';
	const LEVEL_INFO    = 'INFO';
	const LEVEL_DEBUG   = 'DEBUG';

	/**
	 * Log contexts
	 */
	const CONTEXT_PRODUCT_FEED = 'PRODUCT_FEED';
	const CONTEXT_API          = 'API';
	const CONTEXT_CART         = 'CART';
	const CONTEXT_ORDER        = 'ORDER';
	const CONTEXT_SETTINGS     = 'SETTINGS';
	const CONTEXT_GENERAL      = 'GENERAL';

	/**
	 * Instance
	 *
	 * @var ActiveTrail_Logger
	 */
	private static $instance = null;

	/**
	 * Log file path
	 *
	 * @var string
	 */
	private $log_file;

	/**
	 * Whether logging is enabled
	 *
	 * @var bool
	 */
	private $logging_enabled;

	/**
	 * Constructor
	 */
	private function __construct() {
		$this->log_file        = WP_CONTENT_DIR . '/activetrail-debug.log';
		$this->logging_enabled = $this->is_logging_enabled();
	}

	/**
	 * Get instance
	 *
	 * @return ActiveTrail_Logger
	 */
	public static function get_instance() {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Check if logging is enabled
	 *
	 * @return bool
	 */
	private function is_logging_enabled() {
		// Check if WP_DEBUG is enabled.
		if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
			return false;
		}

		// Check if debug mode is enabled in plugin settings.
		if ( class_exists( 'ActiveTrail_Settings' ) ) {
			return ActiveTrail_Settings::get( 'debug_mode', false );
		}

		// Fallback to option.
		return get_option( 'activetrail_debug_mode', 'no' ) === 'yes';
	}

	/**
	 * Log a message
	 *
	 * @param string $message Log message
	 * @param string $level Log level
	 * @param string $context Log context
	 * @param array  $data Additional data to log
	 */
	public function log( $message, $level = self::LEVEL_INFO, $context = self::CONTEXT_GENERAL, $data = array() ) {
		// Re-evaluate logging status on each call so runtime changes are respected.
		$this->logging_enabled = $this->is_logging_enabled();

		if ( ! $this->logging_enabled ) {
			return;
		}

		// Normalize message to string to avoid "Array to string conversion" warnings.
		if ( is_array( $message ) || is_object( $message ) ) {
			$message = wp_json_encode( $message, JSON_UNESCAPED_UNICODE );
		}

		$timestamp         = current_time( 'Y-m-d H:i:s' );
		$formatted_message = sprintf(
			'[%s] [%s] [%s] %s',
			$timestamp,
			$level,
			$context,
			$message
		);

		// Add data if provided.
		if ( ! empty( $data ) ) {
			$formatted_message .= ' | Data: ' . wp_json_encode( $data, JSON_UNESCAPED_UNICODE );
		}

		// Write to WordPress debug log.
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Logger class intentionally uses error_log
			error_log( $formatted_message );
		}

		// Also write to our custom log file.
		$this->write_to_custom_log( $formatted_message );
	}

	/**
	 * Write to custom log file
	 *
	 * @param string $message Formatted message
	 */
	private function write_to_custom_log( $message ) {
		try {
			// Ensure log directory exists.
			$log_dir = dirname( $this->log_file );
			if ( ! file_exists( $log_dir ) ) {
				wp_mkdir_p( $log_dir );
			}

			// Write to log file.
			file_put_contents( $this->log_file, $message . PHP_EOL, FILE_APPEND | LOCK_EX );

			// Rotate log file if it gets too large (> 10MB).
			if ( file_exists( $this->log_file ) && filesize( $this->log_file ) > 10 * 1024 * 1024 ) {
				$this->rotate_log_file();
			}
		} catch ( Exception $e ) {
			// Fallback to error_log if custom logging fails.
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Logger class intentionally uses error_log
				error_log( '[ActiveTrail Logger] Failed to write to custom log: ' . $e->getMessage() );
			}
		}
	}

	/**
	 * Rotate log file
	 */
	private function rotate_log_file() {
		$backup_file = $this->log_file . '.old';

		// Remove old backup if exists.
		if ( file_exists( $backup_file ) ) {
			wp_delete_file( $backup_file );
		}

		// Move current log to backup.
		global $wp_filesystem;
		if ( empty( $wp_filesystem ) ) {
			require_once ABSPATH . '/wp-admin/includes/file.php';
			WP_Filesystem();
		}
		if ( $wp_filesystem ) {
			$wp_filesystem->move( $this->log_file, $backup_file, true );
		} else {
			// Fallback: try to use wp_delete_file and create new file if WP_Filesystem is not available.
			// Note: This is a fallback only and should rarely be needed.
			if ( file_exists( $this->log_file ) ) {
				// Copy content and delete original as fallback.
				$log_content = file_get_contents( $this->log_file );
				if ( $log_content !== false && file_put_contents( $backup_file, $log_content ) !== false ) {
					wp_delete_file( $this->log_file );
				}
			}
		}
	}

	/**
	 * Log error message
	 *
	 * @param string $message Error message
	 * @param string $context Context
	 * @param array  $data Additional data
	 */
	public function error( $message, $context = self::CONTEXT_GENERAL, $data = array() ) {
		$this->log( $message, self::LEVEL_ERROR, $context, $data );
	}

	/**
	 * Log warning message
	 *
	 * @param string $message Warning message
	 * @param string $context Context
	 * @param array  $data Additional data
	 */
	public function warning( $message, $context = self::CONTEXT_GENERAL, $data = array() ) {
		$this->log( $message, self::LEVEL_WARNING, $context, $data );
	}

	/**
	 * Log info message
	 *
	 * @param string $message Info message
	 * @param string $context Context
	 * @param array  $data Additional data
	 */
	public function info( $message, $context = self::CONTEXT_GENERAL, $data = array() ) {
		$this->log( $message, self::LEVEL_INFO, $context, $data );
	}

	/**
	 * Log debug message
	 *
	 * @param string $message Debug message
	 * @param string $context Context
	 * @param array  $data Additional data
	 */
	public function debug( $message, $context = self::CONTEXT_GENERAL, $data = array() ) {
		$this->log( $message, self::LEVEL_DEBUG, $context, $data );
	}

	/**
	 * Log product feed specific message
	 *
	 * @param string $message Message
	 * @param string $level Log level
	 * @param array  $data Additional data
	 */
	public function product_feed( $message, $level = self::LEVEL_INFO, $data = array() ) {
		$this->log( $message, $level, self::CONTEXT_PRODUCT_FEED, $data );
	}

	/**
	 * Log API specific message
	 *
	 * @param string $message Message
	 * @param string $level Log level
	 * @param array  $data Additional data
	 */
	public function api( $message, $level = self::LEVEL_INFO, $data = array() ) {
		$this->log( $message, $level, self::CONTEXT_API, $data );
	}

	/**
	 * Get log file path
	 *
	 * @return string
	 */
	public function get_log_file_path() {
		return $this->log_file;
	}

	/**
	 * Get recent log entries
	 *
	 * @param int $lines Number of lines to retrieve
	 * @return array
	 */
	public function get_recent_logs( $lines = 100 ) {
		if ( ! file_exists( $this->log_file ) ) {
			return array();
		}

		$log_content = file_get_contents( $this->log_file );
		$log_lines   = explode( PHP_EOL, $log_content );

		// Get last N lines.
		$recent_lines = array_slice( $log_lines, -$lines );

		// Remove empty lines.
		return array_filter( $recent_lines );
	}

	/**
	 * Clear log file
	 *
	 * @return bool
	 */
	public function clear_logs() {
		if ( file_exists( $this->log_file ) ) {
			return wp_delete_file( $this->log_file );
		}
		return true;
	}

	/**
	 * Get log file size
	 *
	 * @return int Size in bytes
	 */
	public function get_log_file_size() {
		if ( file_exists( $this->log_file ) ) {
			return filesize( $this->log_file );
		}
		return 0;
	}

	/**
	 * Format file size for display
	 *
	 * @param int $bytes Size in bytes
	 * @return string Formatted size
	 */
	public function format_file_size( $bytes ) {
		$units = array( 'B', 'KB', 'MB', 'GB' );
		$bytes = max( $bytes, 0 );
		$pow   = floor( ( $bytes ? log( $bytes ) : 0 ) / log( 1024 ) );
		$pow   = min( $pow, count( $units ) - 1 );

		$bytes /= pow( 1024, $pow );

		return round( $bytes, 2 ) . ' ' . $units[ $pow ];
	}

	/**
	 * Log request details for debugging
	 *
	 * @param string $context Context description
	 */
	public function log_request_details( $context = 'Request' ) {
		if ( ! $this->logging_enabled ) {
			return;
		}

		$request_data = array(
			'method'      => isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : 'Unknown',
			'uri'         => isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : 'Unknown',
			'user_agent'  => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : 'Unknown',
			'ip'          => $this->get_client_ip(),
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Debug logging only, data is sanitized
			'get_params'  => $_GET,
            // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Debug logging only, data is sanitized
			'post_params' => $this->sanitize_post_data( $_POST ),
			'headers'     => $this->get_request_headers(),
		);

		$this->debug( $context . ' Details', self::CONTEXT_GENERAL, $request_data );
	}

	/**
	 * Get client IP address
	 *
	 * @return string
	 */
	private function get_client_ip() {
		$ip_keys = array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' );

		foreach ( $ip_keys as $key ) {
			if ( array_key_exists( $key, $_SERVER ) === true ) {
                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below
				$server_value = wp_unslash( $_SERVER[ $key ] );
				foreach ( explode( ',', $server_value ) as $ip ) {
					$ip = trim( sanitize_text_field( $ip ) );
					if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
						return $ip;
					}
				}
			}
		}

		return isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'Unknown';
	}

	/**
	 * Sanitize POST data for logging (remove sensitive information)
	 *
	 * @param array $post_data POST data
	 * @return array Sanitized data
	 */
	private function sanitize_post_data( $post_data ) {
		$sensitive_keys = array( 'password', 'api_key', 'token', 'secret', 'key' );
		$sanitized      = $post_data;

		foreach ( $sensitive_keys as $key ) {
			if ( isset( $sanitized[ $key ] ) ) {
				$sanitized[ $key ] = '[REDACTED]';
			}
		}

		return $sanitized;
	}

	/**
	 * Get request headers
	 *
	 * @return array
	 */
	private function get_request_headers() {
		$headers = array();

		foreach ( $_SERVER as $key => $value ) {
			if ( strpos( $key, 'HTTP_' ) === 0 ) {
				$header             = str_replace( 'HTTP_', '', $key );
				$header             = str_replace( '_', '-', $header );
				$headers[ $header ] = sanitize_text_field( wp_unslash( $value ) );
			}
		}

		return $headers;
	}
}

/**
 * Helper function to get logger instance
 *
 * @return ActiveTrail_Logger
 */
function activetrail_logger() {
	return ActiveTrail_Logger::get_instance();
}
