<?php

namespace Prodigy\Includes\Support\Addons\Elementor\Classes;

use Elementor\Api;
use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
use Elementor\Plugin;
use Elementor\TemplateLibrary\Manager;
use Elementor\TemplateLibrary\Source_Local;
use Elementor\User;


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

/**
 * @version 2.0.7
 */
class Prodigy_Manager {

	private $manager;
	private $_registered_sources;

	public function __construct( Manager $manager ) {
		$this->manager = $manager;

		// Unregister source with closure binding, thank Steve.
		$unregister_source = function ( $id ) {
			unset( $this->_registered_sources[ $id ] );
		};

		$unregister_source->call( $this->manager, 'remote' );

		$this->manager->register_source( 'Prodigy\Includes\Support\Addons\Elementor\Classes\Prodigy_Source_Custom' );
		$this->reset_actions();
	}

	/**
	 * @return void
	 */
	private function reset_actions() {
		add_action( 'elementor/ajax/register_actions', array( $this, 'register_ajax_actions' ) );
		add_action( 'wp_ajax_elementor_library_direct_actions', array( $this, 'handle_direct_actions' ) );
	}

	/**
	 * @param Ajax $ajax
	 *
	 * @return void
	 */
	public function register_ajax_actions( Ajax $ajax ) {
		$library_ajax_requests = array(
			'get_library_data',
			'get_template_data',
			'save_template',
			'update_templates',
			'delete_template',
			'import_template',
			'mark_template_as_favorite',
		);

		foreach ( $library_ajax_requests as $ajax_request ) {
			$ajax->register_ajax_action(
				$ajax_request,
				function ( $data ) use ( $ajax_request ) {
					return $this->handle_ajax_request( $ajax_request, $data );
				}
			);
		}
	}

	/**
	 * @param $ajax_request
	 * @param array        $data
	 *
	 * @return mixed
	 * @throws \Exception
	 */
	private function handle_ajax_request( $ajax_request, array $data ) {
		if ( ! User::is_current_user_can_edit_post_type( Source_Local::CPT ) ) {
			throw new \Exception( 'Access Denied' );
		}

		if ( ! empty( $data['editor_post_id'] ) ) {
			$editor_post_id = absint( $data['editor_post_id'] );

			if ( ! get_post( $editor_post_id ) ) {
				throw new \Exception( esc_html__( 'Post not found.', 'prodigy-commerce' ) );
			}

			Plugin::$instance->db->switch_to_post( $editor_post_id );
		}

		if ( 'save_template' === $ajax_request && ( ! empty( $data['source'] ) && 'remote' === $data['source'] || ! empty( $data['cloud'] ) ) ) {
			if ( method_exists( $this->manager, $ajax_request ) ) {
				return call_user_func_array( array( $this->manager, $ajax_request ), array( $data ) );
			}
		}

		if ( method_exists( $this, $ajax_request ) ) {
			$result = call_user_func( array( $this, $ajax_request ), $data );
		} elseif ( method_exists( $this->manager, $ajax_request ) ) {
			$result = call_user_func( array( $this->manager, $ajax_request ), $data );
		} else {
			throw new \Exception( sprintf( 'Method %s not found', esc_html( $ajax_request ) ) );
		}

		if ( is_wp_error( $result ) ) {
			throw new \Exception( esc_html( $result->get_error_message() ) );
		}

		return $result;
	}

	/**
	 * Handle direct actions for library
	 */
	public function handle_direct_actions() {
		if ( method_exists( $this->manager, 'handle_direct_actions' ) ) {
			return call_user_func_array( array( $this->manager, 'handle_direct_actions' ), func_get_args() );
		}
		return new \WP_Error( 'handler_not_found', 'Direct actions handler not available' );
	}


	/**
	 * @param $method
	 * @param $args
	 *
	 * @return mixed|void
	 */
	public function __call( $method, $args ) {
		$args;
		if ( method_exists( $this->manager, $method ) ) {
			return call_user_func_array( array( $this->manager, $method ), $args );
		}
	}

	/**
	 * @param array $args
	 *
	 * @return array
	 */
	public function get_library_data( array $args ): array {
		try {
			$native_library_data = Api::get_library_data( ! empty( $args['sync'] ) );
			$library_data        = Prodigy_Api::get_library_data( ! empty( $args['sync'] ) );

			if ( ! is_array( $native_library_data ) ) {
				$native_library_data = array();
			}

			if ( ! is_array( $library_data ) ) {
				$library_data = array();
			}

			$library_data = self::merge_types( $native_library_data, $library_data );

			if ( ! isset( $library_data['types_data'] ) ) {
				$library_data['types_data'] = isset( $native_library_data['types_data'] ) ? $native_library_data['types_data'] : array();
			}

			$templates = $this->get_templates( $args );

			return array(
				'templates' => $templates,
				'config'    => $library_data['types_data'],
			);
		} catch ( \Exception $e ) {
			$native_library_data = Api::get_library_data( ! empty( $args['sync'] ) );
			return array(
				'templates' => array(),
				'config'    => isset( $native_library_data['types_data'] ) ? $native_library_data['types_data'] : array(),
			);
		}
	}

	/**
	 * @param array $args
	 *
	 * @return array|mixed|\WP_Error
	 * @throws \Exception
	 */
	public function get_template_data( array $args ) {
		if ( ! empty( $args['source'] ) && 'remote' === $args['source'] ) {
			$source = $this->manager->get_source( 'remote' );
			if ( $source && $source instanceof Prodigy_Source_Custom ) {
				return $source->get_data( $args, 'display' );
			}
		}

		if ( method_exists( $this->manager, 'get_template_data' ) ) {
			return call_user_func( array( $this->manager, 'get_template_data' ), $args );
		}

		return new \WP_Error( 'template_not_found', 'Template not found' );
	}

	/**
	 * Get templates from all registered sources.
	 *
	 * @param array $args Optional arguments.
	 * @return array Templates array.
	 */
	private function get_templates( array $args = array() ): array {
		$templates = array();

		if ( method_exists( $this->manager, 'get_source' ) ) {
			$prodigy_source = $this->manager->get_source( 'remote' );
			if ( $prodigy_source && $prodigy_source instanceof Prodigy_Source_Custom ) {
				if ( method_exists( $prodigy_source, 'get_items' ) ) {
					$prodigy_templates = $prodigy_source->get_items( $args );
					if ( is_array( $prodigy_templates ) && ! empty( $prodigy_templates ) ) {
						$templates = array_merge( $templates, $prodigy_templates );
					}
				}
			}
		}

		if ( method_exists( $this->manager, 'get_source' ) ) {
			$local_source = $this->manager->get_source( 'local' );
			if ( $local_source && method_exists( $local_source, 'get_items' ) ) {
				$local_templates = $local_source->get_items( $args );
				if ( is_array( $local_templates ) && ! empty( $local_templates ) ) {
					$templates = array_merge( $templates, $local_templates );
				}
			}
		}

		return $templates;
	}

	/**
	 * @param array $elementor_data
	 * @param array $custom_data
	 * @return array
	 */
	public static function merge_types( $elementor_data, $custom_data ) {
		if ( ! is_array( $elementor_data ) ) {
			$elementor_data = array();
		}

		if ( ! is_array( $custom_data ) ) {
			return $elementor_data;
		}

		if ( ! isset( $elementor_data['types_data'] ) ) {
			$elementor_data['types_data'] = array();
		}

		if ( ! isset( $elementor_data['types_data']['block'] ) ) {
			$elementor_data['types_data']['block'] = array();
		}

		if ( ! isset( $elementor_data['types_data']['block']['categories'] ) ) {
			$elementor_data['types_data']['block']['categories'] = array();
		}

		if ( ! isset( $elementor_data['templates'] ) ) {
			$elementor_data['templates'] = array();
		}

		if ( ! isset( $elementor_data['categories'] ) ) {
			$elementor_data['categories'] = array();
		}

		if ( isset( $custom_data['types_data']['block']['categories'] ) && is_array( $custom_data['types_data']['block']['categories'] ) ) {
			foreach ( $custom_data['types_data']['block']['categories'] as $item ) {
				if ( ! in_array( $item, $elementor_data['types_data']['block']['categories'], true ) ) {
					array_push( $elementor_data['types_data']['block']['categories'], $item );
				}
			}
		}

		if ( isset( $custom_data['templates'] ) && is_array( $custom_data['templates'] ) ) {
			foreach ( $custom_data['templates'] as $item ) {
				if ( ! in_array( $item, $elementor_data['templates'], true ) ) {
					array_push( $elementor_data['templates'], $item );
				}
			}
		}

		if ( isset( $custom_data['categories'] ) && is_array( $custom_data['categories'] ) ) {
			foreach ( $custom_data['categories'] as $item ) {
				if ( ! in_array( $item, $elementor_data['categories'], true ) ) {
					array_push( $elementor_data['categories'], $item );
				}
			}
		}

		return $elementor_data;
	}
}
