<?php

namespace WP_Defender\Behavior\Scan_Item;

use Calotes\Component\Behavior;
use WP_Defender\Behavior\Scan_Item\Plugin_Integrity;
use WP_Defender\Component\Error_Code;
use WP_Defender\Component\Quarantine as Quarantine_Component;
use WP_Defender\Model\Scan;
use WP_Defender\Model\Scan_Item;
use WP_Defender\Traits\File_Operations;
use WP_Defender\Traits\Formats;
use WP_Defender\Traits\IO;
use WP_Defender\Traits\Plugin;
use WP_Defender\Traits\Theme;
use WP_Defender\Helper\File as File_Helper;

class Malware_Result extends Behavior {
	use Formats, IO, Plugin, Theme, File_Operations;

	private const REMOTE_FILE_IDENTICAL = 'REMOTE_FILE_IDENTICAL';
	private const REMOTE_FILE_DIFFER = 'REMOTE_FILE_DIFFER';
	private const REMOTE_FILE_NOT_EXISTS = 'REMOTE_FILE_NOT_EXISTS';
	private const REPAIR_NOT_IMPLEMENTED = 'REPAIR_NOT_IMPLEMENTED';

	public function to_array() {
		$file = $this->owner->raw_data['file'];
		$file_created_at = @filemtime( $file );
		if ( $file_created_at ) {
			$file_created_at = $this->format_date_time( $file_created_at );
		} else {
			$file_created_at = 'n/a';
		}
		$file_size = @filesize( $file );
		if ( ! $file_size ) {
			$file_size = 'n/a';
		} else {
			$file_size = $this->format_bytes_into_readable( $file_size );
		}

		$is_quarantinable = $this->is_quarantinable( $file );

		$quarantine_data = class_exists( 'WP_Defender\Component\Quarantine' ) ?
			wd_di()->get( Quarantine_Component::class )->scan_item_data( $this->owner ) :
			[ 'is_quarantinable' => $is_quarantinable ];

		return array_merge(
			[
				'id' => $this->owner->id,
				'type' => Scan_Item::TYPE_SUSPICIOUS,
				'file_name' => pathinfo( $file, PATHINFO_BASENAME ),
				'full_path' => $file,
				'date_added' => $file_created_at,
				'size' => $file_size,
				'short_desc' => __( 'Suspicious function found', 'defender-security' ),
			],
			$quarantine_data
		);
	}

	/**
	 * @return array
	 */
	public function ignore(): array {
		$scan = Scan::get_last();
		$scan->ignore_issue( $this->owner->id );

		return [ 'message' => __( 'The suspicious file has been successfully ignored.', 'defender-security' ) ];
	}

	/**
	 * @return array
	 */
	public function unignore(): array {
		$scan = Scan::get_last();
		$scan->unignore_issue( $this->owner->id );

		return [ 'message' => __( 'The suspicious file has been successfully restored.', 'defender-security' ) ];
	}

	/**
	 * Delete the file, or whole folder.
	 *
	 * @return array|\WP_Error
	 */
	public function delete() {
		$data = $this->owner->raw_data;
		$scan = Scan::get_last();
		$path = $data['file'];
		/**
		 * If the path inside theme or plugin folder, then we remove the whole plugin/theme for prevent fatal error.
		 * If the path is to wp-config, we can't delete it.
		 */
		if ( defender_wp_config_path() === $path ) {
			return new \WP_Error(
				Error_Code::INVALID,
				__( 'wp-config.php can\'t be removed. Please remove the suspicious code manually.', 'defender-security' )
			);
		}
		// If it's not relative path, which mean it is something lay on wp-content folder, we can delete it.
		if ( ! $this->delete_infected_file( $path ) ) {
			return $this->get_permission_error( $path );
		}

		return $this->after_delete( $path, $scan, 'malware' );
	}

	/**
	 *  Return the source code depending on the type of the issue.
	 *
	 * @return void|array
	 */
	public function pull_src() {
		$data = $this->owner->raw_data;

		$file_path = wp_normalize_path( $data['file'] );

		$active = false;
		$type_src = '';
		if ( ! file_exists( $file_path ) ) {
			return [
				'origin' => '',
				'mapper' => [],
				'active' => $active,
				'type_src' => $type_src,
			];
		}
		// Is this the path to the plugin or theme file?
		if ( false !== stripos( $file_path, wp_normalize_path( WP_PLUGIN_DIR ) ) ) {
			$active = $this->is_active_plugin( $file_path );
			$type_src = 'plugin';
		} elseif ( false !== stripos( $file_path, wp_normalize_path( $this->get_path_of_themes_dir() ) ) ) {
			$active = $this->is_active_theme( $file_path );
			$type_src = 'theme';
		}

		$markers = [];
		foreach ( $data as $item ) {
			if ( isset( $item['catches'] ) ) {
				foreach ( $item['catches'] as $catch ) {
					$markers = array_merge( $markers, $catch['mapper'] );
				}
			}
		}

		$success_data = [
			'origin' => file_get_contents( $file_path ),
			'mapper' => $markers,
			'active' => $active,
			'type_src' => $type_src,
		];

		if ( $type_src === 'plugin' ) {
			$directory_name = $this->get_plugin_directory_name( $file_path );
			$plugin_headers = $this->get_plugin_headers( $file_path );
			if ( empty( $plugin_headers ) ) {
				return $success_data;
			}

			$plugin_headers = reset( $plugin_headers );

			$success_data['local_file_state'] = $this->get_local_file_state(
				$file_path,
				$directory_name,
				$plugin_headers
			);

			$success_data['directory_name'] = $directory_name;
			$success_data['author'] = $plugin_headers['Author'] ?? '';
			$success_data['author_uri'] = $plugin_headers['AuthorURI'] ?? '';
		}

		wp_send_json_success( $success_data );
	}

	/**
	 * Get local file state which used to handle repair or delete in front end.
	 *
	 * @param string $file_path File full path.
	 * @param string $directory_name Plugin directory name.
	 * @param array $plugin_headers Plugin headers.
	 *
	 * @return string Get local file state.
	 */
	private function get_local_file_state( $file_path, $directory_name, $plugin_headers ): string {
		$file_relative_path = $this->get_plugin_relative_path( $file_path );

		$source_file_url = $this->get_file_url(
			$directory_name,
			$plugin_headers['Version'],
			$file_relative_path
		);

		/**
		 * @var File_Helper
		 */
		$file_helper = wd_di()->get( File_Helper::class );

		$is_identical = $file_helper->is_identical_content(
			$file_path,
			$source_file_url
		);

		if (
			is_wp_error( $is_identical ) &&
			$is_identical->get_error_code() === 'http_404'
		) {
			// If file is not exists in remote then it may not be repaired.
			$local_file_state = self::REMOTE_FILE_NOT_EXISTS;
		} elseif ( $is_identical === false ) {
			$local_file_state = self::REMOTE_FILE_DIFFER;
		} else {
			$local_file_state = self::REMOTE_FILE_IDENTICAL;
		}

		return $local_file_state;
	}

	/**
	 * A wrapper method for WP_Defender\Behavior\Scan_Item\Plugin_Integrity::resolve().
	 */
	public function resolve() {

		$file_path = $this->owner->raw_data['file'];

		$directory_name = $this->get_plugin_directory_name( $file_path );
		$plugin_headers = $this->get_plugin_headers( $file_path );
		$plugin_headers = reset( $plugin_headers );

		$local_file_state = $this->get_local_file_state(
			$file_path,
			$directory_name,
			$plugin_headers
		);

		if ( $local_file_state !== 'REMOTE_FILE_DIFFER' ) {
			return;
		}

		/**
		 * @var Plugin_Integrity
		 */
		$plugin_integrity = wd_di()->get( Plugin_Integrity::class );

		$this->owner->raw_data['type'] = 'modified';

		$plugin_integrity->owner = $this->owner;

		return $plugin_integrity->resolve();
	}
}
