<?php
/**
 * Safe Mode state and admin UX.
 *
 * @package Polanger_Shield
 */

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

class Polanger_Shield_Safe_Mode {

  /**
   * Transient prefix.
   */
  const TRANSIENT_PREFIX = 'polanger_shield_safe_mode_';

  /**
   * Admin post action.
   */
  const ACTION = 'polanger_shield_safe_mode';

  /**
   * Default duration in minutes.
   */
  const DEFAULT_DURATION_MINUTES = 15;

  /**
   * Singleton.
   *
   * @var Polanger_Shield_Safe_Mode|null
   */
  private static $instance = null;

  /**
   * Get singleton.
   *
   * @return Polanger_Shield_Safe_Mode
   */
  public static function instance() {
    if ( null === self::$instance ) {
      self::$instance = new self();
    }

    return self::$instance;
  }

  /**
   * Register hooks.
   *
   * @return void
   */
  public function register_hooks() {
    add_action( 'admin_post_' . self::ACTION, array( $this, 'handle_admin_post' ) );
    add_action( 'admin_notices', array( $this, 'render_notice' ) );
    add_action( 'admin_init', array( $this, 'maybe_migrate_legacy_query' ), 0 );
  }

  /**
   * Whether Shield is globally disabled.
   *
   * @return bool
   */
  public static function is_globally_disabled() {
    return defined( 'POLANGER_SHIELD_DISABLE' ) && POLANGER_SHIELD_DISABLE;
  }

  /**
   * Whether Safe Mode is active for the current user.
   *
   * @return bool
   */
  public static function is_active() {
    if ( self::is_globally_disabled() ) {
      return true;
    }

    if ( ! Polanger_Shield_Permissions::current_user_can_manage_shield( true ) ) {
      return false;
    }

    $state = self::get_current_state();
    return ! empty( $state['expires_at'] );
  }

  /**
   * Enable Safe Mode for current user.
   *
   * @return void
   */
  public static function enable_for_current_user() {
    $user_id = get_current_user_id();
    if ( ! $user_id ) {
      return;
    }

    $duration_minutes = (int) apply_filters( 'polanger_shield_safe_mode_duration_minutes', self::DEFAULT_DURATION_MINUTES );
    $duration_minutes = max( 1, $duration_minutes );
    $expires_at       = time() + ( $duration_minutes * MINUTE_IN_SECONDS );

    set_transient(
      self::get_transient_key( $user_id ),
      array(
        'enabled_at' => time(),
        'expires_at' => $expires_at,
      ),
      $duration_minutes * MINUTE_IN_SECONDS
    );
  }

  /**
   * Disable Safe Mode for current user.
   *
   * @return void
   */
  public static function disable_for_current_user() {
    $user_id = get_current_user_id();
    if ( ! $user_id ) {
      return;
    }

    delete_transient( self::get_transient_key( $user_id ) );
  }

  /**
   * Get Safe Mode state for current user.
   *
   * @return array
   */
  public static function get_current_state() {
    $user_id = get_current_user_id();
    if ( ! $user_id ) {
      return array();
    }

    $state = get_transient( self::get_transient_key( $user_id ) );
    if ( ! is_array( $state ) ) {
      return array();
    }

    $expires_at = isset( $state['expires_at'] ) ? absint( $state['expires_at'] ) : 0;
    if ( $expires_at <= time() ) {
      delete_transient( self::get_transient_key( $user_id ) );
      return array();
    }

    return array(
      'enabled_at' => isset( $state['enabled_at'] ) ? absint( $state['enabled_at'] ) : 0,
      'expires_at' => $expires_at,
    );
  }

  /**
   * Get remaining seconds for current user.
   *
   * @return int
   */
  public static function get_remaining_seconds() {
    if ( self::is_globally_disabled() ) {
      return 0;
    }

    $state = self::get_current_state();
    if ( empty( $state['expires_at'] ) ) {
      return 0;
    }

    return max( 0, absint( $state['expires_at'] ) - time() );
  }

  /**
   * Handle enable/disable requests.
   *
   * @return void
   */
  public function handle_admin_post() {
    if ( ! Polanger_Shield_Permissions::current_user_can_manage_shield( true ) ) {
      wp_die( esc_html__( 'Permission denied.', 'polanger-admin-menu-manager' ) );
    }

    check_admin_referer( self::ACTION );

    $mode = isset( $_REQUEST['mode'] ) ? sanitize_key( wp_unslash( $_REQUEST['mode'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

    if ( 'enable' === $mode ) {
      self::enable_for_current_user();
    } elseif ( 'disable' === $mode ) {
      self::disable_for_current_user();
    }

    $redirect = $this->get_redirect_target();
    wp_safe_redirect( $redirect );
    exit;
  }

  /**
   * Show admin notice while Safe Mode is active.
   *
   * @return void
   */
  public function render_notice() {
    if ( ! is_admin() || ! Polanger_Shield_Permissions::current_user_can_manage_shield( true ) ) {
      return;
    }

    if ( self::is_globally_disabled() ) {
      ?>
      <div class="notice notice-warning">
        <p>
          <?php esc_html_e( 'Shield Safe Mode is forced globally by the POLANGER_SHIELD_DISABLE constant. All Shield page blocks, menu hiding, element hiding, notes, and floating tools are currently disabled.', 'polanger-admin-menu-manager' ); ?>
        </p>
      </div>
      <?php
      return;
    }

    if ( ! self::is_active() ) {
      return;
    }

    $remaining = self::get_remaining_seconds();
    $expires_at = time() + $remaining;
    $dashboard_url = admin_url( 'admin.php?page=polanger-shield' );
    $disable_url = wp_nonce_url(
      add_query_arg(
        array(
          'action'      => self::ACTION,
          'mode'        => 'disable',
          'redirect_to' => rawurlencode( $this->get_current_admin_url() ),
        ),
        admin_url( 'admin-post.php' )
      ),
      self::ACTION
    );
    ?>
    <div class="notice notice-warning">
      <p>
        <?php
        echo esc_html(
          sprintf(
            /* translators: %s: expiry time */
            __( 'Shield Safe Mode is active for your account until %s. All Shield page blocks, menu hiding, element hiding, notes, and floating tools are temporarily disabled while you review the admin area safely.', 'polanger-admin-menu-manager' ),
            wp_date( get_option( 'time_format' ), $expires_at )
          )
        );
        ?>
      </p>
      <p>
        <a class="button button-secondary" href="<?php echo esc_url( $dashboard_url ); ?>">
          <?php esc_html_e( 'Open Shield Dashboard', 'polanger-admin-menu-manager' ); ?>
        </a>
        <a class="button button-primary" href="<?php echo esc_url( $disable_url ); ?>">
          <?php esc_html_e( 'Exit Safe Mode', 'polanger-admin-menu-manager' ); ?>
        </a>
      </p>
    </div>
    <?php
  }

  /**
   * Support old ?polanger_shield_safe=1 links by converting them into
   * a timed user-scoped Safe Mode session and then redirecting to a clean URL.
   *
   * @return void
   */
  public function maybe_migrate_legacy_query() {
    if ( self::is_globally_disabled() || ! Polanger_Shield_Permissions::current_user_can_manage_shield( true ) ) {
      return;
    }

    if ( ! isset( $_GET['polanger_shield_safe'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
      return;
    }

    self::enable_for_current_user();

    $redirect = $this->get_current_admin_url();
    $redirect = remove_query_arg( 'polanger_shield_safe', $redirect );

    wp_safe_redirect( $redirect );
    exit;
  }

  /**
   * Build the enable URL.
   *
   * @param string $redirect_to Optional redirect target.
   * @return string
   */
  public static function get_enable_url( $redirect_to = '' ) {
    $redirect_to = $redirect_to ? $redirect_to : admin_url();

    return wp_nonce_url(
      add_query_arg(
        array(
          'action'      => self::ACTION,
          'mode'        => 'enable',
          'redirect_to' => rawurlencode( $redirect_to ),
        ),
        admin_url( 'admin-post.php' )
      ),
      self::ACTION
    );
  }

  /**
   * Build the disable URL.
   *
   * @param string $redirect_to Optional redirect target.
   * @return string
   */
  public static function get_disable_url( $redirect_to = '' ) {
    $redirect_to = $redirect_to ? $redirect_to : admin_url();

    return wp_nonce_url(
      add_query_arg(
        array(
          'action'      => self::ACTION,
          'mode'        => 'disable',
          'redirect_to' => rawurlencode( $redirect_to ),
        ),
        admin_url( 'admin-post.php' )
      ),
      self::ACTION
    );
  }

  /**
   * Resolve transient key for a user.
   *
   * @param int $user_id User ID.
   * @return string
   */
  private static function get_transient_key( $user_id ) {
    return self::TRANSIENT_PREFIX . absint( $user_id );
  }

  /**
   * Resolve post-action redirect target.
   *
   * @return string
   */
  private function get_redirect_target() {
    $redirect = isset( $_REQUEST['redirect_to'] ) ? wp_unslash( $_REQUEST['redirect_to'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $redirect = is_string( $redirect ) ? rawurldecode( $redirect ) : '';
    $redirect = wp_validate_redirect( $redirect, admin_url() );

    return $redirect ? $redirect : admin_url();
  }

  /**
   * Get the current admin URL.
   *
   * @return string
   */
  private function get_current_admin_url() {
    $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
    $request_uri = is_string( $request_uri ) ? $request_uri : '';

    if ( '' === $request_uri ) {
      return admin_url();
    }

    $admin_parts = wp_parse_url( admin_url() );
    if ( ! is_array( $admin_parts ) || empty( $admin_parts['host'] ) ) {
      return admin_url();
    }

    $scheme = ! empty( $admin_parts['scheme'] ) ? $admin_parts['scheme'] : ( is_ssl() ? 'https' : 'http' );
    $host   = (string) $admin_parts['host'];
    $port   = ! empty( $admin_parts['port'] ) ? ':' . absint( $admin_parts['port'] ) : '';
    $url    = $scheme . '://' . $host . $port . $request_uri;

    return wp_validate_redirect( $url, admin_url() );
  }
}
