<?php
/**
 * Admin page context resolver.
 *
 * @package Polanger_Shield
 */

if ( ! defined( 'ABSPATH' ) ) {
  exit;
}

class Polanger_Shield_Context {

  /**
   * Per-request cache for the current admin context.
   *
   * @var array|null
   */
  private static $current_context_cache = null;

  /**
   * Per-request cache for the normalized request URI.
   *
   * @var string|null
   */
  private static $normalized_request_uri_cache = null;

  /**
   * Per-request cache for URL-derived contexts.
   *
   * @var array<string, array>
   */
  private static $from_url_cache = array();

  /**
   * Get normalized context for current admin screen.
   *
   * @return array
   */
  public static function current() {
    if ( null !== self::$current_context_cache ) {
      return self::$current_context_cache;
    }

    $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

    // phpcs:disable WordPress.Security.NonceVerification.Recommended
    $page_slug = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
    // phpcs:enable WordPress.Security.NonceVerification.Recommended

    self::$current_context_cache = array(
      'screen_id'   => $screen ? (string) $screen->id : '',
      'page_hook'   => isset( $GLOBALS['hook_suffix'] ) ? sanitize_text_field( (string) $GLOBALS['hook_suffix'] ) : '',
      'page_slug'   => $page_slug,
      'request_uri' => self::normalized_request_uri(),
      'pagenow'     => isset( $GLOBALS['pagenow'] ) ? sanitize_text_field( (string) $GLOBALS['pagenow'] ) : '',
    );

    return self::$current_context_cache;
  }

  /**
   * Normalize request uri while dropping volatile params.
   *
   * @return string
   */
  public static function normalized_request_uri() {
    if ( null !== self::$normalized_request_uri_cache ) {
      return self::$normalized_request_uri_cache;
    }

    $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
    $request_uri = is_string( $request_uri ) ? $request_uri : '';

    if ( '' === $request_uri ) {
      self::$normalized_request_uri_cache = '';
      return self::$normalized_request_uri_cache;
    }

    $url_parts = wp_parse_url( $request_uri );
    $path = isset( $url_parts['path'] ) ? $url_parts['path'] : '';
    $query = array();

    if ( ! empty( $url_parts['query'] ) ) {
      parse_str( $url_parts['query'], $query );
    }

    self::$normalized_request_uri_cache = self::normalize_path_and_query( $path, $query );

    return self::$normalized_request_uri_cache;
  }

  /**
   * Build a Shield context array from a URL or request URI.
   *
   * @param string $url Raw URL or request URI.
   * @return array
   */
  public static function from_url( $url ) {
    $url = is_string( $url ) ? trim( $url ) : '';
    if ( '' === $url ) {
      return self::empty_context();
    }

    if ( isset( self::$from_url_cache[ $url ] ) ) {
      return self::$from_url_cache[ $url ];
    }

    $parts = wp_parse_url( $url );
    if ( false === $parts ) {
      self::$from_url_cache[ $url ] = self::empty_context();
      return self::$from_url_cache[ $url ];
    }

    $path = isset( $parts['path'] ) ? (string) $parts['path'] : '';
    if ( '' === $path || false === strpos( $path, '/wp-admin/' ) ) {
      self::$from_url_cache[ $url ] = self::empty_context();
      return self::$from_url_cache[ $url ];
    }

    $query = array();
    if ( ! empty( $parts['query'] ) ) {
      parse_str( (string) $parts['query'], $query );
    }

    $page_slug = isset( $query['page'] ) ? sanitize_text_field( wp_unslash( (string) $query['page'] ) ) : '';

    self::$from_url_cache[ $url ] = array(
      'screen_id'   => '',
      'page_hook'   => '',
      'page_slug'   => $page_slug,
      'request_uri' => self::normalize_path_and_query( $path, $query ),
      'pagenow'     => sanitize_text_field( wp_basename( $path ) ),
    );

    return self::$from_url_cache[ $url ];
  }

  /**
   * Normalize a request path and query.
   *
   * @param string $path  Request path.
   * @param array  $query Query args.
   * @return string
   */
  private static function normalize_path_and_query( $path, $query ) {
    $volatile = array(
      '_wpnonce',
      '_wp_http_referer',
      'updated',
      'settings-updated',
      'message',
      'paged',
      'polanger_shield_test_rule',
      'polanger_shield_demo_locked',
    );

    foreach ( $volatile as $key ) {
      unset( $query[ $key ] );
    }

    $normalized = (string) $path;
    if ( ! empty( $query ) ) {
      ksort( $query );
      $normalized .= '?' . http_build_query( $query, '', '&' );
    }

    return sanitize_text_field( $normalized );
  }

  /**
   * Empty context helper.
   *
   * @return array
   */
  private static function empty_context() {
    return array(
      'screen_id'   => '',
      'page_hook'   => '',
      'page_slug'   => '',
      'request_uri' => '',
      'pagenow'     => '',
    );
  }
}
