<?php
class OsController {

	protected $params,
	  $files,
	$layout                        = 'admin',
	$views_folder                  = LATEPOINT_VIEWS_ABSPATH_SHARED,
	$return_format                 = 'html',
	$extra_css_classes             = [ 'latepoint' ];
	public array $fields_to_update = [];

	// if an action can only be accessed by a backend user, we need to define capabilities that are required
	public array $controller_capabilities = [ 'settings__edit' ];  // default for controller
	public array $action_capabilities     = []; // per action

	public array $action_access = [
		'customer' => [],
		'public'   => [],
	];

	public $vars;
	public $route_name;



	function __construct() {
		$this->params = $this->get_params();
		$this->files  = $this->get_files();
		$this->set_layout( $this->layout );
		$this->vars['page_header']   = __( 'Bookings', 'latepoint' );
		$this->vars['breadcrumbs'][] = array(
			'label' => __( 'Dashboard', 'latepoint' ),
			'link'  => OsRouterHelper::build_link( [ 'dashboard', 'index' ] ),
		);

		$this->load_settings();
		$this->vars['logged_in_customer'] = OsAuthHelper::get_logged_in_customer();
	}

	public function check_nonce( $action, $custom_nonce = '' ) {
		$nonce = ! empty( $custom_nonce ) ? $custom_nonce : $this->params['_wpnonce'];
		if ( ! wp_verify_nonce( $nonce, $action ) ) {
			if ( $this->get_return_format() == 'json' ) {
				$this->send_json(
					array(
						'status'  => LATEPOINT_STATUS_ERROR,
						'message' => __( 'Invalid Request', 'latepoint' ),
					) 
				);
			} else {
				wp_die();
			}
		}
	}

	public function can_current_user_access_action( string $action ): bool {
		if ( in_array( $action, $this->action_access['public'] ) ) {
			// public route
			$can = true;
		} elseif ( in_array( $action, $this->action_access['customer'] ) && OsAuthHelper::get_current_user()->customer ) {
			// customer route & customer is logged in
			$can = true;
		} else {
			// backend route, check for capabilities
			$can = OsAuthHelper::get_current_user()->has_capability( $this->get_capabilities_required_for_action( $action ) );
		}

		/**
		 * Determines if a currently logged in user can access controller's action
		 *
		 * @since 4.7.0
		 * @hook latepoint_can_current_user_access_action
		 *
		 * @param {bool} $can Decision true|false
		 * @param {string} $action Name of the action that is being called
		 * @param {LatePoint\Misc\User} $current_user Currently logged in latepoint user
		 * @returns {bool} Decision true|false
		 */
		return apply_filters( 'latepoint_can_current_user_access_action', $can, $action, OsAuthHelper::get_current_user() );
	}

	public function get_capabilities_required_for_action( $action ) {
		return OsRolesHelper::get_capabilities_required_for_controller_action( get_class( $this ), $action );
	}

	function generate_css_class( $view_name ) {
		$class_name_filtered = strtolower( preg_replace( '/^Os(\w+)Controller/i', '$1', static::class ) );
		return "latepoint-view-{$class_name_filtered}-{$view_name}";
	}

	protected function load_settings() {
	}


	public function access_not_allowed() {
		$this->format_render( __FUNCTION__, [], [], true );
		exit();
	}

	function format_render( $view_name, $extra_vars = array(), $json_return_vars = array(), $from_shared_folder = false ) {
		echo $this->format_render_return( $view_name, $extra_vars, $json_return_vars, $from_shared_folder );
	}

	// You can pass array to $view_name, ['json_view_name' => ..., 'html_view_name' => ...]
	function format_render_return( $view_name, $extra_vars = array(), $json_return_vars = array(), $from_shared_folder = false ) {
		$html = '';
		if ( $this->get_return_format() == 'json' ) {
			if ( is_array( $view_name ) ) {
				$view_name = $view_name['json_view_name'];
			}
			$response_html = $this->render( $this->get_view_uri( $view_name, $from_shared_folder ), 'none', $extra_vars );
			$this->send_json(
				array_merge(
					array(
						'status'  => LATEPOINT_STATUS_SUCCESS,
						'message' => $response_html,
					),
					$json_return_vars 
				) 
			);
		} else {
			if ( is_array( $view_name ) ) {
				$view_name = $view_name['html_view_name'];
			}
			$this->extra_css_classes[]       = $this->generate_css_class( $view_name );
			$this->vars['extra_css_classes'] = $this->extra_css_classes;
			$html                            = $this->render( $this->get_view_uri( $view_name, $from_shared_folder ), $this->get_layout(), $extra_vars );
		}
		return $html;
	}

	function set_layout( $layout = 'admin' ) {
		if ( isset( $this->params['layout'] ) ) {
			$this->layout = $this->params['layout'];
		} else {
			$this->layout = $layout;
		}
	}

	function get_layout() {
		return $this->layout;
	}

	function set_return_format( $format = 'html' ) {
		$this->return_format = $format;
	}

	function get_return_format() {
		return $this->return_format;
	}

	function send_json( $data, $status_code = null ) {
		if ( ! empty( $this->fields_to_update ) ) {
			$data['fields_to_update'] = $this->fields_to_update;
		}
		wp_send_json( $data, $status_code );
	}

	function get_view_uri( $view_name, $from_shared_folder = false ) {
		if ( $from_shared_folder ) {
			$view_uri = LATEPOINT_VIEWS_ABSPATH_SHARED . $view_name . '.php';
		} else {
			$view_uri = $this->views_folder . $view_name . '.php';
		}
		return $view_uri;
	}

	private function get_safe_layout_path( $layout ) {
		// 1. Remove any path separators and null bytes
		$layout = str_replace( [ '/', '\\', "\0" ], '', $layout );

		// 2. Remove any dots to prevent directory traversal
		$layout = str_replace( '.', '', $layout );

		// 3. Only allow alphanumeric, underscore, and hyphen
		$layout = preg_replace( '/[^a-zA-Z0-9_-]/', '', $layout );

		// 4. Construct the full path
		$layout_file = $this->add_extension( $layout, '.php' );
		$full_path   = LATEPOINT_VIEWS_LAYOUTS_ABSPATH . $layout_file;

		// 5. Use realpath to resolve any remaining traversal attempts
		$real_path = realpath( $full_path );
		$base_path = realpath( LATEPOINT_VIEWS_LAYOUTS_ABSPATH );

		// 6. Ensure the resolved path is within the layouts directory
		if ( $real_path && $base_path && strpos( $real_path, $base_path ) === 0 ) {
			return $real_path;
		}

		return false;
	}

	// render view and if needed layout, when layout is rendered - view variable is passed to a layout file
	function render( $view, $layout = 'none', $extra_vars = array() ) {
		$this->vars['route_name'] = $this->route_name;
		extract( $extra_vars );
		extract( $this->vars );
		ob_start();
		if ( $layout != 'none' ) {
			$layout_path = $this->get_safe_layout_path( $layout );
			// rendering layout, view variable will be passed and used in layout file
			if ( $layout_path ) {
				include $layout_path;
			} else {
				__( 'Invalid layout', 'latepoint' );
			}
		} else {
			include $this->add_extension( $view, '.php' );
		}
		$response_html = ob_get_clean();
		return $response_html;
	}

	/*
	Adds extension to a file string if its missing
	*/
	function add_extension( $string = '', $extension = '.php' ) {
		if ( substr( $string, -strlen( $extension ) ) === $extension ) {
			return $string;
		} else {
			return $string . $extension;
		}
	}

	function get_files() {
		return OsParamsHelper::get_files();
	}

	function get_params() {
		return OsParamsHelper::get_params();
	}
}
