<?php

namespace WP_Defender\Behavior\Scan;

use Calotes\Component\Behavior;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Exceptions\TokenizerException;
use PHP_CodeSniffer\Tokenizers\PHP;
use WP_Defender\Component\Scan\Tokens;
use WP_Defender\Traits\IO;

/**
 * We need to verify the result to avoid false positive.
 *
 * Class Malware_Deep_Scan
 * @package WP_Defender\Behavior\Scan
 */
class Malware_Deep_Scan extends Behavior {
	use IO;

	/**
	 * @param string $file
	 * @param array $rules
	 * @param array $quick_found
	 *
	 * @return bool|array
	 */
	public function do_deep_scan( $file, $rules, $quick_found ) {
		$this->load_phpcodesniffer_needed();
		if ( ! defined( 'PHP_CODESNIFFER_VERBOSITY' ) ) {
			define( 'PHP_CODESNIFFER_VERBOSITY', 0 );
		}
		if ( ! defined( 'PHP_CODESNIFFER_CBF' ) ) {
			define( 'PHP_CODESNIFFER_CBF', false );
		}
		$content = file_get_contents( $file );
		try {
			$parser = new PHP( $content, $this->make_phpsniff_config(), PHP_EOL );
			Tokens::$tokens = $parser->getTokens();
			foreach ( $quick_found as &$item ) {
				if ( ! isset( $item['catches'] ) || ! is_array( $item['catches'] ) ) {
					continue;
				}
				foreach ( $item['catches'] as &$catch ) {
					$offset = $catch['offset'];
					$catch['line'] = Tokens::get_line_from_offset( $content, $offset );
					$catch['mapper'] = Tokens::get_offsets_map( Tokens::get_tokens_by_line( $catch['line'] ) );
					// $code = Tokens::formatter( Tokens::get_tokens_by_line( $catch['line'] ) );
					$tokens = Tokens::get_tokens_by_line( $catch['line'] );
					foreach ( $tokens as $k => $token ) {
						if ( in_array(
							$token['code'], [
								T_DOC_COMMENT_WHITESPACE,
								T_DOC_COMMENT_STAR,
								T_DOC_COMMENT_WHITESPACE,
								T_DOC_COMMENT_STRING,
							]
						) ) {
							unset( $tokens[ $k ] );
						}
					}
					$tokens = array_filter( $tokens );
					if ( empty( $tokens ) ) {
						// This mean the catch is comment, do nothing.
						return false;
					}
					unset( $catch['text'] );
					//$catch['code'] = $code;
				}
			}

			return $quick_found;
		} catch ( TokenizerException $e ) {
			$this->log( $e->getMessage() );
		}

		return false;
	}

	private function load_phpcodesniffer_needed() {
		$ds = DIRECTORY_SEPARATOR;
		$vendor_path = defender_path( 'src/extra/php-codesniffer-php-token' );
		if ( ! class_exists( \PHP_CodeSniffer\Util\Tokens::class ) ) {
			require_once $vendor_path . $ds . 'Util' . $ds . 'Tokens.php';
		}
		if ( ! class_exists( \PHP_CodeSniffer\Util\Common::class ) ) {
			require_once $vendor_path . $ds . 'Util' . $ds . 'Common.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Tokenizers\Tokenizer::class ) ) {
			require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Tokenizer.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Tokenizers\PHP::class ) ) {
			require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'PHP.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Tokenizers\Comment::class ) ) {
			require_once $vendor_path . $ds . 'Tokenizers' . $ds . 'Comment.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Util\Standards::class ) ) {
			require_once $vendor_path . $ds . 'Util' . $ds . 'Standards.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Exceptions\RuntimeException::class ) ) {
			require_once $vendor_path . $ds . 'Exceptions' . $ds . 'RuntimeException.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Exceptions\DeepExitException::class ) ) {
			require_once $vendor_path . $ds . 'Exceptions' . $ds . 'DeepExitException.php';
		}

		if ( ! class_exists( \PHP_CodeSniffer\Config::class ) ) {
			require_once $vendor_path . $ds . 'Config.php';
		}
	}

	/**
	 * @return Config
	 */
	private function make_phpsniff_config() {
		$config = new \stdClass();
		$config->files = [];
		$config->standards = [ 'PEAR' ];
		$config->verbosity = 0;
		$config->interactive = false;
		$config->cache = false;
		$config->cacheFile = null;
		$config->colors = false;
		$config->explain = false;
		$config->local = false;
		$config->showSources = true;
		$config->showProgress = false;
		$config->quiet = false;
		$config->annotations = true;
		$config->parallel = 1;
		$config->tabWidth = 0;
		$config->encoding = 'utf-8';
		$config->extensions = [
			'php' => 'PHP',
			'inc' => 'PHP',
			'js' => 'JS',
			'css' => 'CSS',
		];
		$config->sniffs = [];
		$config->exclude = [];
		$config->ignored = [];
		$config->reportFile = null;
		$config->generator = null;
		$config->filter = null;
		$config->bootstrap = [];
		$config->basepath = null;
		$config->reports = [ 'full' => null ];
		$config->reportWidth = 'auto';
		$config->errorSeverity = 5;
		$config->warningSeverity = 5;
		$config->recordErrors = true;
		$config->suffix = '';
		$config->stdin = true;
		$config->stdinContent = null;
		$config->stdinPath = null;
		$config->unknown = [];

		return $config;
	}
}
