<?php
/**
 * Class Controller file.
 *
 * @link       https://www.webtoffee.com/
 * @since      3.0.0
 * @package    WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes
 */

namespace WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes;

use WP_Error;
use WebToffee\CookieConsent\Lite\Includes\Base_Controller;
use WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes\Scanner;
use WebToffee\CookieConsent\Lite\Admin\Modules\Cookies\Includes\Cookie;
use WebToffee\CookieConsent\Lite\Admin\Modules\Scanner\Includes\Cookie_Serve;

use stdClass;

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

/**
 * Handles Cookies Operation
 *
 * @class       Controller
 * @version     3.0.0
 * @package     CookieYes
 */
class Controller extends Base_Controller {

	/**
	 * Instance of the current class
	 *
	 * @var object
	 */
	private static $instance;
	/**
	 * Cache group
	 *
	 * @var array
	 */
	protected $cache_group = 'scanner';

	/**
	 * Table versioning option name.
	 *
	 * @var string
	 */
	protected $table_option = 'scanner';

	/**
	 * Unique item identifier.
	 *
	 * @var string
	 */
	protected $id = 'id';

	/**
	 * 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;
	}

	/**
	 * Return a list of Cookies tables
	 *
	 * @return array Cookies tables.
	 */
	protected function get_tables() {
		global $wpdb;
		return array(
			"{$wpdb->prefix}wcc_cookie_scan_history",
		);
	}

	/**
	 * Get table schema
	 *
	 * @return string
	 */
	protected function get_schema() {

		global $wpdb;

		$collate = '';

		if ( $wpdb->has_cap( 'collation' ) ) {
			$collate = $wpdb->get_charset_collate();
		}
		$tables = "
		CREATE TABLE {$wpdb->prefix}wcc_cookie_scan_history (
			id bigint(20) NOT NULL AUTO_INCREMENT,
			scan_id int(11) NOT NULL DEFAULT 0,
			scan_status varchar(40) NOT NULL DEFAULT '',
			scan_token longtext NOT NULL,
			date_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
			total_url int(11) NOT NULL DEFAULT 0,
			total_cookies int(11) NOT NULL DEFAULT 0,
			category varchar(190) NOT NULL DEFAULT '',
			total_scripts int(11) NOT NULL DEFAULT 0,
			urls_scanned longtext NOT NULL,
			scan_history longtext NOT NULL,
			PRIMARY KEY (id)
		) $collate;
		";
		return $tables;
	}

	/**
	 * Get a list of scanners from localhost.
	 *
	 * @param array $args Array of arguments.
	 * @return array
	 */
	public function get_item_from_db( $args = array() ) {

		global $wpdb;
		$items = array();
		if ( isset( $args['id'] ) && '' !== $args['id'] ) {
			$results = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}wcc_cookie_scan_history` WHERE `id` = %d", absint( $args['id'] ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
		} else {
			$results = $wpdb->get_results( "SELECT * FROM `{$wpdb->prefix}wcc_cookie_scan_history`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
		}
		if ( isset( $results ) && ! empty( $results ) ) {
			if ( true === is_array( $results ) ) {
				foreach ( $results as $data ) {
					$item = $this->prepare_item( $data );
					if ( ! empty( $item ) ) {
						$items[ $item->{$this->id} ] = $item;
					}
				}
			} else {
				$items = $this->prepare_item( $results );
			}
		}
		return $items;
	}

	/**
	 * Create a new scanner.
	 *
	 * @param object $scanner Scanner object.
	 * @return void
	 */
	public function create_item( $scanner ) {

		global $wpdb;
		$scan_id = ! empty( $scanner->get_scan_id() ) ? $scanner->get_scan_id() : '';
		if ( ! empty( $scan_id ) ) {
			$date_created = current_time( 'mysql' );
			$scanner->set_date_created( $date_created );
			$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
				$wpdb->prefix . 'wcc_cookie_scan_history',
				array(
					'scan_id'       => $scanner->get_scan_id(),
					'scan_status'   => $scanner->get_scan_status(),
					'scan_token'    => $scanner->get_scan_token(),
					'date_created'  => $scanner->get_date_created(),
					'total_url'     => $scanner->get_total_url(),
					'total_cookies' => $scanner->get_total_cookies(),
					'category'      => $scanner->get_category(),
					'total_scripts' => $scanner->get_total_scripts(),
					'urls_scanned'  => $scanner->get_urls_scanned(),
					'scan_history'  => $scanner->get_scan_history(),
				),
				array(
					'%d',
					'%s',
					'%s',
					'%s',
					'%d',
					'%d',
					'%s',
					'%d',
					'%s',
					'%s',
				)
			);
			$scanner->set_id( $wpdb->insert_id );
		}
		if ( $wpdb->last_error ) {
			// Handle the database insertion error here.
			print_r( 'Database Insertion Error: ' . $wpdb->last_error ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
		}
		do_action( 'wcc_after_update_scanner' );
	}

	/**
	 * Update an existing scanner locally.
	 *
	 * @param object $scanner Scanner object.
	 * @return void
	 */
	public function update_item( $scanner ) {
		global $wpdb;
		$data = array(
			'scan_status'   => $scanner->get_scan_status(),
			'scan_token'    => $scanner->get_scan_token(),
			'date_created'  => $scanner->get_date_created(),
			'total_url'     => $scanner->get_total_url(),
			'total_cookies' => $scanner->get_total_cookies(),
			'total_scripts' => $scanner->get_total_scripts(),
			'category'      => $scanner->get_category(),
			'urls_scanned'  => $scanner->get_urls_scanned(),
			'scan_history'  => $scanner->get_scan_history(),
		);

		$where = array(
			'scan_id' => $scanner->get_scan_id(),
		);
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$wpdb->update(
			$wpdb->prefix . 'wcc_cookie_scan_history',
			$data,
			$where,
			array(
				'%s',
				'%s',
				'%s',
				'%d',
				'%d',
				'%d',
				'%s',
				'%s',
				'%s',
			),
			array( '%d' )
		);

		if ( defined( 'WCC_BULK_REQUEST' ) && WCC_BULK_REQUEST ) {
			return;
		}

		do_action( 'wcc_after_update_scanner' );
	}

	/**
	 * Delete a scanner locally.
	 *
	 * @param object $id Scanner id.
	 * @return boolean
	 */
	public function delete_item( $id ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$status = $wpdb->delete(
			$wpdb->prefix . 'wcc_cookie_scan_history',
			array(
				'id' => $id,
			)
		);
		do_action( 'wcc_after_update_scanner' );
		return $status;
	}

	/**
	 * Prepare scanner data to response.
	 *
	 * @param object $item Scanner object.
	 * @return object
	 */
	public function prepare_item( $item ) {
		if ( false === is_object( $item ) ) {
			return false;
		}
		$data                = new stdClass();
		$data->id            = isset( $item->id ) ? absint( $item->id ) : 0;
		$data->scan_id       = isset( $item->scan_id ) ? absint( $item->scan_id ) : 0;
		$data->scan_status   = isset( $item->scan_status ) ? sanitize_text_field( $item->scan_status ) : '';
		$data->scan_token    = isset( $item->scan_token ) ? sanitize_text_field( $item->scan_token ) : '';
		$data->date_created  = isset( $item->date_created ) ? sanitize_text_field( $item->date_created ) : '';
		$data->total_url     = isset( $item->total_url ) ? absint( $item->total_url ) : '';
		$data->total_cookies = isset( $item->total_cookies ) ? absint( $item->total_cookies ) : 0;
		$data->total_scripts = isset( $item->total_scripts ) ? absint( $item->total_scripts ) : 0;
		$data->category      = isset( $item->category ) ? sanitize_text_field( $item->category ) : '';
		$data->urls_scanned  = isset( $item->urls_scanned ) ? sanitize_text_field( $item->urls_scanned ) : '';
		$data->scan_history  = isset( $item->scan_history ) ? sanitize_text_field( $item->scan_history ) : '';
		return $data;
	}

	/**
	 * Decode a JSON string if necessary
	 *
	 * @param string $data String data.
	 * @return array
	 */
	public function prepare_json( $data ) {
		return is_string( $data ) ? json_decode( $data, true ) : $data;
	}
	/**
	 * Load default scanner
	 *
	 * @return void
	 */
	protected function load_default() {
	}

	/**
	 * Check scanner status
	 *
	 * @return boolean
	 */
	public function check_status() {
		global $wpdb;
		if ( false === $this->table_exist() ) {
			return false;
		}
		$items = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$wpdb->prefix}wcc_cookie_scan_history WHERE scan_status = %s", '' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
		return $items > 0 ? true : false;
	}

	/**
	 * Update scan result
	 *
	 * @return array
	 */
	public function update_scan_result( $response ) {
		$cookie_serve = new Cookie_Serve();

		// Check if the response is set and is an object
		if ( isset( $response ) && is_object( $response ) ) {
			// Decode the JSON response
			$response_body = json_decode( $response->get_body(), true );
			// Check if response_body is an array
			if ( is_array( $response_body ) ) {
				// Fetch scan result and token
				$scan_result       = isset( $response_body['scan_result'] ) ? $response_body['scan_result'] : array();
				$scan_result_token = isset( $response_body['scan_result_token'] ) ? $response_body['scan_result_token'] : '';

				// Validate the scan result token
				if ( $cookie_serve->validate_scan_instance( $scan_result_token ) === false ) {
					return new WP_Error( 'invalid', __( 'Invalid scan token', 'webtoffee-cookie-consent' ) );
				}

				// Insert category data
				$this->insert_categories( $scan_result );

				// Insert cookie details
				$this->insert_cookies( $scan_result );

				// Update the cookie serve info
				$cookie_serve->update_info(
					array(
						'scan_status' => sanitize_text_field( 'completed' ),
						'scan_id'     => isset( $response_body['scan_id'] ) ? sanitize_text_field( $response_body['scan_id'] ) : '',
					)
				);
				/**
				 * Add prompt to renew consent after successful scan
				 * @since 3.2.0
				 */
				update_option( 'wcc_renew_consent_notice_dismissed', false );
				// Return the original response
				return $response_body;
			} else {
				// Return an error if the response body is not an array
				return new WP_Error( 'invalid_response_body', __( 'Invalid response body.', 'webtoffee-cookie-consent' ) );
			}
		} else {
			// Return an error if the response is not set or not an object
			return new WP_Error(
				'webtoffee_rest_item_exists',
				__( 'Cannot update scan result.', 'webtoffee-cookie-consent' ),
				array( 'status' => 400 )
			);
		}
	}

	/**
	 * Insert the scanned Cookies to the corresponding table
	 *
	 * @param $scan_data scanned cookies.
	 * @return void
	 */
	public function insert_cookies( $scan_data ) {
		global $wpdb;
		// Decode the JSON scan_data into a PHP array
		$scan_data_arr = json_decode( $scan_data, true );

		// Mark all existing discovered cookies as not found in scan_data
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		$wpdb->query( "UPDATE {$wpdb->prefix}wcc_cookies SET cookie_status = 0 WHERE discovered = 1" );

		foreach ( $scan_data_arr as $category_data ) {
			if ( isset( $category_data['cookies'] ) && is_array( $category_data['cookies'] ) ) {
				$category = isset( $category_data['category'] ) ? $category_data['category'] : '';
				foreach ( $category_data['cookies'] as $cookie_data ) {
					$parsed_cookie_data = $this->create_category_mapped_cookie_data( $cookie_data, $category );
					$object             = $this->prepare_item_for_database( $parsed_cookie_data );
					$cookie_name        = $object->get_name();
					// Check whether a cookie with the same name exists or not
					// phpcs:ignore WordPress.DB.DirectDatabaseQuery
					$existing_cookie = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wcc_cookies WHERE name = %s", $cookie_name ), ARRAY_A );
					if ( $existing_cookie ) {
						// For existing cookies, only update cookie_status
						// phpcs:ignore WordPress.DB.DirectDatabaseQuery
						$wpdb->update(
							$wpdb->prefix . 'wcc_cookies',
							array( 'cookie_status' => 1 ),
							array( 'cookie_id' => $existing_cookie['cookie_id'] ),
							array( '%d' ),
							array( '%d' )
						);
					} else {
						// Create new cookie
						$object->create( $object );
					}
				}
			}
		}
	}

	/**
	 * Insert all the cookie categories.
	 *
	 * @since 3.0.0
	 * @param $categories scanned cookie categories.
	 * @return void
	 */
	public function insert_categories( $categories ) {
		global $wpdb;
		$cat_table = $wpdb->prefix . 'wcc_cookie_categories';

		// Decode the JSON categories into a PHP array
		$categories_arr = json_decode( $categories, true );

		foreach ( $categories_arr as $id => $category_data ) {
			$category     = isset( $category_data['category'] ) ? esc_sql( sanitize_text_field( $category_data['category'] ) ) : '';
			$description  = isset( $category_data['category_desc'] ) ? esc_sql( addslashes( sanitize_textarea_field( $category_data['category_desc'] ) ) ) : '';
			$script_slug  = isset( $category_data['script_slug'] ) ? esc_sql( addslashes( sanitize_textarea_field( $category_data['script_slug'] ) ) ) : '';
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$existing_cat = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM  {$wpdb->prefix}wcc_cookie_categories WHERE slug = %s", $category ), ARRAY_A );
			if ( ! $existing_cat && ! empty( $category ) && ! empty( $script_slug ) ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery
				$wpdb->insert(
					$cat_table,
					array(
						'slug'        => $category,
						'description' => $description,
						'script_slug' => $script_slug,
					)
				);
			}
		}
	}


	/**
	 * Prepare a single item for create or update.
	 *
	 * @param  WP_REST_Request $request Request object.
	 * @return object
	 */
	public function prepare_item_for_database( $request ) {
		$id     = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
		$object = new Cookie( $id );
		$object->set_name( isset( $request['cookie_name'] ) ? $request['cookie_name'] : '' );
		$object->set_slug( isset( $request['script_slug'] ) ? $request['script_slug'] : '' );
		$object->set_description( isset( $request['description'] ) ? $request['description'] : '' );
		$object->set_duration( isset( $request['duration'] ) ? $request['duration'] : '' );
		$object->set_domain( isset( $request['url'] ) ? $request['url'] : '' );
		$object->set_category( isset( $request['category_id'] ) ? $request['category_id'] : '' );
		$object->set_type( isset( $request['type'] ) ? $request['type'] : '' );
		$object->set_discovered( true );
		$object->set_url_pattern( isset( $request['url_pattern'] ) ? $request['url_pattern'] : '' );
		$object->set_meta( isset( $request['meta'] ) ? $request['meta'] : '' );
		$object->set_cookie_status( true );
		$object->set_date_created( isset( $request['date_created'] ) ? $request['date_created'] : current_time( 'mysql' ) );
		$object->set_date_modified( isset( $request['date_modified'] ) ? $request['date_modified'] : current_time( 'mysql' ) );
		return $object;
	}

	/**
	 * Prepare new $cookie_data array with category_id mapped correctly
	 *
	 * @param  $cookie_data array and $category.
	 * @return array
	 */
	public function create_category_mapped_cookie_data( $cookie_data, $category ) {

		switch ( $category ) {
			case 'Necessary':
				$cookie_data['category_id'] = 1;
				break;
			case 'Functional':
				$cookie_data['category_id'] = 2;
				break;
			case 'Analytics':
				$cookie_data['category_id'] = 3;
				break;
			case 'Performance':
				$cookie_data['category_id'] = 4;
				break;
			case 'Advertisement':
				$cookie_data['category_id'] = 5;
				break;
			case 'Others':
				$cookie_data['category_id'] = 6;
				break;
			default:
				// Handle the case where $category is none of the above
				$cookie_data['category_id'] = isset( $cookie_data['category_id'] ) ? intval( $cookie_data['category_id'] ) : 6;
				break;
		}
		return $cookie_data;
	}
}
