<?php

namespace WP_Defender\Behavior\Scan;

use Calotes\Component\Behavior;
use WP_Defender\Traits\IO;

/**
 * This will fast travel all yara rules to quickly get files seem suspicious, this is catchy.
 *
 * Class Malware_Quick_Scan
 * @package WP_Defender\Behavior\Scan
 */
class Malware_Quick_Scan extends Behavior {
	use IO;

	private $content;

	/**
	 * @param string $file
	 * @param array $rules
	 *
	 * @return array
	 */
	public function do_quick_scan( string $file, array $rules ): array {
		$total_issues = 0;
		$qs_detail = [];
		if ( file_exists( $file ) ) {
			$content = file_get_contents( $file );
			$this->content = $content;
			foreach ( $rules as $rule ) {
				$strings = $rule['strings'];
				$local_issues = [
					'identifier' => $rule['identifier'],
					'catches' => [],
				];
				foreach ( $strings as $string ) {
					$offset = false;
					if ( $string['type'] === 0 ) {
						$offset = strpos( $content, $string['text'] );
						$text = $string['text'];
					} else {
						$pattern = "/{$string['text']}/";
						$is_match = preg_match_all( $pattern, $content, $matches,
							PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE );
						if ( $is_match ) {
							$offset = $matches[0][0][1];
							$text = $matches[0][0][0];
						}
					}
					if ( false === $offset ) {
						continue;
					}
					$local_issues['catches'] [] = [
						'id' => $string['id'],
						'text' => $text,
						'offset' => $offset,
					];
				}
				if ( empty( $local_issues['catches'] ) ) {
					continue;
				}

				if ( $this->condition_valid( $local_issues['catches'], $rule ) ) {
					$total_issues ++;
					$qs_detail[] = $local_issues;
				}
			}
		}

		return [ $total_issues, $qs_detail ];
	}

	/**
	 * Parse the condition to see if this is an issue or not.
	 *
	 * @param $found
	 * @param $rule
	 *
	 * @return bool
	 */
	private function condition_valid( $found, $rule ): bool {
		$condition = explode( 'and', $rule['condition'] );
		$count = 0;
		foreach ( $condition as $item ) {
			$patterns = [
				'x_of_them' => '/([a-z0-9]+)\s*of\s*(.*)/',
				'id_at_pos' => '/(\$[a-zA-Z0-9]+)\s*at\s*(\d+)/',
			];
			foreach ( $patterns as $func => $pattern ) {
				if ( preg_match( $pattern, $item, $match ) ) {
					$count ++;
					$ret = $this->$func( $found, $match, $rule );
					if ( false === $ret ) {
						return false;
					}
				}
			}
		}
		if ( $count === 0 ) {
			$this->log( $rule['identifier'], 'malware_scan.log' );
			$this->log( var_export( $found, true ), 'malware_scan.log' );

			return false;
		}

		return true;
	}

	/**
	 * This will parse the condition x of y.
	 *
	 * @param $found
	 * @param $match
	 * @param $rule
	 *
	 * @return bool
	 */
	private function x_of_them( $found, $match, $rule ): bool {
		$num = $match[1];
		$num = 'all' === $num ? count( $rule['strings'] ) : $num;
		$num = 'any' === $num ? 1 : $num;

		if ( '(s*)' == $match[2] ) {
			// Hard code in this situation.
			$num -= 1;
		}
		//$this->log( sprintf( 'num %s', $num ), 'malware_scan.log' );
		//$this->log( sprintf( 'count %s', count( $found ) ), 'malware_scan.log' );

		return ( is_array($found) || $found instanceof \Countable ? count( $found ) : 0 ) >= $num;
	}

	/**
	 * @param $found
	 * @param $match
	 * @param $rule
	 *
	 * @return bool
	 */
	private function id_at_pos( $found, $match, $rule ): bool {
		// Get the text to check.
		foreach ( $found as $item ) {
			if ( $item['id'] === $match[1] ) {
				return strpos( $this->content, $item['text'] ) == $match[2];
			}
		}

		return false;
	}
}
