<?php

namespace WP_Defender\Behavior\Scan;

use Calotes\Component\Behavior;
use Calotes\Helper\Array_Cache;
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_Verify_Result
 * @package WP_Defender\Behavior\Scan
 */
class Malware_Deep_Scan extends Behavior {
	use IO;

	/**
	 * @param $file
	 * @param $rules
	 * @param $quick_found
	 *
	 * @return bool
	 */
	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' ) ) {
			require_once $vendor_path . $ds . 'Util' . $ds . 'Tokens.php';
		}
		if ( ! class_exists( 'PHP_CodeSniffer\Util\Common' ) ) {
			require_once $vendor_path . $ds . 'Util' . $ds . 'Common.php';
		}

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

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

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

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

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

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

		if ( ! class_exists( 'PHP_CodeSniffer\Config' ) ) {
			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;
	}
}