<?php
/**
 * AJAX handlers for Login Session Manager.
 *
 * @package LoginSessionManager
 */

namespace LoginSessionManager\Includes;

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

class Session_Ajax {

	private Session_Repository $repository;

	/** @var callable(): array<string, mixed> */
	private $settings_provider;

	/**
	 * @param callable(): array<string, mixed> $settings_provider
	 */
	public function __construct( Session_Repository $repository, callable $settings_provider ) {
		$this->repository        = $repository;
		$this->settings_provider = $settings_provider;
	}

	public function register_hooks(): void {
		add_action( 'wp_ajax_wpps_login_session_manager_list', [ $this, 'list_sessions' ] );
		add_action( 'wp_ajax_wpps_login_session_manager_destroy', [ $this, 'destroy_session' ] );
		add_action( 'wp_ajax_wpps_login_session_manager_destroy_all_user', [ $this, 'destroy_all_user' ] );
		add_action( 'wp_ajax_wpps_login_session_manager_destroy_all_site', [ $this, 'destroy_all_site' ] );
	}

	/**
	 * @return array<string, mixed>
	 */
	private function get_settings(): array {
		$settings = ( $this->settings_provider )();

		return is_array( $settings ) ? $settings : [];
	}

	private function apply_privacy_settings(): void {
		$settings = $this->get_settings();
		$this->repository->set_privacy_options(
			[
				'hide_ip'         => ! empty( $settings['hide_ip'] ),
				'hide_user_agent' => ! empty( $settings['hide_user_agent'] ),
			]
		);
	}

	public function list_sessions(): void {
		$this->verify_request();
		$this->apply_privacy_settings();

		$actor_id = get_current_user_id();
		$user_id  = isset( $_POST['user_id'] ) ? absint( wp_unslash( $_POST['user_id'] ) ) : 0;

		if ( $user_id > 0 ) {
			if ( ! Session_Capabilities::can_view_user_sessions( $actor_id, $user_id ) ) {
				wp_send_json_error(
					[
						'message' => __( 'You do not have permission to view these sessions.', 'wppowersuite-lite' ),
						'code'    => 'insufficient_permissions',
					],
					403
				);
			}

			$sessions = $this->repository->get_sessions_for_user( $user_id );
			$user     = get_userdata( $user_id );
			$stats    = [
				'total_sessions'   => count( $sessions ),
				'users_logged_in'  => $sessions !== [] ? 1 : 0,
				'sessions_by_role' => [],
			];

			if ( $user ) {
				$role = is_array( $user->roles ) ? ( $user->roles[0] ?? 'unknown' ) : 'unknown';
				if ( $sessions !== [] ) {
					$stats['sessions_by_role'][ $role ] = count( $sessions );
				}
			}

			wp_send_json_success(
				[
					'sessions'   => $sessions,
					'stats'      => $stats,
					'pagination' => [
						'page'        => 1,
						'per_page'    => count( $sessions ),
						'total'       => count( $sessions ),
						'total_pages' => count( $sessions ) > 0 ? 1 : 0,
					],
				]
			);
		}

		if ( ! user_can( $actor_id, 'edit_users' ) ) {
			wp_send_json_error(
				[
					'message' => __( 'You do not have permission to view all sessions.', 'wppowersuite-lite' ),
					'code'    => 'insufficient_permissions',
				],
				403
			);
		}

		$filter_user = isset( $_POST['filter_user_id'] ) ? absint( wp_unslash( $_POST['filter_user_id'] ) ) : 0;
		$page        = isset( $_POST['page'] ) ? absint( wp_unslash( $_POST['page'] ) ) : 1;
		$per_page    = isset( $_POST['per_page'] ) ? absint( wp_unslash( $_POST['per_page'] ) ) : 50;
		$search      = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( (string) $_POST['search'] ) ) : '';

		$result = $this->repository->query_sessions(
			[
				'filter_user_id' => $filter_user > 0 ? $filter_user : 0,
				'page'           => $page,
				'per_page'       => $per_page,
				'search'         => $search,
			]
		);

		wp_send_json_success( $result );
	}

	public function destroy_session(): void {
		$this->verify_request();

		if ( ! $this->check_rate_limit( 'destroy', get_current_user_id(), 2 ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Please wait before revoking another session.', 'wppowersuite-lite' ),
					'code'    => 'rate_limited',
				],
				429
			);
		}

		$actor_id = get_current_user_id();
		$user_id  = isset( $_POST['user_id'] ) ? absint( wp_unslash( $_POST['user_id'] ) ) : 0;
		$verifier = isset( $_POST['verifier'] ) ? $this->sanitize_verifier( (string) wp_unslash( $_POST['verifier'] ) ) : '';

		if ( $user_id < 1 || $verifier === '' ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid session.', 'wppowersuite-lite' ),
					'code'    => 'invalid_request',
				]
			);
		}

		if ( ! Session_Capabilities::can_revoke_user_sessions( $actor_id, $user_id ) ) {
			wp_send_json_error(
				[
					'message' => __( 'You do not have permission to revoke this session.', 'wppowersuite-lite' ),
					'code'    => 'insufficient_permissions',
				],
				403
			);
		}

		$destroyed_current = $this->repository->is_current_session( $user_id, $verifier );

		if ( ! $this->repository->destroy_session( $user_id, $verifier ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Session not found or already revoked.', 'wppowersuite-lite' ),
					'code'    => 'not_found',
				]
			);
		}

		Session_Audit_Log::dispatch(
			'destroy_session',
			$actor_id,
			$user_id,
			$verifier
		);

		wp_send_json_success(
			[
				'message'           => __( 'Session revoked.', 'wppowersuite-lite' ),
				'destroyed_current' => $destroyed_current,
				'redirect'          => $destroyed_current ? wp_login_url() : '',
			]
		);
	}

	public function destroy_all_user(): void {
		$this->verify_request();

		if ( ! $this->check_rate_limit( 'destroy_all_user', get_current_user_id(), 3 ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Please wait before revoking more sessions.', 'wppowersuite-lite' ),
					'code'    => 'rate_limited',
				],
				429
			);
		}

		$actor_id  = get_current_user_id();
		$user_ids  = [];

		if ( isset( $_POST['user_ids'] ) && is_array( $_POST['user_ids'] ) ) {
			$user_ids = array_map( 'absint', wp_unslash( $_POST['user_ids'] ) );
		} elseif ( isset( $_POST['user_id'] ) ) {
			$user_ids[] = absint( wp_unslash( $_POST['user_id'] ) );
		}

		$user_ids = array_values( array_filter( array_unique( $user_ids ) ) );

		if ( $user_ids === [] ) {
			wp_send_json_error(
				[
					'message' => __( 'No users selected.', 'wppowersuite-lite' ),
					'code'    => 'invalid_request',
				]
			);
		}

		$total_destroyed   = 0;
		$destroyed_current = false;
		$skipped           = 0;

		foreach ( $user_ids as $user_id ) {
			if ( ! Session_Capabilities::can_revoke_user_sessions( $actor_id, $user_id ) ) {
				++$skipped;
				continue;
			}

			if ( $user_id === get_current_user_id() ) {
				$destroyed_current = true;
			}

			$count = $this->repository->destroy_all_for_user( $user_id );
			if ( $count > 0 ) {
				$total_destroyed += $count;
				Session_Audit_Log::dispatch(
					'destroy_all',
					$actor_id,
					$user_id,
					'',
					[ 'session_count' => $count ]
				);
			}
		}

		if ( $total_destroyed < 1 ) {
			wp_send_json_error(
				[
					'message' => $skipped > 0
						? __( 'No sessions were revoked. Some selected users could not be managed.', 'wppowersuite-lite' )
						: __( 'No sessions were revoked.', 'wppowersuite-lite' ),
					'code'    => 'not_found',
				]
			);
		}

		wp_send_json_success(
			[
				'message'           => sprintf(
					/* translators: %d: number of sessions revoked */
					_n( '%d session revoked.', '%d sessions revoked.', $total_destroyed, 'wppowersuite-lite' ),
					$total_destroyed
				),
				'destroyed_count'   => $total_destroyed,
				'destroyed_current' => $destroyed_current,
				'skipped_users'     => $skipped,
				'redirect'          => $destroyed_current ? wp_login_url() : '',
			]
		);
	}

	public function destroy_all_site(): void {
		$this->verify_request();

		if ( ! $this->check_rate_limit( 'destroy_all_site', get_current_user_id(), 10 ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Please wait before running another site-wide logout.', 'wppowersuite-lite' ),
					'code'    => 'rate_limited',
				],
				429
			);
		}

		$actor_id = get_current_user_id();

		if ( ! Session_Capabilities::can_destroy_all_site_wide( $actor_id ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Only super administrators can revoke all site sessions.', 'wppowersuite-lite' ),
					'code'    => 'insufficient_permissions',
				],
				403
			);
		}

		$result = $this->repository->destroy_all_site_wide();

		Session_Audit_Log::dispatch(
			'destroy_all_site',
			$actor_id,
			0,
			'',
			$result
		);

		wp_send_json_success(
			[
				'message' => sprintf(
					/* translators: 1: number of users, 2: number of sessions */
					__( 'Revoked %2$d sessions across %1$d users.', 'wppowersuite-lite' ),
					(int) $result['destroyed_users'],
					(int) $result['destroyed_sessions']
				),
				'stats'    => $result,
				'redirect' => wp_login_url(),
			]
		);
	}

	private function sanitize_verifier( string $raw ): string {
		if ( ! preg_match( '/^[a-f0-9]{64}$/i', $raw ) ) {
			return '';
		}

		return strtolower( $raw );
	}

	private function check_rate_limit( string $action, int $user_id, int $seconds ): bool {
		if ( $user_id < 1 ) {
			return false;
		}

		$key = 'wpps_lsm_rate_' . sanitize_key( $action ) . '_' . $user_id;

		if ( get_transient( $key ) ) {
			return false;
		}

		set_transient( $key, 1, max( 1, $seconds ) );

		return true;
	}

	private function verify_request(): void {
		if ( ! Session_Capabilities::is_manager_active( $this->get_settings() ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Session manager is disabled.', 'wppowersuite-lite' ),
					'code'    => 'disabled',
				]
			);
		}

		check_ajax_referer( 'wpps_login_session_manager', 'nonce' );
	}
}
