<?php
/**
 * Dashboard controller class.
 *
 * @link       https://www.cookieyes.com/
 * @since      3.0.0
 *
 * @package    WebToffee\CookieConsent\Admin\Modules\Scanner\Includes
 */

namespace WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes;
use WP_Error;
use WebToffee\CookieConsent\Lite\Integrations\Cookieyes;
use WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes\Controller;
use WebToffee\CookieConsent\Lite\Admin\Modules\Settings\Includes\Settings;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Dashboard controller class.
 *
 * @class       Controller
 * @version     3.0.0
 * @package     CookieYes
 */
class Cookie_Serve extends Cookieyes {

	/**
	 * Instance of the current class
	 *
	 * @var object
	 */
	private static $instance;
	/**
	 * Cookie items
	 *
	 * @var array
	 */
	public $languages;

	/**
	 * Last scan info
	 *
	 * @var array
	 */
	public $last_scan_info;

	/**
	 * Default scan data
	 *
	 * @var array
	 */
	private static $default = array(
		'scan_id'           => 0,
		'scan_status'       => '',
		'scan_token'        => '',
		'page_limit'        => '',
		'scan_result_token' => '',
	);

	/**
	 * Scanner controller object.
	 *
	 * @var object
	 */
	protected $controller;

	/**
	 * Return the current instance of the class
	 *
	 * @return object
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Load data
	 *
	 * @return array
	 */
	public function get_items() {
		$data = array();
		if ( ! $this->get_website_id() ) {
			return $data;
		}
		$response      = $this->get(
			'websites/' . $this->get_website_id() . '/scanner'
		);
		$response_code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $response_code ) {
			$response = json_decode( wp_remote_retrieve_body( $response ), true );
			$stats    = isset( $response['statistics'] ) ? $response['statistics'] : array();
			$data     = array(
				'cookies'    => isset( $stats['total_cookies'] ) ? $stats['total_cookies'] : 0,
				'scripts'    => isset( $stats['total_scripts'] ) ? $stats['total_scripts'] : 0,
				'categories' => isset( $stats['total_categories'] ) ? $stats['total_categories'] : 0,
				'pages'      => isset( $stats['total_pages'] ) ? $stats['total_pages'] : 0,
			);
		}
		return $data;
	}

	/**
	 * Initiate scan
	 *
	 * @return array
	 */
	public function initiate_scan( $controller ) {
		$data = array();
		if ( ! $this->get_api_url() ) {
			return $data;
		}
		// Set the page count for scan
		$count = 1;
		$urls  = $controller->get_pages();
		if ( isset( $urls ) && $urls['total'] ) {
			$count = $urls['total'];
		}
		$license      = new \WebToffee\CookieConsent\Lite\Admin\Modules\License\Includes\Controller();
		$license_data = $license->get_license_data();
		// Define the data to send in the request body
		$requestdata   = array(
			'url'               => home_url(),
			'email'             => $license_data['licence_email'],
			'license_key'       => $license_data['licence_key'],
			'page_count'        => $count,
			'license_version'   => '2.0',
			'scan_result_token' => $this->set_scan_instance(),
			'languages'         => Settings::get_instance()->get_selected_languages(),
		);
		$response      = $this->post(
			'/api/v2/scan/create',
			json_encode( $requestdata )
		);
		$response_code = wp_remote_retrieve_response_code( $response );

		// First check for WP_Error
		if ( is_wp_error( $response ) ) {
			return new WP_Error(
				'webtoffee_api_error',
				$response->get_error_message(),
				array( 'status' => 400 )
			);
		}

		// Get response body and try to decode it
		$response_body    = wp_remote_retrieve_body( $response );
		$decoded_response = json_decode( $response_body, true );

		// Check if response contains an error message
		if ( isset( $decoded_response['message'] ) ) {
			return new WP_Error(
				'webtoffee_api_error',
				$decoded_response['message'],
				array( 'status' => 400 )
			);
		}

		if ( 200 === $response_code ) {
			if ( isset( $decoded_response['status'] ) && 'error' === $decoded_response['status'] ) {
				return $decoded_response;
			}

			$controller->update_info(
				array(
					'scan_status' => sanitize_text_field( 'initiated' ),
					'scan_id'     => isset( $decoded_response['scan_id'] ) ? $decoded_response['scan_id'] : '',
					'scan_token'  => isset( $decoded_response['scan_token'] ) ? $decoded_response['scan_token'] : '',
					'page_limit'  => isset( $decoded_response['page_limit'] ) ? $decoded_response['page_limit'] : '',
				)
			);
			return $decoded_response;
		} else {
			return new WP_Error(
				'webtoffee_api_error',
				__( 'Failed to connect to the scanning service', 'webtoffee-cookie-consent' ),
				array( 'status' => $response_code )
			);
		}
	}
	/**
	 * Start scan
	 *
	 * @return array
	 */
	public function start_scan( $request, $controller ) {
		$data = array();
		if ( ! $this->get_api_url() ) {
			return $data;
		}
		$scan_id    = (int) $request['id'];
		$scan_token = isset( $request['scanToken'] ) ? $request['scanToken'] : '';
		$urls       = $controller->get_pages( $scan_id );
		if ( isset( $urls ) && $urls['log'] ) {
			$urls = $urls['log'];
		}
		foreach ( $urls as $key => $url ) {
			$urls[ $key ] = $this->append_bypasspath( $url );
		}
		// Filter to change usage static IP for scanning
		$use_static_ip = apply_filters( 'wcc_use_static_ip', false );
		// Define the data to send in the request body
		$requestdata   = array(
			'domain'              => home_url(),
			'urls'                => $urls,
			'concurrency'         => apply_filters( 'wcc_scanner_url_per_scan', 5 ),
			'websiteId'           => 'wp_plugin',
			'scanId'              => $scan_id,
			'useStaticIPs'        => $use_static_ip,
			'useStealthUserAgent' => apply_filters( 'wcc_enable_alternate_scanner', false ),
		);
		$response      = $this->post(
			'',
			json_encode( $requestdata ),
			$scan_token
		);
		$response_code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $response_code || 400 === $response_code ) {
			$response = json_decode( wp_remote_retrieve_body( $response ), true );
			if ( 200 === $response_code ) {
				if ( isset( $response['status'] ) && 'error' === $response['status'] ) {
					return $response;
				}
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'initiated' ),
						'scan_id'     => isset( $response['scan_id'] ) ? $response['scan_id'] : '',
						'scan_token'  => isset( $response['scan_token'] ) ? sanitize_text_field( $response['scan_token'] ) : '',
						'page_limit'  => isset( $response['page_limit'] ) ? $response['page_limit'] : '',
					)
				);
				return $response;
			} else {
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'initiated' ),
					)
				);
				return rest_ensure_response( $controller->get_info() );
			}
		} else {
			return new WP_Error(
				'webtoffee_rest_item_exists',
				__( 'Cannot start a scan.', 'webtoffee-cookie-consent' ),
				array( 'status' => 400 )
			);
		}
		return $response;
	}
	/**
	 * Abort scan
	 * @return array
	 */
	public function abort_scan( $request, $controller ) {
		$data = array();
		if ( ! $this->get_api_url() ) {
			return $data;
		}
		$scan_id       = (int) $request['id'];
		$scan_token    = isset( $request['scanToken'] ) ? $request['scanToken'] : '';
		$response      = $this->post(
			'/api/v1/scan/' . $scan_id . '/abort',
			'',
			$scan_token
		);
		$response_code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $response_code || 400 === $response_code ) {
			$response = json_decode( wp_remote_retrieve_body( $response ), true );
			if ( 200 === $response_code ) {
				if ( isset( $response['status'] ) && 'error' === $response['status'] ) {
					return $response;
				}
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'aborted' ),
						'scan_id'     => isset( $response['scan_id'] ) ? $response['scan_id'] : '',
					)
				);
				return $response;
			} else {
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'initiated' ),
					)
				);
				return rest_ensure_response( $controller->get_info() );
			}
		} else {
			return new WP_Error(
				'webtoffee_rest_item_exists',
				__( 'Cannot abort scan.', 'webtoffee-cookie-consent' ),
				array( 'status' => 400 )
			);
		}
		return $response;
	}

	/**
	 * Fetch scan status
	 * @return array
	 */
	public function fetch_scan_status( $request, $controller ) {
		$data = array();
		if ( ! $this->get_api_url() ) {
			return $data;
		}
		$scan_id       = (int) $request['id'];
		$scan_token    = isset( $request['scanToken'] ) ? $request['scanToken'] : '';
		$response      = $this->get(
			'/api/v1/scan/' . $scan_id . '/status',
			$data,
			$scan_token
		);
		$response_code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $response_code || 400 === $response_code ) {
			$response = json_decode( wp_remote_retrieve_body( $response ), true );
			if ( 200 === $response_code ) {
				if ( isset( $response['status'] ) && 'error' === $response['status'] ) {
					if ( isset( $response['error_message'] ) && 'Scan Failed.' === $response['error_message'] ) {
						$controller->update_info(
							array(
								'scan_status' => sanitize_text_field( 'failed' ),
							)
						);
						return array(
							'scan_id'     => $scan_id,
							'scan_status' => 'failed',
						);
					}
					return $response;
				}
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'completed' ),
						'scan_id'     => isset( $response['scan_id'] ) ? $response['scan_id'] : '',
					)
				);
				return $response;
			} else {
				$controller->update_info(
					array(
						'scan_status' => sanitize_text_field( 'in progress' ),
					)
				);
				return rest_ensure_response( $controller->get_info() );
			}
		} else {
			return new WP_Error(
				'webtoffee_rest_item_exists',
				__( 'Cannot fetch scan status.', 'webtoffee-cookie-consent' ),
				array( 'status' => 400 )
			);
		}
		return $response;
	}
	/**
	 * Fetch scan result
	 * @return array
	 */
	public function fetch_scan_result( $request, $controller ) {
		$data = array();
		if ( ! $this->get_api_url() ) {
			return $data;
		}
		$scan_id       = (int) $request['id'];
		$scan_token    = isset( $request['scanToken'] ) ? $request['scanToken'] : '';
		$response      = $this->get(
			'/api/v1/scan/' . $scan_id . '/result',
			$data,
			$scan_token
		);
		$response_code = wp_remote_retrieve_response_code( $response );
		if ( 200 === $response_code || 400 === $response_code ) {
			$response = json_decode( wp_remote_retrieve_body( $response ), true );
			if ( 200 === $response_code ) {
				if ( isset( $response['status'] ) && 'error' === $response['status'] ) {
					return $response;
				}
				//Fetch scan result
				$scan_result = $response['scan_result'];
				//Insert category data
				Controller::get_instance()->insert_categories( $scan_result );
				//Insert cookie details
				Controller::get_instance()->insert_cookies( $scan_result );

				$this->update_info(
					array(
						'scan_status' => sanitize_text_field( 'completed' ),
						'scan_id'     => isset( $response['scan_id'] ) ? $response['scan_id'] : '',
					)
				);
				/**
				 * Add prompt to renew consent after successful scan
				 * @since 3.2.0
				 */
				update_option( 'wcc_show_renew_consent_notice', true );

				return $response;
			} else {
				$this->update_info(
					array(
						'scan_status' => sanitize_text_field( 'in progress' ),
					)
				);
				return rest_ensure_response( $controller->get_info() );
			}
		} else {
			return new WP_Error(
				'webtoffee_rest_item_exists',
				__( 'Cannot fetch scan status.', 'webtoffee-cookie-consent' ),
				array( 'status' => 400 )
			);
		}
		return $response;
	}
	/**
	 * Get the last scan info
	 *
	 * @return array
	 */
	public function get_info() {
		if ( ! $this->last_scan_info ) {
			$data                 = get_option( 'cky_scan_details', self::$default );
			$timestamp            = strtotime( sanitize_text_field( $data['date'] ) );
			$formatted            = gmdate( 'd F Y H:i:s', $timestamp );
			$this->last_scan_info = array(
				'scan_id'     => absint( $data['scan_id'] ),
				'scan_status' => sanitize_text_field( $data['scan_status'] ),
				'scan_token'  => sanitize_text_field( $data['scan_token'] ),
			);
		}
		return $this->last_scan_info;
	}
	/**
	 * Update the last scan info to the option table
	 *
	 * @param array $data Scan data recieved from the CookieYes web app after initiating the scan.
	 * @return void
	 */
	public function update_info( $data = array() ) {
		$scan_data = get_option( 'wcc_scan_details', self::$default );
		$data      = array(
			'scan_status'       => sanitize_text_field( isset( $data['scan_status'] ) ? $data['scan_status'] : $scan_data['scan_status'] ),
			'scan_id'           => absint( isset( $data['scan_id'] ) ? $data['scan_id'] : $scan_data['scan_token'] ),
			'scan_token'        => sanitize_text_field( isset( $data['scan_token'] ) ? $data['scan_token'] : $scan_data['scan_token'] ),
			'page_limit'        => absint( isset( $data['page_limit'] ) ? $data['page_limit'] : $scan_data['page_limit'] ),
			'scan_result_token' => sanitize_text_field( isset( $data['scan_result_token'] ) ? $data['scan_result_token'] : $scan_data['scan_result_token'] ),
		);
		update_option( 'wcc_scan_details', $data );
		$this->last_scan_info = $data;
	}
	/*
	*
	*   Taking public pages of the website
	*/
	public function get_pages( $scan_id = '' ) {
		global $wpdb;

		$mxdata = 100;
		//taking query params
		$offset           = 0;
		$scan_id          = $scan_id;
		$total            = 0;
		$wt_cli_site_host = $this->get_host( get_site_url() );

		$out = array(
			'log'      => array(),
			'total'    => $total,
			'offset'   => $offset,
			'limit'    => $mxdata,
			'scan_id'  => '',
			'response' => true,
		);

		$sql = $this->get_scan_pages_query();
		if ( 0 === $total ) {
			//taking total
			$total_rows   = $wpdb->get_row( 'SELECT COUNT(ID) AS ttnum' . $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
			$total        = $total_rows ? $total_rows['ttnum'] + 1 : 1; //always add 1 becuase home url is there
			$out['total'] = apply_filters( 'wt_cli_cookie_scanner_urls', $total );
		}
		if ( 0 === $scan_id ) {
			$out['scan_id'] = $scan_id;
			$out['log'][]   = get_home_url();
		}
		//creating sql for fetching data
		$sql  = 'SELECT post_name,post_title,post_type,ID' . $sql . " ORDER BY post_type='page' DESC LIMIT $offset,$mxdata";
		$data = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
		if ( ! empty( $data ) ) {
			foreach ( $data as $value ) {
				$permalink         = get_permalink( $value['ID'] );
				$currrent_url_host = $this->get_host( $permalink );

				if ( ( $this->filter_url( $permalink ) ) && ( $currrent_url_host === $wt_cli_site_host ) ) {
					$out['log'][] = $permalink;
				} else {
					$out['total'] = $out['total'] - 1;
				}
			}
		}
		return $out;
	}
	public function get_scan_pages_query() {
		global $wpdb;
		$post_table = $wpdb->prefix . 'posts';
		$post_types = get_post_types(
			array(
				'public' => true,
			)
		);
		unset( $post_types['attachment'] );
		unset( $post_types['revision'] );
		unset( $post_types['custom_css'] );
		unset( $post_types['customize_changeset'] );
		unset( $post_types['user_request'] );
		//generating sql conditions
		$sql = " FROM $post_table WHERE post_type IN('" . implode( "','", $post_types ) . "') AND post_status='publish'";
		return $sql;
	}
	/*
	*
	* Return site host name
	* @return string
	* @since 3.0.0
	*/
	private function get_host( $url ) {
		$site_host  = '';
		$parsed_url = wp_parse_url( $url );
		$site_host  = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
		return $site_host;
	}
	/*
	*
	* Filtering non html URLS
	* @return boolean
	*/
	private function filter_url( $permalink ) {
		$url_arr = explode( '/', $permalink );
		$end     = trim( end( $url_arr ) );
		if ( '' !== $end ) {
			$url_end_arr = explode( '.', $end );
			if ( count( $url_end_arr ) > 1 ) {
				$end_end = trim( end( $url_end_arr ) );
				if ( '' !== $end_end ) {
					$allowed = array( 'html', 'htm', 'shtml', 'php' );
					if ( ! in_array( $end_end, $allowed, true ) ) {
						return false;
					}
				}
			}
		}
		return true;
	}
	/*
	*  adding url parameter for cookiebar bypassing to scan urls
	*/
	public function append_bypasspath( $url ) {
		$url_arr = explode( '?', $url );
		if ( count( $url_arr ) > 1 ) {
			if ( trim( $url_arr[1] ) !== '' ) {
				parse_str( $url_arr[1], $params );
				$params['wcc_bypass'] = 1;
				$url_arr[1]           = http_build_query( $params );
			} else {
				$url_arr[1] = 'wcc_bypass=1';
			}
		} else {
			$url_arr[] = 'wcc_bypass=1';
		}
		return implode( '?', $url_arr );
	}
	/*
	* Creates scan token
	* @return string
	*/
	public function set_scan_instance() {
		$instance_id = 'wcc-scan-' . wp_create_nonce( 'webtoffee-cookie-consent' );
		$instance_id = base64_encode( $instance_id );
		$this->update_info(
			array(
				'scan_result_token' => $instance_id,
			)
		);
		return $instance_id;
	}
	public function validate_scan_instance( $instance ) {
		$scan_data  = get_option( 'wcc_scan_details', array() );
		$scan_token = isset( $scan_data['scan_result_token'] ) ? $scan_data['scan_result_token'] : '';
		if ( ( 0 !== $instance ) && ! empty( $instance ) && ( $instance === $scan_token ) ) {
			return true;
		}
		return false;
	}
}
