<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
if ( defined( 'WP_CLI' ) && WP_CLI ) {
	class Malcure_Advanced {

		private $wp_custom_dir  = false;
		public $log_marker      = '// MALCURE LOG MARKER ?>';
		private $logfile_header = '';
		private $wp_home_dir    = '';
		private $logging        = '';
		private $logfile        = '';


		function __construct() {
			// phpcs:ignore Squiz.PHP.Heredoc.NotAllowed -- Security action, need distinction for visual alertness
			$cdt = gmdate( 'Y' ); $brand = <<<EOD
            

███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████
█████                                                                    ██████
█████    ███╗   ███╗ █████╗ ██╗      ██████╗██╗   ██╗██████╗ ███████╗    ██████
█████    ████╗ ████║██╔══██╗██║     ██╔════╝██║   ██║██╔══██╗██╔════╝    ██████
█████    ██╔████╔██║███████║██║     ██║     ██║   ██║██████╔╝█████╗      ██████
█████    ██║╚██╔╝██║██╔══██║██║     ██║     ██║   ██║██╔══██╗██╔══╝      ██████
█████    ██║ ╚═╝ ██║██║  ██║███████╗╚██████╗╚██████╔╝██║  ██║███████╗    ██████
█████    ╚═╝     ╚═╝╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝    ██████
█████                                                                    ██████
███████████████████████████████████████████████████████████████████████████████
█████                                                                    ██████
█████                     © Copyright $cdt MalCure                       ██████
█████                     Author —                                       ██████
█████                     Shiv / shiv@malcure.com                        ██████
█████                                                                    ██████
███████████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████████

EOD;
			WP_CLI::log( WP_CLI::colorize( $this->brand_color() . $brand . '%n' ) );
			$this->init();
			$this->sync( array(), array( 'silent' => 1 ) );
		}

		private function init() {
			$this->prerequisites();
			$wpmr = wp_malware_removal();
			$met  = @ini_get( 'max_execution_time' );
			$ml   = @ini_get( 'memory_limit' );
			if ( ! $wpmr->is_advanced_edition() ) {
				if ( ! array_intersect( array( 'help', 'activate', 'register', 'sync', 'status' ), isset( $_SERVER['argv'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_SERVER['argv'] ) ) : array() ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked with isset(), unslashed with wp_unslash(), and sanitized with sanitize_text_field()
					WP_CLI::error( 'Full CLI integration is only possible with Malcure Advanced Edition. Please see: https://malcure.com/?p=116', false );
					if ( $wpmr->get_setting( 'license_key' ) ) {
						$this->license_req( 'check_license', $wpmr->get_setting( 'license_key' ) );
					}
					$this->aalert();
					die();
				}
			}
		}

		private function get_log_dir( $path ) {
			if ( empty( $path ) ) {
				return trailingslashit( $this->normalise_path( WP_CONTENT_DIR ) );
			}

			$pathinfo = pathinfo( $path );
			if ( ! empty( $pathinfo['dirname'] ) && is_dir( $pathinfo['dirname'] ) ) {
				return trailingslashit( $this->normalise_path( $pathinfo['dirname'] ) );
			} else {
				return trailingslashit( $this->normalise_path( WP_CONTENT_DIR ) );
			}
		}

		function scan( $args, $assoc_args ) {
			$this->info();
			$wpmr                    = wp_malware_removal();
			$infected                = false;
			$start_time              = time();
			$assoc_args['timestamp'] = time();

			$assoc_args['mcskipvuln']      = $this->get_flag( $assoc_args, 'mcskipvuln' );
			$assoc_args['mcsuspicious']    = $this->get_flag( $assoc_args, 'mcsuspicious' );
			$assoc_args['mcskipdb']        = $this->get_flag( $assoc_args, 'mcskipdb' );
			$assoc_args['mcskipfiles']     = $this->get_flag( $assoc_args, 'mcskipfiles' );
			$assoc_args['mcskipredirects'] = $this->get_flag( $assoc_args, 'mcskipredirects' );
			$assoc_args['mcdebug']         = $this->get_flag( $assoc_args, 'mcdebug' );

			$logfile       = '';
			$valid_formats = array( 'json' ); // array( 'table', 'json', 'csv', 'yaml', 'ids', 'count' ); // for now we'll stick to json unless we have user-request
			$this->logging = false;
			if ( ! empty( $assoc_args['log'] ) ) {
				$logfile = $assoc_args['log'];

				if ( ! empty( $assoc_args['logformat'] ) && in_array( $assoc_args['logformat'], $valid_formats ) ) {
					$logformat = $assoc_args['logformat'];
				} else {
					$logformat = 'json';
				}

				$logfile       = $this->get_log_dir( $logfile ) . pathinfo( $logfile )['filename'] . '.' . $logformat . '.php';
				$this->logfile = $logfile;

				WP_CLI::log( WP_CLI::colorize( $this->heading_format() . "\nLogging Scan Results To File :\t" . $logfile . '%n' ) );
				if ( file_exists( $logfile ) ) {
					WP_CLI::log( WP_CLI::colorize( $this->heading_format() . "\t★ Previous log-file will be overwritten :\t" . $logfile . '%n' ) );
				}
				$this->logging = true;

				$this->log( $this->logfile_header, 0 );

				$logcontents = array(
					'starttime' => $this->get_time(),
					'env'       => array(
						'website url'       => get_bloginfo( 'url' ),
						'wordpress url'     => get_bloginfo( 'wpurl' ),
						'wordpress version' => get_bloginfo( 'version' ),
						'multisite'         => is_multisite(),
						'active theme'      => get_bloginfo( 'stylesheet_directory' ),
						'parent theme'      => get_bloginfo( 'template_directory' ),
						'php version'       => phpversion(),
						'server'            => php_uname(),
						'users roles'       => array(),
						'must-use plugins'  => array(),
						'drop-in plugins'   => array(),
					),
					'malcure'   => array(
						'definition version' => $wpmr->get_definition_version(),
						'last updated'       => $wpmr->get_last_updated_ago(),
					),

				);

				global $wp_roles;
				foreach ( $wp_roles->roles as $role => $capabilities ) {
					$logcontents['env']['users roles'][] = $role;
				}

				$mu = get_mu_plugins();
				foreach ( $mu as $key => $value ) {
					$logcontents['env']['must-use plugins'][] = $key;
				}

				$dropins = get_dropins();
				foreach ( $dropins as $key => $value ) {
					$logcontents['env']['drop-in plugins'][] = $key;
				}

				$this->log( $logcontents );
			}
			$logcontents['messages']        = array();
			$assoc_args['do_vuln_scan']     = ! $assoc_args['mcskipvuln'];
			$assoc_args['do_db_scan']       = ! $assoc_args['mcskipdb'];
			$assoc_args['do_file_scan']     = ! $assoc_args['mcskipfiles'];
			$assoc_args['do_redirect_scan'] = ! $assoc_args['mcskipredirects'];
			if ( ! empty( $assoc_args['mcfiles'] ) ) {
				$assoc_args['mcfiles'] = explode( ',', $assoc_args['mcfiles'] );
			}

			if ( empty( $assoc_args['mcbatchsize'] ) ) {
				$assoc_args['mcbatchsize'] = 50;
			}

			$batchsize                      = $assoc_args['mcbatchsize'];
			$logcontents['malcure']['args'] = $assoc_args;
			$this->log( $logcontents );

			$bootstrap = $wpmr->bootstrap( $assoc_args );

			if ( $bootstrap['last_updated'] == 'Never' ) {
				WP_CLI::warning( 'Your site is using out of date definitions! Limited dection ability...' );
			}
			if ( ! empty( $assoc_args['mcfiles'] ) ) {
				$files = $assoc_args['mcfiles'];
			} else {
				$files = $bootstrap['files'];
			}
			$db_scan         = empty( $bootstrap['db_scan'] ) ? false : $bootstrap['db_scan'];
			$title_hack      = $bootstrap['title_hack'];
			$redirect_hijack = $bootstrap['redirect_hijack'];
			$vuln_scan       = $bootstrap['vulnerabilities_scan'];

			if ( ! empty( $vuln_scan ) ) {
				$infected                                  = true;
				$logcontents['results']['vulnerabilities'] = array();
				WP_CLI::log( "\n" . WP_CLI::colorize( $this->heading_format() . '  VULNERABILITY SCAN RESULTS  %n' ) );

				foreach ( $vuln_scan as $category => $items ) {
					if ( ! empty( $items ) && is_array( $items ) ) {
						foreach ( $items as $item_name => $details ) {
							$severity  = isset( $details['severity'] ) ? (string) $details['severity'] : 'unknown';
							$signature = isset( $details['signature'] ) ? (string) $details['signature'] : 'unknown';
							$message   = isset( $details['message'] ) ? (string) $details['message'] : 'No message available';

							WP_CLI::log( WP_CLI::colorize( '%r' . "\t" . strtoupper( $severity ) . "\t" . $signature . "\t" . $item_name . ' - ' . html_entity_decode( $message ) . '%n' ) );
							$logcontents['results']['vulnerabilities'][] = array(
								'category'  => $category,
								'name'      => $item_name,
								'severity'  => $severity,
								'reference' => trailingslashit( MALCURE_API ) . 'webscan/sig/' . $signature,
								'message'   => html_entity_decode( $message ),
							);
						}
					}
				}
				$this->log( $logcontents );
			}

			$logcontents['malcure']['workload'] = array(
				'files to scan' => $bootstrap['count'],
				'checksums'     => $bootstrap['checksums'],
			);
			$this->log( $logcontents );

			WP_CLI::log( "\nFiles To Scan :\t" . $bootstrap['count'] );
			WP_CLI::log( "Checksums     :\t" . $bootstrap['checksums'] );
			WP_CLI::log( "Batch-Size    :\t" . $batchsize );

			if ( ! empty( $title_hack ) ) {
				$infected = true;
				WP_CLI::log( "\n" . WP_CLI::colorize( $this->heading_format() . '  TITLE SCAN RESULTS  ' . '%n' ) );
				WP_CLI::log( WP_CLI::colorize( $this->get_severity_format( 'SEVERE' ) ) . '    ' . 'Site Title is Hacked' );
				$logcontents['results']['title_hack'] = array(
					'severity'  => 'severe',
					'reference' => 'N/A',
					'message'   => 'Site Title is Hacked',
				);
				$this->log( $logcontents );
			}
			if ( ! empty( $redirect_hijack ) ) {
				$infected = true;
				WP_CLI::log( "\n" . WP_CLI::colorize( $this->heading_format() . '  REDIRECT HIJACK SCAN RESULTS  ' . '%n' ) );
				WP_CLI::log( WP_CLI::colorize( $this->get_severity_format( 'SEVERE' ) ) . '    ' . 'Site redirect hijack dettected' );
				$logcontents['results']['redirect_hijack'] = array(
					'severity'  => 'severe',
					'reference' => 'N/A',
					'message'   => 'Site redirect hijack dettected',
				);
				$this->log( $logcontents );
			}
			if ( ! empty( $db_scan ) ) {
				$infected                           = true;
				$logcontents['results']['database'] = array();
				WP_CLI::log( "\n" . WP_CLI::colorize( $this->heading_format() . '  DATABASE SCAN RESULTS  %n' ) );
				foreach ( $db_scan as $key => $value ) {
					WP_CLI::log( WP_CLI::colorize( '%r' . "\t" . strtoupper( $value['severity'] ) . "\t" . $value['signature'] . "\t" . html_entity_decode( $value['message'] ) . '%n' ) );
					$logcontents['results']['database'][] = array(
						'severity'  => $value['severity'],
						'reference' => trailingslashit( MALCURE_API ) . 'webscan/sig/' . $value['signature'],
						'message'   => html_entity_decode( $value['message'] ),
					);
				}
				$this->log( $logcontents );
			}
			$total = $bootstrap['count'];
			if ( empty( $assoc_args['mcdebug'] ) || $assoc_args['mcdebug'] != 'true' ) {
				$progress = WP_CLI\Utils\make_progress_bar( 'Progress:', count( $files ) );
			} else {
				$progress = false;
			}
			if ( count( $files ) ) {
				if ( $this->wp_custom_dir ) {
					WP_CLI::error( 'WordPress is installed in a custom directory ' . $this->wp_home_dir, 0 );
					WP_CLI::error( 'Malcure will only consider files in the above WordPress directory.', 0 );
					$logcontents['messages'][] = 'WordPress installed in custom directory. Excluding files above WordPress directory.';
					$this->log( $logcontents );
				}
				WP_CLI::log( "\n" . WP_CLI::colorize( $this->heading_format() . '  FILE SCAN RESULTS  %n' ) );
				do {
					@set_time_limit( 0 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for large scans to prevent timeout
					$arrbatch = array_splice( $files, 0, $batchsize );
					if ( $progress ) {
						$last_file = str_replace( wp_normalize_path( ABSPATH ), '', wp_normalize_path( $arrbatch[ count( $arrbatch ) - 1 ] ) );
						$progress->tick( count( $arrbatch ), ( $total - count( $files ) ) . ' of ' . $total . ' ' . $last_file );
					}
					if ( ! $progress ) {
						$i = (string) number_format( round( ( ( ( $total - count( $files ) ) / $total ) * 100 ), 1 ), 1, '.', '' );
						WP_CLI::log( str_pad( $i, ( strlen( $i ) ), ' ' ) . "%\t" );
					}
					$batch                 = $arrbatch;
					$assoc_args['mcfiles'] = $batch; // array_map( array( $this, 'encode_filename' ), $batch );
					$result                = $wpmr->wpmr_scan_files( $assoc_args );
					$result                = $result['report'];
					foreach ( $result as $file => $report ) {
						if ( in_array( $report['severity'], array( 'severe', 'high' ) ) ) {
							$infected = true;
						}
						WP_CLI::log( WP_CLI::colorize( $this->get_severity_format( strtoupper( $report['severity'] ) ) . '    ' . strtoupper( $report['severity'] ) . '    ' . $report['signature'] . "\t\t" . $file . '%n' ) );
						$logcontents['results']['files'][] = array(
							'severity'  => $report['severity'],
							'reference' => trailingslashit( MALCURE_API ) . 'webscan/sig/' . $report['signature'],
							'message'   => $file,
						);
					}
					$this->log( $logcontents );
				} while ( count( $files ) );
			}
			if ( ! empty( $progress ) ) {
				$progress->finish();
			}
			echo "\n";
			$end_time       = time();
			$execution_time = ( $end_time - $start_time );
			if ( ! $infected && $assoc_args['do_db_scan'] && empty( $assoc_args['mcskipdirs'] ) ) {
				$wpmr->delete_setting( 'infected' );
			}
			WP_CLI::success( 'Malcure Scan Completed in ' . human_time_diff( $start_time, $end_time ) . "!\n" );
			$this->aalert();
		}

		/**
		 * Update definitions
		 *
		 * @param array $args
		 * @param array $assoc_args
		 * @return void
		 */
		function sync( $args = array(), $assoc_args = array() ) {
			$wpmr = wp_malware_removal();
			$wpmr->check_definitions( true );
			$updates    = $wpmr->definition_updates_available();
			$force      = $this->get_flag( $assoc_args, 'mcforce' );
			$can_update = ( $wpmr->get_setting( 'def_auto_update_enabled' ) || $force );
			$silent     = $this->get_flag( $assoc_args, 'silent' );

			if ( ! $updates && ! $can_update ) {
				if ( ! $silent ) {
					WP_CLI::log( WP_CLI::colorize( '%GNo updates available.%R %nPlease enable definition-auto-updates in settings to auto-update definitions.' ) );
				}
				return;
			} elseif ( ! $updates && $can_update ) {
				if ( ! $silent ) {
					WP_CLI::log( WP_CLI::colorize( '%GNo updates available.%R %n Definitions are at version ' . $wpmr->get_definition_version() ) );
				}
				return;
			} elseif ( $updates && ! $can_update ) {
				if ( ! $silent ) {
					WP_CLI::log( WP_CLI::colorize( "%WNew Definition Updates Are Available.\t%1 INSTALLED: " . $updates['current'] . " \t%3 LATEST: " . $updates['new'] . ' %n' ) );
					WP_CLI::log( WP_CLI::colorize( '%WPlease enable definition-auto-updates in settings to auto-update definitions.%n' ) );
				}
				return;
			} elseif ( $updates && $can_update ) {
				$wpmr->update_definitions_cli( 1 );
				if ( ! $silent ) {
					WP_CLI::log( WP_CLI::colorize( '%GUpdated to the latest definitions from version %R' . $updates['current'] . '%n to %Y' . $updates['new'] . '%n.' ) );
				}
				return;
			}
		}

		function register( $args, $assoc_args ) {
			if ( empty( $assoc_args['mc-email'] ) ) {
				WP_CLI::error( 'Need email' );
			}
			if ( ! filter_var( $assoc_args['mc-email'], FILTER_VALIDATE_EMAIL ) ) {
				WP_CLI::error( 'Need authentic email.' );
			}
			$email = $assoc_args['mc-email'];
			$fn    = $ln = '';
			if ( empty( $assoc_args['mc-fname'] ) ) {
				$fn = explode( '@', $assoc_args['mc-email'] )[0];
			}
			if ( empty( $assoc_args['mc-lname'] ) ) {
				$ln = $fn;
			}
			$wpmr = wp_malware_removal();
			$wpmr->wpmr_cli_register( $email, $fn, $ln, 1 );
		}

		private function encode_filename( $filename ) {
			return base64_encode( urlencode( $filename ) );
		}

		function info() {
			$wpmr  = wp_malware_removal();
			$files = $wpmr->return_all_files();
			global $wp_version;
			WP_CLI::log( WP_CLI::colorize( '%n%wMalcure Advanced Edition ' . $wpmr->plugin_data['Version'] . '%B' ) );

			// Display clean registration details
			$user_details = $wpmr->get_setting( 'user' );
			if ( ! empty( $user_details ) ) {
				WP_CLI::log( WP_CLI::colorize( '%n%wRegistration Details:%n' ) );
				if ( isset( $user_details['ID'] ) ) {
					WP_CLI::log( "\tUser ID       : " . $user_details['ID'] );
				}
				if ( isset( $user_details['user_email'] ) ) {
					WP_CLI::log( "\tEmail         : " . $user_details['user_email'] );
				}
				if ( isset( $user_details['first_name'] ) ) {
					WP_CLI::log( "\tFirst Name    : " . $user_details['first_name'] );
				}
				if ( isset( $user_details['last_name'] ) ) {
					WP_CLI::log( "\tLast Name     : " . $user_details['last_name'] );
				}
			}

			$this->license_req();
			// WP_CLI::log( print_r( $wpmr->plugin_data, 1 ) );
			// WP_CLI::log( WP_CLI::colorize( '%n%wVersion              : %B' . $wpmr->plugin_data['Version'] ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wWebsite URL          : %B' . get_bloginfo( 'url' ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wWP URL               : %B' . get_bloginfo( 'wpurl' ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wWP Installation DIR  : %B' . ABSPATH ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wWP Version           : %B' . get_bloginfo( 'version' ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wWP Language          : %B' . get_bloginfo( 'language' ) ) );
			WP_CLI::log( WP_CLI::colorize( is_multisite() ? '%n%wMultisite            : %BYes' : '%n%wMultisite            : %BNo' ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wActive Theme         : %B' . get_bloginfo( 'stylesheet_directory' ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wParent Theme         : %B' . get_bloginfo( 'template_directory' ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wPHP                  : %B' . phpversion() ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wServer               : %B' . php_uname() ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wLast Updated         : %B' . $wpmr->get_last_updated_ago() ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wChecksum-Count       : %B' . count( $wpmr->get_all_checksums() ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wDefinition-Count     : %B' . $wpmr->get_definition_count() ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wDefinition-Version   : %B' . $wpmr->get_definition_version() ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wFiles                : %B' . count( $files ) ) );
			WP_CLI::log( WP_CLI::colorize( '%n%wUser Roles           :%B%n' ) );
			global $wp_roles;
			foreach ( $wp_roles->roles as $role => $capabilities ) {
				WP_CLI::log( "\t" . $role );
			}
			WP_CLI::log( WP_CLI::colorize( '%n%wMust-Use Plugins:%B' ) );
			$mu = get_mu_plugins();
			foreach ( $mu as $key => $value ) {
				WP_CLI::log( "\t" . $key );
			}
			WP_CLI::log( WP_CLI::colorize( '%n%wDrop-ins:%B' ) );
			$dropins = get_dropins();
			foreach ( $dropins as $key => $value ) {
				WP_CLI::log( "\t" . $key );
			}
			WP_CLI::log( WP_CLI::colorize( '%YHidden Files:%n' ) );
			$this->hidden();
		}

		private function get_time() {
			$time = time();
			$zone = function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : $this->timezone_string_compat();
			$date = new DateTime( '@' . $time, new DateTimeZone( 'UTC' ) );
			$date->setTimezone( new DateTimeZone( $zone ) );
			return $date->format( 'c' );
		}

		private function timezone_string_compat() {
			$timezone_string = get_option( 'timezone_string' );
			if ( $timezone_string ) {
				return $timezone_string;
			}
			$offset    = (float) get_option( 'gmt_offset' );
			$hours     = (int) $offset;
			$minutes   = ( $offset - $hours );
			$sign      = ( $offset < 0 ) ? '-' : '+';
			$abs_hour  = abs( $hours );
			$abs_mins  = abs( $minutes * 60 );
			$tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
			return $tz_offset;
		}

		function hidden( $count = true ) {
			$wpmr    = wp_malware_removal();
			$files   = $wpmr->return_all_files();
			$hidden  = array_filter(
				$files,
				function ( $v ) {
					return ( empty( explode( '.', basename( $v ) )[0] ) || empty( explode( '.', basename( dirname( $v ) ) )[0] ) ) ? true : false;
				}
			);
			$hidden  = array_values( $hidden );
			$newlist = array();
			foreach ( $hidden as $k => $v ) {
				$parts = explode( '.', basename( dirname( $v ) ) );
				if ( empty( $parts [0] ) ) {
					$newlist[ dirname( $v ) ] = "\t" . '[DIR] ' . dirname( $v );
				}
				$newlist[ $v ] = "\t" . '[FILE] ' . $v;
			}
			if ( $count ) {
				WP_CLI::log( count( $newlist ) . ' hidden files / folders. Run `wp malcure hidden` to get exact names.' );
			} else {
				WP_CLI::log( implode( "\n", $newlist ) . "\n" );
			}
		}

		function help() {
			WP_CLI::log( "\t" );
			WP_CLI::log( WP_CLI::colorize( '%N%W████████████████████████████' ) );
			WP_CLI::log( WP_CLI::colorize( '%N%W████    HELP & USAGE    ████' ) );
			WP_CLI::log( WP_CLI::colorize( '%N%W████████████████████████████' ) );
			// WP_CLI::log( WP_CLI::colorize( "%N%W╔═══════════════════════╗" ) );
			// WP_CLI::log( WP_CLI::colorize( "%N%W║════════ USAGE ════════║" ) );
			// WP_CLI::log( WP_CLI::colorize( "%N%W╚═══════════════════════╝" ) );
			WP_CLI::log( WP_CLI::colorize( "\t%Y" ) );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure help' ) );
			WP_CLI::log( WP_CLI::colorize( "\t%YThis help screen / information." ) );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure info%Y' ) );
			WP_CLI::log( WP_CLI::colorize( "\t%YDisplays WP info." ) );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure hidden%Y' ) );
			WP_CLI::log( WP_CLI::colorize( "\t%YList hidden files and directories." ) );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure register --mc-email=myemail@example.com --mc-fname="Firstname" --mc-lname="Lastname"' ) );
			WP_CLI::log( WP_CLI::colorize( "\t%YRegister (free) to get definition updates." ) );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure sync' ) );
			WP_CLI::log( WP_CLI::colorize( "\t%YUpdate definitions. Works only if you have enabled auto-update definitions in the web-UI; pass --mcforce=true to force sync." ) );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure status%Y' ) );
			WP_CLI::log( "\tDisplays license status." );
			WP_CLI::log( "\t" );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure activate licensekeyhere%Y' ) );
			WP_CLI::log( "\tActivates license key and sets up the install (including registration, definition update)." );
			WP_CLI::log( '' );
			WP_CLI::log( WP_CLI::colorize( '%rwp malcure deactivate%Y' ) );
			WP_CLI::log( "\tDeactivate license key." );
			WP_CLI::log( "\t" );

			WP_CLI::log( WP_CLI::colorize( '%rwp malcure extractlog --from=source_file --to=target_file%Y' ) );
			WP_CLI::log( "\tExtracts and saves JSON compliant output from log-file to target file." );
			WP_CLI::log( "\t" );

			WP_CLI::log( WP_CLI::colorize( '%rwp malcure show_file_type <file>%Y' ) );
			WP_CLI::log( "\tShow file information about the file." );
			WP_CLI::log( "\t" );

			WP_CLI::log( WP_CLI::colorize( '%rwp malcure reset%Y' ) );
			WP_CLI::log( "\tResets this plugin; pass --mcresetlogs to deletes scan-logs also." );
			WP_CLI::log( "\t" );

			WP_CLI::log( WP_CLI::colorize( '%rwp malcure scan%Y' ) );
			WP_CLI::log( "\tInitiate malware scan." );
			WP_CLI::log( "\t" );

			WP_CLI::log( WP_CLI::colorize( "%N%W\tThe following options are supported with scan:" ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcbatchsize=10" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tSets number of files to scan per loop / iteration to 10. Default is 50." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcskipdb" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tSkip database scan." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcskipfiles" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tSkip file scan." ) );

			// Need option to skip scan for redirects

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcsuspicious" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tShow suspicious files. Default \"false\"" ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcskipdirs=\"themes,uploads\"" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tSkip themes and uploads directories. Expects directory name(s); comma-separated." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcregex=\"/find_.*_me/is\"" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tMatch custom regular expression (in addition to the existing malware definitions)." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcdbquery=\"%script%\" --mcdbregex=\"/href=\/malware/\"" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tScan database with custom query and regular expression (in addition to the existing malware definitions)." . '%n' ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--log=<path_to_log_file>" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tCreate a JSON scan-log and save it to file." ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tFor security reasons, .php will be added to the file extension." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcfiles=<absolute_path_to_file>" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tScan some specific file." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcscanonlydirs=<relative_path_to_the_specific_directory>" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tScan some specific directory." ) );

			WP_CLI::log( WP_CLI::colorize( "%B\t\t--mcdebug" ) );
			WP_CLI::log( WP_CLI::colorize( "%n%w\t\t\tShow debug output." ) );

			WP_CLI::runcommand( 'malcure' );
		}

		function reset( $args = array(), $assoc_args = array() ) {
			$wpmr       = wp_malware_removal();
			$force      = $this->get_flag( $assoc_args, 'mcforce' );
			$reset_logs = $this->get_flag( $assoc_args, 'mcresetlogs' );
			if ( ! $force ) {
				WP_CLI::confirm( WP_CLI::colorize( "\n\n\t" . 'Resetting %W' . $wpmr->plugin_data['Name'] . '%n will require re-activation via license key like:' . "\n%8%9\t    wp malcure activate <your license key here>    \t%n\n\tYou'll find the license key in the email received at the time of purchase.\n" . "%1%_\t    Are you sure you want to reset " . $wpmr->plugin_data['Name'] . ' to factory defaults?    %N%n' ) );
			}
			WP_CLI::log( $wpmr->reset( $reset_logs ) );
			$this->aalert();
		}

		function show_file_type( $args, $assoc_args ) {
			$file = ! empty( $args[0] ) ? $args[0] : '.';
			if ( function_exists( 'exec' ) && is_readable( $file ) ) {
				// exec(string $command, array &$output = null, int &$result_code = null): string|false
				$out = exec( 'file -b --mime-encoding ' . escapeshellarg( $file ), $output, $return );
				print_r( $out . ' ' . $this->normalise_path( $file ) . ' ' . ( ! empty( $return ) ? $return : '' ) . PHP_EOL ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Required for CLI output display
			} else {
				print_r( 'Could not execute or access ' . $this->normalise_path( $file ) . PHP_EOL ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Required for CLI output display
			}
		}

		function activate( $args, $assoc_args ) {
			$action = 'activate_license';
			if ( empty( $args[0] ) ) {
				// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- No applicable for STDOUT
				fwrite( STDOUT, 'Enter your license key and hit ENTER...' . "\n" );
				$key = sanitize_text_field( trim( fgets( STDIN ) ) );
				$this->license_req( $action, $key );
			} else {
				$this->license_req( $action, $args[0] );
			}
		}

		function deactivate( $args, $assoc_args ) {
			$action = 'deactivate_license';
			if ( empty( $args[0] ) ) {
				$this->license_req( $action, false );
			} else {
				$this->license_req( $action, $args[0] );
			}
		}

		function status( $args, $assoc_args ) {
			$action = 'check_license';
			if ( empty( $args[0] ) ) {
				$this->license_req( $action, false );
			} else {
				$this->license_req( $action, $args[0] );
			}
		}

		private function license_req( $action = 'check_license', $key = false ) {
			$wpmr = wp_malware_removal();
			if ( $action == 'check_license' || $action == 'deactivate_license' ) {
				$key = $wpmr->get_setting( 'license_key' );
			}
			if ( empty( $key ) ) {
				if ( $action == 'check_license' ) {
					WP_CLI::error( 'Not activated. No saved license key.' );
				}
				WP_CLI::error( 'Please run with your license key like: ' . WP_CLI::colorize( '%Rwp malcure [activate|deactivate|status] <license key>%n' . "\n%Reg: %Ywp malcure activate yourlicensekey" . '%n' ) );
			}
			$key         = trim( $key );
			$url         = MALCURE_API . '?edd_action=' . $action . '&item_id=1725&license=' . $key . '&url=' . site_url();
			$response    = wp_safe_remote_request( $url, array( 'timeout' => $wpmr->timeout ) );
			$headers     = wp_remote_retrieve_headers( $response );
			$status_code = wp_remote_retrieve_response_code( $response );
			if ( 200 != $status_code ) {
				WP_CLI::error( 'Error ' . $status_code . ' fetching Update.' );
			}
			if ( is_wp_error( $response ) ) {
				WP_CLI::error( $response->get_error_message() );
			}
			$body   = wp_remote_retrieve_body( $response );
			$status = json_decode( $body, true );

			if ( is_null( $status ) ) {
				WP_CLI::error( 'Unparsable response data.' );
			}
			if ( $status['success'] != true ) {
				WP_CLI::error( sanitize_text_field( $status['license'] ) );
			}
			if ( ! empty( $status['success'] ) && $status['success'] == true ) {

				if ( $action == 'deactivate_license' ) {
					$wpmr->delete_setting( 'license_key' );
					$wpmr->clear_license_status();
					$name = $status['customer_name'];
					$name = array_filter( explode( ' ', $name ) );
					$fn   = empty( $name ) ? explode( '@', $email )[0] : array_shift( $name );
					WP_CLI::success( 'Deactivated!' );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '********************************************************************************%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G\t" . 'You are still a proud owner of the Malcure Advanced Edition. %N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G\t" . 'Thank you ' . $fn . '! Catch ya on some other WP install.%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '********************************************************************************%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N%n' ) );
				}

				if ( $action == 'activate_license' ) {
					WP_CLI::success( 'Activated! We are setting up everything...' );
					$wpmr->update_setting( 'license_key', $key );
					$wpmr->save_license_status( $status );
					$email = $status['customer_email'];
					$wpmr  = wp_malware_removal();
					$name  = $status['customer_name'];
					$name  = array_filter( explode( ' ', $name ) );
					$fn    = empty( $name ) ? explode( '@', $email )[0] : array_shift( $name );
					$ln    = empty( $name ) ? explode( '@', $email )[0] : array_shift( $name );
					if ( ! $wpmr->is_registered() ) {
						$wpmr->wpmr_cli_register( $email, $fn, $ln, true );
					}
					$this->sync(
						array(),
						array(
							'mcforce' => 1,
							'silent'  => 1,
						)
					);
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '********************************************************************************%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G\t" . 'voilà! You are a proud owner of the professional plan. %N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G\t" . 'Thank you ' . $fn . '! We\'ve set up everything for you!%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '********************************************************************************%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N%n' ) );
					// $this->help();
					WP_CLI::log( WP_CLI::colorize( "\t%G" . '%N' ) );
					WP_CLI::log( WP_CLI::colorize( "\t%Y" . 'Run \'wp malcure help\' for documentation. %N' ) );
				}

				if ( $action == 'check_license' ) {
					WP_CLI::log( WP_CLI::colorize( '%n%wLicense Info:%n' ) );
					foreach ( $status as $key => $value ) {
						if (
							in_array(
								$key,
								array( 'license', 'customer_email', 'site_count', 'activations_left', 'license_limit', 'expires' )
							) ) {
							$formatted_key   = ucwords( str_replace( '_', ' ', $key ) );
							$formatted_value = $value;
							// Format specific values for better readability
							if ( $key === 'license' ) {
								$formatted_key   = 'License';
								$formatted_value = ucfirst( $value );
							} elseif ( $key === 'expires' ) {
								$formatted_key = 'Expires';
							} elseif ( $key === 'customer_email' ) {
								$formatted_key = 'Customer Email';
							} elseif ( $key === 'license_limit' ) {
								$formatted_key   = 'License Limit';
								$formatted_value = ( $value == 0 ) ? 'unlimited' : $value;
							} elseif ( $key === 'site_count' ) {
								$formatted_key = 'Site Count';
							} elseif ( $key === 'activations_left' ) {
								$formatted_key   = 'Activations Left';
								$formatted_value = ( $value == 999999 || $value == 0 ) ? 'unlimited' : $value;
							}

							WP_CLI::log( "\t" . str_pad( $formatted_key, 18, ' ', STR_PAD_RIGHT ) . ': ' . $formatted_value );
						}
					}
				}
			}
		}

		private function brand_color() {
			return '%B';
		}

		private function heading_format() {
			return '%W%9';
		}

		private function llog( $str ) {
			print_r( $str ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Required for debug logging functionality
			print_r( "\n" ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Required for debug logging functionality
		}

		private function get_severity_format( $status ) {
			$status = strtoupper( $status );
			switch ( $status ) {
				case 'CLEAN':
					return '%g'; // green
				case 'SKIPPED':
					return '%b'; // blue
				case 'SUSPICIOUS':
					return '%y'; // yellow
				case 'HIGH':
					return '%m'; // magenta
				case 'SEVERE':
					return '%r'; // red
				default:
					return '%w'; // grey
			}
		}

		private function mc_get_bool( $var ) {
			return filter_var( $var, FILTER_VALIDATE_BOOLEAN );
		}

		private function memory_human( $size ) {
			$unit = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB' );
			return @round( $size / pow( 1024, ( $i = floor( log( $size, 1024 ) ) ) ), 2 ) . ' ' . $unit[ $i ];
		}

		private function log( $message, $append = true ) {
			if ( ! $this->logging ) {
				return;
			}

			$logfile = $this->logfile;
			$flags   = LOCK_EX;
			if ( $append ) {
				// $flags = FILE_APPEND | $flags;
				$contents = $this->get_log( $this->logfile );
				if ( ! empty( $contents ) ) {
					$contents = json_decode( $contents, 1 );
					if ( is_null( $contents ) ) {
						WP_CLI::warning( 'Could not decode log contents!' );
						WP_CLI::warning( 'Malcure logging may be corrupt.' );
						$contents = array();
					}
				} else {
					$contents = array();
				}
				$message = array_merge( $contents, $message );

				$message = $this->logfile_header . json_encode( $message, JSON_PRETTY_PRINT );
			}

			file_put_contents( $logfile, $message, $flags );
		}

		private function get_log( $file ) {
			$contents = file_get_contents( $file );
			if ( empty( $contents ) ) {
				return '';
			}
			$replace  = '/.*' . preg_quote( $this->log_marker, '/' ) . '[\n\r]*' . '/s';
			$contents = preg_replace( $replace, '', $contents );
			return $contents;
		}

		function extractlog( $args = array(), $assoc_args = array() ) {
			if ( empty( $assoc_args['from'] ) ) {
				WP_CLI::error( 'No "from" argument passed. Pass the path of the logfile like wp malcure putlog --from=log.json.php' );
			}
			if ( empty( $assoc_args['to'] ) ) {
				WP_CLI::error( 'No "to" argument passed. Pass the path of the logfile like wp malcure putlog --to=log.json' );
			}
			$from     = trailingslashit( $this->normalise_path( pathinfo( $assoc_args['from'] )['dirname'] ) ) . pathinfo( $assoc_args['from'] )['basename'];
			$to       = trailingslashit( $this->normalise_path( pathinfo( $assoc_args['to'] )['dirname'] ) ) . pathinfo( $assoc_args['to'] )['basename'];
			$contents = file_get_contents( $from );
			if ( empty( $contents ) ) {
				WP_CLI::error( 'Malcure Scan Log file ' . $to . ' is empty.' );
			}
			$replace  = '/.*' . preg_quote( $this->log_marker, '/' ) . '[\n\r]*' . '/s';
			$contents = preg_replace( $replace, '', $contents );
			if ( file_put_contents( $to, $contents ) ) {
				WP_CLI::success( "Malcure Scan Log Successfully Extracted JSON format\n\tFrom:\t$from\n\tTo\t$to" );
			} else {
				WP_CLI::error( 'Could not write to file ' . $to );
			}
		}

		private function prerequisites() {
			$this->logfile_header = '<?php' . PHP_EOL . 'error_log( \'⥳⥳⥳⥳ MALCURE SCAN LOG ⥴⥴⥴⥴\' );' . PHP_EOL . 'echo \'<!-- M -->\';' . PHP_EOL . 'exit;' . PHP_EOL . '// This file is intentionally output as .php for protection from prying eyes && security reasons.' . PHP_EOL . '// You can programatically handle the contents in supported format following the following marker:' . PHP_EOL . $this->log_marker . PHP_EOL;
			$wpmr                 = wp_malware_removal();
			$home                 = set_url_scheme( get_option( 'home' ), 'http' );
			$siteurl              = set_url_scheme( get_option( 'siteurl' ), 'http' );
			if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
				$this->wp_custom_dir = true;
				$this->wp_home_dir   = trailingslashit( $this->normalise_path( ABSPATH ) );
			}
		}

		private function test_index_php( $path = '' ) {
			$wpmr = wp_malware_removal();
			if ( ! $path ) {
				$path = $wpmr->get_home_dir();
			}
			$parent = dirname( $path );
			$this->llog( PHP_EOL );
			$this->llog( 'path :' . $path );
			$this->llog( 'parent :' . $parent );
			$index_path = '';
			$this->llog( 'Searching for index.php in :' . $parent );
			if ( is_readable( trailingslashit( $parent ) . 'index.php' ) ) {
				$index_path = trailingslashit( $parent ) . 'index.php';
			} else {
				$this->llog( 'cannot locate index.php in :' . $parent );
				return;
			}

			$index_code = file_get_contents( $index_path );

			$regex = '/^\s*\brequire\b.*?[\'\"]\/?(' . basename( $path ) . ')\/wp\-blog\-header\.php[\'\"]/m';
			$this->llog( 'Regex: ' . $regex );

			if ( preg_match( $regex, $index_code, $matches ) ) {
				$path = trailingslashit( $this->normalise_path( $parent ) );
				$this->llog( $matches );
				$this->llog( $path );
				return $path;
			} else {
				return $this->test_index_php( $parent );
			}
		}

		private function normalise_path( $path ) {
			$realpath = wp_normalize_path( realpath( $path ) );
			if ( $realpath ) {
				return $realpath;
			}
			return wp_normalize_path( $path );
		}

		private function get_flag( $a = array(), $k = false ) {
			if ( is_array( $a ) && array_key_exists( $k, $a ) ) {
				return true;
			}
			return false;
		}

		function aalert() {
			fprintf( STDOUT, '%s', "\x07" );
		}

		function saas_test_api() {
			$wpmr   = wp_malware_removal();
			$result = $wpmr->saas_request( 'saas_test_api', array( 'log' => true ) );

			$wpmr->flog( $result );
			WP_CLI::log( 'Check output in ' . trailingslashit( $wpmr->dir ) . 'log.log.' );
		}

		function debug( $args = array(), $assoc_args = array() ) {
			$wpmr = wp_malware_removal();
		}
	}

	WP_CLI::add_command( 'malcure', 'Malcure_Advanced' );
}
