<?php

namespace iThemesSecurity\Site_Scanner;

final class Vulnerability_Issue implements Issue {
	use Issue_Trait;

	/** @var array */
	private $vulnerability;

	/** @var array */
	private $issue;

	/** @var array */
	private $firewall_rules;

	/**
	 * Vulnerability constructor.
	 *
	 * @param array  $vulnerability The full vulnerability details for the software item.
	 * @param string $issue_id      The id of this particular issue.
	 * @param string $details_link  The link to more details about the issue.
	 */
	public function __construct( array $vulnerability, $issue_id, $details_link ) {
		$match = wp_list_filter( $vulnerability['issues'], [ 'id' => $issue_id ] );
		$issue = reset( $match );

		$this->id             = $issue_id;
		$this->description    = $issue['title'];
		$this->status         = \ITSEC_Site_Scanner_Util::is_issue_muted( $issue_id ) ? Status::CLEAN : Status::WARN;
		$this->link           = \ITSEC_Core::get_admin_page_url( 'vulnerabilities', '/vulnerability/' . $issue_id );
		$this->issue          = $issue;
		$this->vulnerability  = $vulnerability;
		$this->entry          = 'vulnerabilities';
		$this->firewall_rules = array_filter(
			isset( $vulnerability['firewall_rules'] ) ? $vulnerability['firewall_rules'] : [],
			function ( array $rule ) use ( $issue_id ) {
				return $issue_id === $rule['vulnerability'];
			}
		);
	}

	public function get_meta() {
		$meta = [
			'issue' => $this->issue,
			'type'  => $this->vulnerability['type'],
		];

		if ( isset( $this->vulnerability['software'] ) ) {
			$meta['software'] = $this->vulnerability['software'];
		}

		return $meta;
	}

	/**
	 * Get the version the vulnerability was fixed in.
	 *
	 * @return string|null
	 */
	public function get_fixed_in() {
		return $this->issue['fixed_in'];
	}

	/**
	 * Get the applicable firewall rules for this vulnerability.
	 *
	 * @return array
	 */
	public function get_firewall_rules(): array {
		return $this->firewall_rules;
	}

	/**
	 * Get the severity for the score.
	 *
	 * @return string
	 */
	public function get_severity(): string {
		$score = $this->issue['score'] ?? 0;

		if ( $score < 3 ) {
			return 'low';
		}

		if ( $score < 7 ) {
			return 'medium';
		}

		if ( $score < 9 ) {
			return 'high';
		}

		return 'critical';
	}

	/**
	 * Patchstack priority is used. If it is unknown, we calculate it from a score.
	 * Patchstask usual values are 1 (low), 2 (medium), 3 and more (high).
	 *
	 * @return int Patch priority
	 */
	public function get_priority(): int {
		if ( ! isset( $this->issue['patch_priority'] ) || ! is_int( $this->issue['patch_priority'] ) ) {
			return self::convert_severity_to_priority( $this->get_severity() );
		}

		$patch_priority = $this->issue['patch_priority'];

		if ( $patch_priority < 1 ) {
			return Priority::LOW;
		}

		if ( $patch_priority > 3 ) {
			return Priority::HIGH;
		}

		return $patch_priority;
	}

	/**
	 * Convert severity key to priority value.
	 *
	 * @psalm-param 'low' | 'medium' | 'high' | 'critical' $severity
	 * @param string $severity
	 *
	 * @return int
	 */
	private static function convert_severity_to_priority( string $severity ): int {
		switch ($severity) {
			case 'low':
				return Priority::LOW;
			case 'medium':
				return Priority::MEDIUM;
			default:
				return Priority::HIGH;
		}
	}
}
