<?php

namespace CTXFeed\V5\API;

use CTXFeed\V5\API\V1\AttributesMapping;
use CTXFeed\V5\API\V1\CategoryMapping;
use CTXFeed\V5\API\V1\DropDown;
use CTXFeed\V5\API\V1\DynamicAttributes;
use CTXFeed\V5\API\V1\MakeFeed;
use CTXFeed\V5\API\V1\ManageFeeds;
use CTXFeed\V5\API\V1\MerchantInfo;
use CTXFeed\V5\API\V1\ProductCategories;
use CTXFeed\V5\API\V1\Products;
use CTXFeed\V5\API\V1\ProductTaxonomy;
use CTXFeed\V5\API\V1\Settings;
use CTXFeed\V5\API\V1\WooFeedDocs;
use CTXFeed\V5\API\V1\WPStatus;
use CTXFeed\V5\API\V1\WPOptions;
use \WP_REST_Controller;
use \WP_Error;
use CTXFeed\V5\Helper\CommonHelper;

/**
 * Class RestController
 *
 * @package    CTXFeed
 * @subpackage CTXFeed\V5\API
 * @author     Azizul Hasan <azizulhasan.cr@gmail.com>
 * @link       https://azizulhasan.com
 * @license    https://opensource.org/licenses/gpl-license.php GNU Public License
 */
class RestController extends WP_REST_Controller {

	/**
	 * @var array $response ;
	 */
	public $response = [
		'status' => 200,
		'data'   => [],
		'extra'  => null
	];
	/**
	 * The single instance of the class
	 *
	 * @var RestController
	 *
	 */
	protected static $_instance = null;

	/**
	 * @var $version ;
	 */
	private $version = WOO_FEED_API_VERSION;

	protected function __construct() {

		$this->namespace = WOO_FEED_API_NAMESPACE . '/' . $this->version;
		add_action( 'rest_api_init', [ $this, 'register_api' ] );
		add_action( 'rest_api_init', function ( $var ) {
			remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
		}, 15, 1 );

	}


	/**
	 * Main RestController Instance.
	 *
	 * Ensures only one instance of RestController is loaded or can be loaded.
	 *
	 * @return RestController Main instance
	 */
	public static function instance() {
		if ( is_null( self::$_instance ) ) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Return true only user is logged in as $mange_ctx_feed roles.
	 * Using postman or other API client Basic-Auth plugin must be installed ( URL is below )
	 * authorization system should be Basic Auth.
	 *
	 * @@ -86,7 +86,16 @@ public static function instance() {
	 * @see https://github.com/WP-API/Basic-Auth
	 */
//	public function get_item_permissions_check( $request ) {
//
//		$user                             = wp_get_current_user();
//		$mange_ctx_feed                   = apply_filters( 'ctx_feed_api_accessed_users', [
//			'manage_options',
//			'manage_woocommerce'
//		] );
//		$current_user_roles               = $user->get_role_caps();
//		$current_user_roles               = array_keys( $current_user_roles );
//		$current_user_can_manage_ctx_feed = false;
//		foreach ( $mange_ctx_feed as $role ) {
//			if ( in_array( $role, $current_user_roles ) ) {
//				$current_user_can_manage_ctx_feed = true;
//			}
//		}
//
//		return apply_filters( 'ctx_feed_current_user_can_manage_api', $current_user_can_manage_ctx_feed, $user, $current_user_roles, $mange_ctx_feed, $request );
//
//	}

    public function get_item_permissions_check( $request ) {

        // Must be logged in
        if ( ! is_user_logged_in() ) {
            return new \WP_Error(
                'ctxfeed_rest_forbidden',
                __( 'Authentication required.', 'woo-feed' ),
                [ 'status' => 401 ]
            );
        }

        // Must have proper capability
        if ( ! current_user_can( 'manage_woocommerce' ) && ! current_user_can( 'manage_options' ) ) {
            return new \WP_Error(
                'ctxfeed_rest_forbidden',
                __( 'You are not allowed to access this resource.', 'woo-feed' ),
                [ 'status' => 403 ]
            );
        }

        return true;
    }


    /**
	 * Register routes according to $_SERVER['REQUEST_URI'].
	 * After 'wp-json' value will be considered as namespace.
	 * After that v1/v2 will be as api version number.
	 * Then next value will be considered as route name.
	 *
	 * Example : wp-json/ctxfeed/v1/drop_down/?type=feed_country
	 *
	 * @description
	 * ctxfeed:  is namespace
	 * v1: is version number and will indicate version folder number
	 * drop_down: route. It will look into 'DropDown' class in V1 folder.
	 * @return void
	 */
	public function register_api() {
//		$uri           = trim( $_SERVER['REQUEST_URI'], '/' );
//		$uri_arr       = explode( '/', $uri );
//		$namespace     = explode( '/', $this->namespace );
//		$uri_namespace = '';
//		$class_name    = '';
//		$version       = $this->version;
		// Get rest base like : 'wp-json' or 'index.php?rest_route='
//		$rest_base = str_replace( home_url(), '', get_rest_url() );
//		$rest_base = trim( $rest_base, '/' );
//
//		foreach ( $uri_arr as $i => $value ) {
//			// Get namespace name;
//			if ( $value === $rest_base ) {
//				$i ++;
//				if ( ! isset( $uri_arr[ $i ] ) ) {
//					break;
//				}
//				$uri_namespace = $uri_arr[ $i ];
//				$i             += 2;
//				// Get current classname from url after version number.
//				if ( isset( $uri_arr[ $i ] ) ) {
//					$class_name = $uri_arr[ $i ];
//					$version    = $uri_arr[ -- $i ];
//				}
//				break;
//			}
//		}

//		 If current namespace and url namespace are equal
//		 load class name from version folder.
//		if ( $class_name && count( $namespace ) && $namespace[0] == $uri_namespace ) {
//			if ( class_exists( $class_name ) ) {
//				self::load_class( $class_name, $version )->register_routes();
//			}else{
				$classes = [
					AttributesMapping::instance(),
					CategoryMapping::instance(),
					DropDown::instance(),
					DynamicAttributes::instance(),
					ManageFeeds::instance(),
					MerchantInfo::instance(),
					ProductCategories::instance(),
					Products::instance(),
					ProductTaxonomy::instance(),
					Settings::instance(),
					WPOptions::instance(),
					MakeFeed::instance(),
					WPStatus::instance(),
					WooFeedDocs::instance(),

				];
				foreach ( $classes as $class ) {
					$class->register_routes();
				}
//			}
//		}
	}


	/**
	 * Cloning is forbidden.
	 */
	final public function __clone() {
		_doing_it_wrong( __FUNCTION__, esc_attr_e( 'Cloning is forbidden.', 'woo-feed' ), esc_attr(WOO_FEED_FREE_VERSION) );
	}

	/**
	 * Unserializing instances of this class is forbidden.
	 */
	final public function __wakeup() {
		_doing_it_wrong( __FUNCTION__, esc_attr_e( 'Unserializing instances of this class is forbidden.', 'woo-feed' ), esc_attr(WOO_FEED_FREE_VERSION) );
	}


	/**
	 * @param $data
	 *
	 * @return void|\WP_REST_Response
	 */
	public function success( $data, $status = 200 ) {
		$this->response['status'] = $status;
		$this->response['data']   = $data;

		$response = rest_ensure_response( $this->response );
		$response = $this->add_additional_headers( $response );

		return $response;
	}


	/**
	 * @param $data
	 *
	 * @return void|\WP_Error
	 */
	public function error( $data = '', $code = 'rest_no_data_found', $status = 404 ) {
		$this->response['status'] = $status;
		$this->response['data']   = $data;
		$this->response['code']   = $code;

		$this->response['status'] = $status;
		$this->response['data']   = $data;

		$response = rest_ensure_response( $this->response );
		$response = $this->add_additional_headers( $response );

		return $response;
	}

	/**
	 * @param $response
	 *
	 * @return \WP_REST_Response
	 */
	protected function add_additional_headers( $response ) {
		$admin_origin = parse_url( admin_url() );
		$response->header( 'Access-Control-Allow-Origin', $admin_origin['host'] );

		return $response;
	}

	/**
	 * @param $args
	 * @param $data
	 * @param $response
	 *
	 * @return mixed
	 */
	protected function maybe_add_pagination( $args, $data, $response ) {
		// Get data according to pagination. If $page and $per_page params are passed in the url.
		$total = count( $data );
		if ( isset( $args['per_page'], $args['page'] ) ) {
			$total_pages = ceil( $total / (int) $args['per_page'] );
			// Set current page data.
			$offset                 = $args['per_page'] * ( $args['page'] - 1 );
			$this->response['data'] = array_slice( $data, $offset, $args['per_page'] );
			$response               = $this->add_pagination_links( $response, $args, $total_pages, $total );
		} else {
			$this->response['data'] = $data;
		}
		$response->data = $this->response;

		return $response;
	}

	/**
	 * @param $response
	 * @param $args
	 * @param $total_pages
	 * @param $total
	 *
	 * @return mixed
	 */
	protected function add_pagination_links( $response, $args, $total_pages, $total ) {

		$url = get_site_url() . '/wp-json/' . $this->namespace . '/' . $this->rest_base . '/?';

		$page = (int) $args['page'];
		unset( $args['page'] );
		$total_args = count( $args );
		$count      = 0;
		foreach ( $args as $arg => $value ) {
			$count ++;
			if ( $count === $total_args ) {
				$url .= $arg . '=' . $value;
			} else {
				$url .= $arg . '=' . $value . '&';
			}
		}
		// Next page link add.
		if ( $total_pages == $page ) {
			$next_url = $url . '&page=' . $page;
		} else {
			$next_page = $page + 1;
			$next_url  = $url . '&page=' . $next_page;
		}
		$response->add_link( 'next_page', $next_url );
		// Previous page link add.
		if ( $page == 1 ) {
			$prev_url = $url . '&page=' . $page;
		} else {
			$prev_page = $page - 1;
			$prev_url  = $url . '&page=' . $prev_page;
		}
		$response->add_link( 'prev_page', $prev_url );
		// add headers.
		$response->header( 'X-WP-TotalPages', (int) $total_pages );
		$response->header( 'X-WP-Total', (int) $total );

		return $response;
	}

	/**
	 * @param $array
	 *
	 * @return bool
	 */
	protected function is_assoc( $array ) {
		if ( array() === $array ) {
			return false;
		}

		return ( $array !== array_values( $array ) );
	}

	public function is_prefix_matched( $string, $prefix ) {
		return str_starts_with( $string, $prefix );
	}


	private static function load_class( $class = null, $version = 'v1' ) {
		$class_name = self::get_class_name( $class );

		return RestFactory::load( $class_name, $version ) ?? null;
	}

	/**
	 * @param $class
	 *
	 * @return string
	 */
	private static function get_class_name( $class ) {
		$api_class = array_map( function ( $part ) {
			if ( 'wp' === $part ) {
				return strtoupper( $part );
			}

			return ucfirst( $part );
		}, explode( '_', $class ) );

		return implode( '', $api_class );
	}


	/**
	 * @param $request
	 *
	 * @return array
	 */
	protected function get_lists( $request, $arr ) {
		$lists = [];

		if ( ! empty( $arr ) ) {
			foreach ( $arr as $option_name => $attr_list ) {
				$item                  = $this->prepare_item_for_response( $attr_list, $request );
				$lists[ $option_name ] = $item;
			}
		}

		return $lists;
	}

	/**
	 * Safe unserialize that prevents PHP Object Injection.
	 *
	 * @param mixed $data The data to unserialize.
	 * @return mixed The unserialized data or original if not serialized.
	 */
	public static function safe_unserialize( $data ) {
		if ( ! is_string( $data ) ) {
			return $data;
		}
		if ( ! is_serialized( $data ) ) {
			return $data;
		}
		// Use allowed_classes = false to prevent object instantiation
		return @unserialize( $data, array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
	}

	/**
	 * @param $item
	 * @param $request
	 *
	 * @return void|\WP_Error|\WP_REST_Response
	 */
	public function prepare_item_for_response( $item, $request ) {
		// Use safe deserialization to prevent PHP Object Injection
		return self::safe_unserialize( $item );
	}


	public function unique_option_name( $option_name, $prefix, $add_prefix = false ) {
		$option_name = preg_replace( "/[^A-Za-z0-9_]/", '', $option_name );
		if ( false !== get_option( sanitize_text_field($prefix) . sanitize_text_field($option_name), false ) ) {
			$option = CommonHelper::unique_option_name( $option_name, $prefix );
		} else {
			$option = $add_prefix ? $prefix . $option_name : $option_name;
		}

		$response = [ 'option_name' => $option ];

		return $response;
	}

	/**
	 * @param $request
	 *
	 * @return string
	 * If Dynamic Attribute, Category mapping and Attribute mapping option name with special char like '&' by default php split the link when
	 * get &, to make unique option name with & we use this function.
	 */
	public function get_feed_option_name( $request ) {
		$args      = $request->get_params();
		$feed_name = $args['name'];

		$feed_name = str_replace( "plus", "+", $feed_name );
		array_shift( $args );
		$temp_arr = [ $feed_name ];
		if ( count( array_keys( $args ) ) > 1 ) {
			$temp_arr2 = implode( '&', array_keys( $args ) );
			array_push( $temp_arr, $temp_arr2 );
			$feed_name = implode( '&', $temp_arr );
		}


		return $feed_name;
	}
}
