<?php

namespace WC_Affiliate\Abstracts;

defined( 'ABSPATH' ) || exit;

use WC_Affiliate\Traits\Asset;
use WC_Affiliate\Traits\Hook;

/**
 * Abstract Controller Class
 *
 * Base class for all controllers in the WC Affiliate plugin.
 * Provides common functionality and enforces structure across controllers.
 *
 * @since   1.0.0
 * @package WC_Affiliate\Abstracts
 */
abstract class Controller {

	use Hook;
	use Asset;

	/**
	 * Controller instance
	 *
	 * @var static|null
	 */
	protected static ?Controller $instance = null;

	/**
	 * Controller context (admin, front, common)
	 *
	 * @var string
	 */
	protected $context = 'common';

	/**
	 * Whether controller is initialized
	 *
	 * @var bool
	 */
	protected $initialized = false;

	/**
	 * Registered hooks
	 *
	 * @var array
	 */
	protected $hooks = array();

	/**
	 * Controller constructor
	 *
	 * Automatically calls the init method if it exists
	 */
	public function __construct() {
		$this->set_context();
		$this->register_hooks();

		$this->initialized = true;
	}

	/**
	 * Set controller context based on namespace
	 */
	protected function set_context() {
		$class = get_class( $this );

		if ( strpos( $class, '\\Admin\\' ) !== false ) {
			$this->context = 'admin';
		} elseif ( strpos( $class, '\\Front\\' ) !== false ) {
			$this->context = 'front';
		} else {
			$this->context = 'common';
		}
	}

	/**
	 * Get controller context
	 *
	 * @return string
	 */
	public function get_context(): string {
		return $this->context;
	}

	/**
	 * Check if controller is for admin
	 *
	 * @return bool
	 */
	public function is_admin_controller(): bool {
		return 'admin' === $this->context;
	}

	/**
	 * Check if controller is for frontend
	 *
	 * @return bool
	 */
	public function is_front_controller(): bool {
		return 'front' === $this->context;
	}

	/**
	 * Check if controller is common (both admin and front)
	 *
	 * @return bool
	 */
	public function is_common_controller(): bool {
		return 'common' === $this->context;
	}

	/**
	 * Register controller hooks
	 * Override this method in child classes to register hooks
	 */
	protected function register_hooks() {
		// Override in child classes.
	}

	/**
	 * Add assets for the controller
	 * Override this method in child classes to add specific assets
	 */
	public function add_assets() {
		// Override in child classes.
	}

	/**
	 * Check if we should load assets based on context
	 *
	 * @return bool
	 */
	protected function should_load_assets(): bool {
		global $current_screen;

		switch ( $this->context ) {
			case 'admin':
				return is_admin() && (
						! $current_screen ||
						strpos( $current_screen->base, 'wc-affiliate' ) !== false
					);

			case 'front':
				return ! is_admin();

			case 'common':
				return true;

			default:
				return false;
		}
	}

	/**
	 * Enqueue common assets based on context
	 */
	protected function enqueue_common_assets() {
		if ( ! $this->should_load_assets() ) {
			return;
		}

		$min        = ( defined( 'WC_AFFILIATE_DEBUG' ) && WC_AFFILIATE_DEBUG ) ? '' : '.min';
		$assets_url = defined( 'WC_AFFILIATE_ASSETS_URL' ) ? WC_AFFILIATE_ASSETS_URL : '';

		if ( empty( $assets_url ) ) {
			return;
		}

		// Common CSS.
		$this->enqueue_style(
			'wc-affiliate-common',
			$assets_url . 'common/css/init.css'
		);

		// Common JS.
		$this->enqueue_script(
			'wc-affiliate-common',
			$assets_url . 'common/js/init.js',
			array( 'jquery' )
		);

		// Context-specific assets.
		switch ( $this->context ) {
			case 'admin':
				$this->enqueue_admin_assets();
				break;

			case 'front':
				$this->enqueue_front_assets();
				break;
		}
	}

	/**
	 * Enqueue admin-specific assets
	 */
	protected function enqueue_admin_assets() {
		if ( ! is_admin() ) {
			return;
		}

		$min        = ( defined( 'WC_AFFILIATE_DEBUG' ) && WC_AFFILIATE_DEBUG ) ? '' : '.min';
		$assets_url = defined( 'WC_AFFILIATE_ASSETS_URL' ) ? WC_AFFILIATE_ASSETS_URL : '';

		if ( empty( $assets_url ) ) {
			return;
		}

		// Admin CSS.
		$this->enqueue_style(
			'wc-affiliate-admin',
			$assets_url . "admin/css/admin{$min}.css"
		);

		// Admin JS.
		$this->enqueue_script(
			'wc-affiliate-admin',
			$assets_url . "admin/js/admin{$min}.js",
			array( 'jquery', 'wc-affiliate-common' )
		);
	}

	/**
	 * Enqueue frontend-specific assets
	 */
	protected function enqueue_front_assets() {
		if ( is_admin() ) {
			return;
		}

		$min        = ( defined( 'WC_AFFILIATE_DEBUG' ) && WC_AFFILIATE_DEBUG ) ? '' : '.min';
		$assets_url = defined( 'WC_AFFILIATE_ASSETS_URL' ) ? WC_AFFILIATE_ASSETS_URL : '';

		if ( empty( $assets_url ) ) {
			return;
		}

		// Frontend CSS.
		$this->enqueue_style(
			'wc-affiliate-dashboard',
			$assets_url . "dashboard/css/dashboard{$min}.css"
		);

		// Frontend JS.
		$this->enqueue_script(
			'wc-affiliate-dashboard',
			$assets_url . "dashboard/js/dashboard{$min}.js",
			array( 'jquery', 'wc-affiliate-common' )
		);
	}

	/**
	 * Check if controller is initialized
	 *
	 * @return bool
	 */
	public function is_initialized(): bool {
		return $this->initialized;
	}

	/**
	 * Handle AJAX requests
	 * Override this method in child classes for AJAX handling
	 *
	 * @param string $action AJAX action name.
	 * @param array  $data   Request data.
	 *
	 * @return array|\WP_Error
	 */
	protected function handle_ajax( string $action, array $data = array() ) {
		return new \WP_Error( 'not_implemented', __( 'AJAX handler not implemented.', 'wc-affiliate' ) );
	}

	/**
	 * Verify nonce for security
	 *
	 * @param string $nonce  Nonce to verify.
	 * @param string $action Action for nonce verification.
	 *
	 * @return bool
	 */
	protected function verify_nonce( string $nonce, string $action = 'wc_affiliate_nonce' ): bool {
		return wp_verify_nonce( $nonce, $action );
	}

	/**
	 * Check user capabilities
	 *
	 * @param string $capability Required capability.
	 * @param mixed  $object_id  Optional object ID for capability check.
	 *
	 * @return bool
	 */
	protected function current_user_can( string $capability, $object_id = null ): bool {
		if ( $object_id ) {
			return current_user_can( $capability, $object_id );
		}

		return current_user_can( $capability );
	}

	/**
	 * Send JSON response
	 *
	 * @param mixed $data        Response data.
	 * @param bool  $success     Whether the response is successful.
	 * @param int   $status_code HTTP status code.
	 */
	protected function send_json_response( $data, bool $success = true, int $status_code = 200 ): void {
		if ( $success ) {
			wp_send_json_success( $data, $status_code );
		} else {
			wp_send_json_error( $data, $status_code );
		}
	}

	/**
	 * Log debug information
	 *
	 * @param mixed  $message Message to log.
	 * @param string $level   Log level.
	 */
	protected function log( $message, string $level = 'info' ): void {
		if ( defined( 'WC_AFFILIATE_DEBUG' ) && WC_AFFILIATE_DEBUG ) {
			error_log(
				sprintf(
					'[WC Affiliate - %s] %s: %s',
					strtoupper( $level ),
					get_class( $this ),
					is_string( $message ) ? $message : print_r( $message, true )
				)
			);
		}
	}

	/**
	 * Get template path for the controller.
	 *
	 * @param string $template Template name.
	 *
	 * @return string
	 */
	protected function get_template_path( string $template ): string {
		$context_path = 'common' === $this->context ? '' : $this->context . '/';
		$plugin_path  = WC_AFFILIATE_PLUGIN_DIR;

		return $plugin_path . 'views/' . $context_path . $template . '.php';
	}

	/**
	 * Load and return template content.
	 *
	 * @param string $template_path Template name.
	 *
	 * @return string
	 */
	protected function get_template_data( string $template_path ): string {
		ob_start();
		include $template_path;

		return ob_get_clean();
	}

	/**
	 * Render template.
	 *
	 * @param string $template Template name.
	 * @param array  $vars     Variables to pass to template.
	 */
	protected function render_template( string $template, array $vars = array() ): string {
		$template_path = $this->get_template_path( $template );

		if ( ! file_exists( $template_path ) ) {
			return '';
		}

		if ( ! empty( $data ) && is_array( $data ) ) {
			extract( $data );
		}

		return $this->get_template_data( $template_path );
	}
}
