<?php

namespace CustomerJourneyForWooCommerce;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

/**
 * Plugin Class.
 *
 * @since 1.0.0
 */
final class Plugin {


	/**
	 * Instance of this class.
	 *
	 * @var object
	 */
	protected static $instance = null;

	/**
	 * Return an instance of this class.
	 *
	 * @return object A single instance of this class.
	 */
	public static function get_instance() {

		// If the single instance hasn't been set, set it now.
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Initialize.
	 *
	 * @return void.
	 */
	public function init() {

		add_action( 'plugins_loaded', array( $this, 'load_classes' ) );
		add_action( 'woocommerce_new_order', array( $this, 'process' ) );
	}

	/**
	 * Load Classes.
	 *
	 * @return void.
	 */
	public function load_classes() {

		$classes = array( 'Frontend', 'Backend' );

		foreach ( $classes as $class ) {
			if ( \class_exists( __NAMESPACE__ . '\\' . $class ) ) {

				$class = __NAMESPACE__ . '\\' . $class;

				$obj = new $class();
				$obj->init();
			}
		}

		do_action( 'customer_journey_for_woocommerce_loaded' );
	}

	/**
	 * Process. Save journey to db after the order is processed.
	 *
	 * @return void.
	 */
	public function process( $order_id ) {

		if ( empty( $_COOKIE['_cjforwoo'] ) ) {
			return;
		}

		$journey = json_decode( sanitize_text_field( wp_unslash( $_COOKIE['_cjforwoo'] ) ), true ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		if ( ! is_array( $journey ) || empty( $journey ) ) {
			return;
		}

		$count          = 1;
		$timestamp_prev = 0;

		foreach ( $journey as $timestamp => $record ) {

			$item = $this->get_record_data( $record, $timestamp, $count );
			++$count;

			if ( empty( $item ) ) {
				continue;
			}

			$item = array_merge(
				array(
					'order_id' => absint( $order_id ),
					'duration' => ! empty( $timestamp_prev ) ? absint( $timestamp ) - absint( $timestamp_prev ) : 0,
				),
				$item
			);

			$this->add( $item );

			$timestamp_prev = $timestamp;
		}

		setcookie( '_cjforwoo', '', time() - 3600, '/' );
	}

	/**
	 * Get record from the string.
	 *
	 * @since 1.0.0
	 *
	 * @param string $record    Record string.
	 * @param int    $timestamp Timestamp.
	 * @param int    $step      Current step.
	 *
	 * @return array
	 */
	private function get_record_data( $record, $timestamp, $step ) {

		if ( empty( $record ) || strpos( $record, '|#|' ) === false ) {
			return array();
		}

		$parts = explode( '|#|', $record );
		$url   = esc_url_raw( strtok( $parts[0], '?' ) );

		if ( 1 !== $step && false === strpos( $url, home_url() ) ) {
			return array();
		}

		$item = array(
			'post_id'    => ! empty( $parts[2] ) ? absint( $parts[2] ) : 0,
			'url'        => $url,
			'parameters' => '',
			'title'      => ! empty( $parts[1] ) ? sanitize_text_field( $parts[1] ) : '',
			'external'   => strpos( $parts[0], home_url() ) === false,
			'step'       => $step,
			'date'       => gmdate( 'Y-m-d H:i:s', absint( $timestamp ) ),
		);

		parse_str( wp_parse_url( $parts[0], PHP_URL_QUERY ), $params );

		if ( ! empty( $params ) ) {
			$parameters = array();
			foreach ( $params as $key => $value ) {
				$parameters[ sanitize_key( $key ) ] = sanitize_text_field( $value );
			}
			$item['parameters'] = wp_json_encode( $parameters );
		}

		if ( 1 === $step && false !== strpos( $item['title'], '{ReferrerPageTitle}' ) ) {
			$title         = $this->get_html_page_title( $url );
			$item['title'] = ! empty( $title )
			? sanitize_text_field( $title )
			: str_replace( '{ReferrerPageTitle}', esc_html__( 'Referrer', 'customer-journey' ), $item['title'] );
		}

		return $item;
	}

	/**
	 * Get the page <title> from a given URL.
	 *
	 * @since 1.0.0
	 *
	 * @param string $url Page URL.
	 *
	 * @return string
	 */
	public function get_html_page_title( $url ) {

		$request = wp_remote_get( $url );

		if ( 'OK' !== wp_remote_retrieve_response_message( $request ) || 200 !== wp_remote_retrieve_response_code( $request ) ) {
			return '';
		}

		$response = wp_remote_retrieve_body( $request );

		preg_match( '/<title>(.*)<\/title>/i', $response, $matches );

		return ! empty( $matches[1] ) ? trim( $matches[1] ) : '';
	}

	/**
	 * Add items to the custom database table.
	 *
	 * @since 1.0.0
	 */
	public function add( $items ) {

		global $wpdb;

		// Set default values.
		$data = (array) wp_parse_args( $items, $this->get_column_defaults() );

		do_action( 'customer_journey_for_woocommerce_pre_insert_to_db', $data );

		// Initialise column format array.
		$column_formats = $this->get_columns();

		// Force fields to lower case.
		$data = array_change_key_case( $data );

		// White list columns.
		$data = array_intersect_key( $data, $column_formats );

		// Reorder $column_formats to match the order of columns given in $data.
		$data_keys      = array_keys( $data );
		$column_formats = array_merge( array_flip( $data_keys ), $column_formats );

		$wpdb->insert( $wpdb->prefix . 'cjforwoo', $data, $column_formats );

		do_action( 'customer_journey_for_woocommerce_post_insert_to_db', $wpdb->insert_id, $data );

		return $wpdb->insert_id;
	}

	/**
	 * Get table columns.
	 *
	 * @since 1.0.0
	 */
	public function get_columns() {

		return array(
			'id'         => '%d',
			'order_id'   => '%d',
			'post_id'    => '%d',
			'url'        => '%s',
			'parameters' => '%s',
			'external'   => '%d',
			'title'      => '%s',
			'duration'   => '%d',
			'step'       => '%d',
			'date'       => '%s',
		);
	}

	/**
	 * Default column values.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_column_defaults() {

		return array(
			'order_id'   => '',
			'post_id'    => '',
			'url'        => '',
			'parameters' => '',
			'external'   => '',
			'title'      => '',
			'duration'   => '',
			'step'       => '',
			'date'       => gmdate( 'Y-m-d H:i:s' ),
		);
	}
}
