<?php

namespace iThemesSecurity\Site_Scanner;

final class Factory {
	const ENTRIES = [ 'vulnerabilities', 'blacklist', 'malware' ];

	/**
	 * Makes a scan result for a log item.
	 *
	 * @param int $log_id The log id.
	 *
	 * @return Scan|\WP_Error
	 */
	public function for_log_id( $log_id ) {
		$log_type = \ITSEC_Modules::get_setting( 'global', 'log_type' );

		if ( 'file' === $log_type ) {
			return new \WP_Error( 'itsec_site_scanner_factory_invalid_log_type', __( 'Logs must be stored in the database to retrieve past scans.', 'it-l10n-ithemes-security-pro' ) );
		}

		$entry = \ITSEC_Log::get_entry( $log_id );

		return $this->for_log_item( $entry );
	}

	/**
	 * Makes a scan result for a log item.
	 *
	 * @param array $log
	 *
	 * @return Scan|\WP_Error
	 */
	public function for_log_item( $log ) {
		$valid = \ITSEC_Site_Scanner_Util::is_log_item_valid_scan( $log );

		if ( is_wp_error( $valid ) ) {
			return $valid;
		}

		if ( ! isset( $log['data']['results'] ) ) {
			return new \WP_Error( 'itsec_site_scanner_factory_malformed_log_item', __( 'Log item is malformed.', 'it-l10n-ithemes-security-pro' ) );
		}

		$id   = (int) $log['id'];
		$code = $log['code'];
		$time = new \DateTimeImmutable( $log['init_timestamp'], new \DateTimeZone( 'UTC' ) );

		$results = $log['data']['results'];

		if ( is_wp_error( $results ) ) {
			return Scan::as_error( $id, $code, '', $time, $results );
		}

		$url     = isset( $results['url'] ) ? $results['url'] : '';
		$entries = ! empty( $results['entries'] ) ? $this->make_entries( $results['entries'] ) : [];
		$errors  = ! empty( $results['errors'] ) ? $results['errors'] : [];

		return new Scan( $id, $code, $url, $time, $entries, $errors );
	}

	/**
	 * Makes a Scan object from the API response.
	 *
	 * @param array|\WP_Error $response
	 *
	 * @return Scan
	 */
	public function for_api_response( $response ) {
		$id   = 0;
		$code = \ITSEC_Site_Scanner_Util::get_scan_result_code( $response );
		$time = new \DateTimeImmutable( 'now', new \DateTimeZone( 'UTC' ) );

		if ( is_wp_error( $response ) ) {
			$url = isset( $response->get_error_data()['url'] ) ? $response->get_error_data()['url'] : '';

			return Scan::as_error( $id, $code, $url, $time, $response );
		}

		$url     = isset( $response['url'] ) ? $response['url'] : '';
		$entries = ! empty( $response['entries'] ) ? $this->make_entries( $response['entries'] ) : [];
		$errors  = ! empty( $response['errors'] ) ? $response['errors'] : [];

		return new Scan( $id, $code, $url, $time, $entries, $errors );
	}

	/**
	 * Make a list of entries from the scan data.
	 *
	 * @param array $data
	 *
	 * @return Entry[]
	 */
	protected function make_entries( array $data ) {
		$entries = [];

		foreach ( self::ENTRIES as $slug ) {
			if ( ! isset( $data[ $slug ] ) ) {
				continue;
			}

			$entry = $this->{"make_{$slug}"}( $data[ $slug ] );

			if ( $entry instanceof Entry ) {
				$entries[] = $entry;
			}
		}

		return $entries;
	}

	/**
	 * Make a vulnerability entry from the scan data.
	 *
	 * @param array $data
	 *
	 * @return Entry
	 */
	protected function make_vulnerabilities( $data ) {
		if ( ! $data || ! is_array( $data ) ) {
			return new Entry( 'vulnerabilities', __( 'Known Vulnerabilities', 'it-l10n-ithemes-security-pro' ), Status::CLEAN );
		}

		$issues = [];

		foreach ( $data as $vulnerability ) {
			$link = \ITSEC_Site_Scanner_Util::authenticate_vulnerability_link( $vulnerability['link'] );

			foreach ( $vulnerability['issues'] as $issue ) {
				if ( empty( $issue['id'] ) ) {
					continue;
				}

				$issues[] = new Vulnerability_Issue( $vulnerability, $issue['id'], $link );
			}
		}

		return new Entry( 'vulnerabilities', __( 'Known Vulnerabilities', 'it-l10n-ithemes-security-pro' ), Status::WARN, $issues );
	}

	/**
	 * Make a blacklist entry from the scan data.
	 *
	 * @param array $data
	 *
	 * @return Entry
	 */
	protected function make_blacklist( $data ) {
		$issues = [];
		$status = Status::CLEAN;

		if ( $data && is_array( $data ) ) {
			foreach ( $data as $details ) {
				$issue    = new Blacklist_Issue( $details );
				$issues[] = $issue;

				if ( $issue->get_status() === Status::WARN ) {
					$status = $issue->get_status();
				}
			}
		}

		return new Entry( 'blacklist', __( 'Blocklist', 'it-l10n-ithemes-security-pro' ), $status, $issues );
	}

	/**
	 * Makes a malware entry from the scan data.
	 *
	 * @param array $data
	 *
	 * @return Entry
	 */
	protected function make_malware( $data ) {
		if ( ! $data || ! is_array( $data ) ) {
			return new Entry( 'malware', __( 'Malware', 'it-l10n-ithemes-security-pro' ), Status::CLEAN );
		}

		$issues = [];

		foreach ( $data as $malware ) {
			$issues[] = new Malware_Issue( $malware );
		}

		return new Entry( 'malware', __( 'Malware', 'it-l10n-ithemes-security-pro' ), Status::WARN );
	}
}
