<?php
/**
 * TemplateManager class.
 *
 * @package DokanKits\Core
 * @since   3.0.0
 */

namespace DokanKits\Core;

use DokanKits\Core\Interfaces\Hookable;

/**
 * Template Manager
 *
 * A clean template manager using only WordPress core functions
 *
 * @since   3.0.0
 * @package DokanKits\Core
 */
class TemplateManager implements Hookable {

	/**
	 * Registered template paths
	 *
	 * @since 3.0.0
	 * @var array
	 */
	protected array $paths = array();

	/**
	 * Constructor
	 *
	 * @since 3.0.0
	 */
	public function __construct() {
		$this->register_core_paths();
	}

	/**
	 * Initialize hooks
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'after_switch_theme', array( $this, 'clear_cache' ) );
		add_action( 'activated_plugin', array( $this, 'clear_cache' ) );
		add_action( 'deactivated_plugin', array( $this, 'clear_cache' ) );
	}

	/**
	 * Register core template paths
	 *
	 * @since 3.0.0
	 * @return void
	 */
	protected function register_core_paths(): void {
		// Register main plugin template path.
		if ( defined( 'DOKAN_KITS_TEMPLATE_PATH' ) ) {
			$this->register_path( 'core', DOKAN_KITS_TEMPLATE_PATH, 100 );
		}

		/**
		 * Allow extensions to register their template paths
		 *
		 * @since 3.0.0
		 *
		 * @param TemplateManager $this Template manager instance.
		 */
		do_action( 'dokan_kits_register_template_paths', $this );
	}

	/**
	 * Register a template path for an extension.
	 *
	 * @since 3.0.0
	 *
	 * @param string $extension Extension name (e.g., 'image-upload-limit').
	 * @param string $path      Template directory path.
	 * @param int    $priority  Priority (lower = higher priority, default: 10).
	 *
	 * @return self
	 */
	public function register_path( string $extension, string $path, int $priority = 10 ): self {
		// Sanitize path using WordPress functions.
		$path = untrailingslashit( wp_normalize_path( $path ) );

		// Validate path exists.
		if ( empty( $path ) || ! is_dir( $path ) ) {
			return $this;
		}

		// Initialize extension array if needed.
		if ( ! isset( $this->paths[ $extension ] ) ) {
			$this->paths[ $extension ] = array();
		}

		// Add path with priority.
		$this->paths[ $extension ][ $path ] = $priority;

		// Sort by priority using WordPress array functions.
		asort( $this->paths[ $extension ] );

		return $this;
	}

	/**
	 * Get template using WordPress locate_template
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 * @param array  $args     Variables to pass to template.
	 *
	 * @return void
	 */
	public function get_template( string $template, array $args = array() ): void {
		$located = $this->locate_template( $template );

		if ( empty( $located ) ) {
			$this->handle_missing_template( $template );

			return;
		}

		/**
		 * Filter template arguments before loading
		 * Following WordPress core filter patterns
		 *
		 * @since 3.0.0
		 *
		 * @param array  $args     Template arguments.
		 * @param string $template Template file path.
		 */
		$args = (array) apply_filters( 'dokan_kits_template_args', $args, $template );

		// Use WordPress load_template if no args, otherwise custom load.
		load_template( $located, false, $args );
	}

	/**
	 * Get template HTML using WordPress output buffering
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 * @param array  $args     Variables to pass to template.
	 *
	 * @return string Template HTML.
	 */
	public function get_template_html( string $template, array $args = array() ): string {
		ob_start();

		$this->get_template( $template, $args );

		return ob_get_clean();
	}

	/**
	 * Get template part using WordPress patterns.
	 *
	 * @since 3.0.0
	 *
	 * @param string $slug Template slug.
	 * @param string $name Optional. Template name.
	 * @param array  $args Variables to pass to template.
	 *
	 * @return void
	 */
	public function get_template_part( string $slug, string $name = '', array $args = array() ): void {
		/**
		 * Fires before the specified template part file is loaded.
		 * Following WordPress core pattern from get_template_part()
		 *
		 * @since 3.0.0
		 *
		 * @param string $slug The slug name for the generic template.
		 * @param string $name The name of the specialized template.
		 * @param array  $args Additional arguments passed to the template.
		 */
		do_action( "dokan_kits_get_template_part_{$slug}", $slug, $name, $args );

		// Build template names using WordPress conventions.
		$templates = array();
		if ( ! empty( $name ) ) {
			$templates[] = "{$slug}-{$name}.php";
		}

		$templates[] = "{$slug}.php";

		// Use WordPress locate_template first.
		$located = locate_template( $this->build_theme_paths( $templates ), false, false );

		// If not found in theme, check plugin paths.
		if ( empty( $located ) ) {
			$located = $this->locate_in_plugin_paths( $templates );
		}

		if ( ! empty( $located ) ) {
			/**
			 * Filter template arguments before loading
			 * Following WordPress core filter patterns
			 *
			 * @since 3.0.0
			 *
			 * @param array  $args     Template arguments.
			 * @param string $template Template file path.
			 */
			$args = (array) apply_filters( 'dokan_kits_template_args', $args, $located );

			load_template( $located, false, $args );
		} else {
			$this->handle_missing_template( $templates[0] );
		}
	}

	/**
	 * Locate template using WordPress core locate_template first
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 *
	 * @return string Template path or empty string.
	 */
	public function locate_template( string $template ): string {
		// Sanitize template name using WordPress functions.
		$template = $this->sanitize_template_name( $template );

		// 1. First check theme using WordPress core locate_template
		$theme_templates = $this->build_theme_paths( array( $template ) );
		$located         = locate_template( $theme_templates, false, false );

		// 2. If not found in theme, check plugin paths
		if ( empty( $located ) ) {
			$located = $this->locate_in_plugin_paths( array( $template ) );
		}

		return $located;
	}

	/**
	 * Check if template exists
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 *
	 * @return bool
	 */
	public function template_exists( string $template ): bool {
		return ! empty( $this->locate_template( $template ) );
	}

	/**
	 * Clear template cache using WordPress functions
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function clear_cache(): void {
		// Clear all dokan_kits template transients.
		global $wpdb;

		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
				'_transient_dokan_kits_template_%'
			)
		);

		/**
		 * Fires after template cache is cleared.
		 *
		 * @since 3.0.0
		 */
		do_action( 'dokan_kits_template_cache_cleared' );
	}

	/**
	 * Get all registered paths
	 *
	 * @since 3.0.0
	 *
	 * @return array
	 */
	public function get_paths(): array {
		/**
		 * Filter the template paths.
		 *
		 * @since 3.0.0
		 *
		 * @param array $paths Template paths.
		 */
		return apply_filters( 'dokan_kits_template_paths', $this->paths );
	}

	/**
	 * Build theme template paths for WordPress locate_template
	 *
	 * @since 3.0.0
	 *
	 * @param array $templates Template names.
	 *
	 * @return array Theme template paths.
	 */
	protected function build_theme_paths( array $templates ): array {
		$theme_paths = array();

		foreach ( $templates as $template ) {
			// Add dokan-kits subdirectory (for theme overrides).
			$theme_paths[] = 'dokan-kits/' . $template;
			// Add direct template name.
			$theme_paths[] = $template;
		}

		return $theme_paths;
	}

	/**
	 * Locate template in plugin paths
	 *
	 * @since 3.0.0
	 *
	 * @param array $templates Template names.
	 *
	 * @return string Template path or empty string.
	 */
	protected function locate_in_plugin_paths( array $templates ): string {
		// Flatten and sort all paths by priority using WordPress array functions.
		$all_paths = array();

		foreach ( $this->paths as $extension_paths ) {
			$all_paths = array_merge( $all_paths, $extension_paths );
		}

		// Sort by priority (lower number = higher priority).
		asort( $all_paths );

		// Check each path for each template.
		foreach ( array_keys( $all_paths ) as $path ) {
			foreach ( $templates as $template ) {
				$file = trailingslashit( $path ) . $template;

				if ( file_exists( $file ) && is_readable( $file ) ) {
					return $file;
				}
			}
		}

		return '';
	}

	/**
	 * Handle missing template using WordPress core patterns
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 *
	 * @return void
	 */
	protected function handle_missing_template( string $template ): void {
		if ( WP_DEBUG ) {
			_doing_it_wrong(
				__METHOD__,
				// translators: %s: template name.
				sprintf( esc_html__( 'Template %s not found.', 'dokan-kits' ), '<code>' . esc_html( $template ) . '</code>' ),
				'3.0.0'
			);
		}

		/**
		 * Action when template is not found
		 * Following WordPress core action patterns
		 *
		 * @since 3.0.0
		 *
		 * @param string $template Template name.
		 */
		do_action( 'dokan_kits_template_not_found', $template );
	}

	/**
	 * Sanitize template name using WordPress core functions
	 *
	 * @since 3.0.0
	 *
	 * @param string $template Template name.
	 *
	 * @return string Sanitized template name.
	 */
	protected function sanitize_template_name( string $template ): string {
		// Remove path traversal attempts.
		$template = str_replace( array( '../', '..\\', '..' ), '', $template );

		// Normalize path using WordPress function.
		return wp_normalize_path( $template );
	}
}
