<?php
/**
 * Login Session Manager Module
 *
 * View active WordPress login sessions, force logout users, and limit concurrent sessions by role.
 *
 * @package LoginSessionManager
 */

namespace LoginSessionManager;

use LoginSessionManager\Includes\Session_Admin;
use LoginSessionManager\Includes\Session_Ajax;
use LoginSessionManager\Includes\Session_Limiter;
use LoginSessionManager\Includes\Session_Profile;
use LoginSessionManager\Includes\Session_Repository;
use WP_PowerSuite\Core\AbstractModule;

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

require_once __DIR__ . '/includes/class-session-repository.php';
require_once __DIR__ . '/includes/class-session-capabilities.php';
require_once __DIR__ . '/includes/class-session-audit-log.php';
require_once __DIR__ . '/includes/class-session-limiter.php';
require_once __DIR__ . '/includes/class-session-ajax.php';
require_once __DIR__ . '/includes/class-session-admin.php';
require_once __DIR__ . '/includes/class-session-profile.php';

class Module extends AbstractModule {

	private const MODULE_ID = 'login-session-manager';

	private Session_Repository $repository;

	/** @var array<string, mixed>|null */
	private ?array $settings_cache = null;

	public function __construct() {
		$this->repository = new Session_Repository();
	}

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Login Session Manager', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return '1.1.0';
	}

	public function getDescription(): string {
		return __( 'View active WordPress login sessions, force logout users, and limit concurrent sessions by role.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'admin', 'ajax', 'login', 'frontend' ];
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function getDefaultSettings(): array {
		return [
			'manager_enabled'       => true,
			'show_on_profile'       => true,
			'show_users_column'     => true,
			'show_admin_bar'        => true,
			'hide_ip'               => false,
			'hide_user_agent'       => false,
			'init_throttle_minutes' => 10,
			'role_max_sessions'     => [
				'administrator' => 0,
				'editor'        => 3,
				'author'        => 2,
				'contributor'   => 1,
				'subscriber'    => 1,
			],
			'excluded_roles'        => [],
			'limit_exceeded_action' => 'logout_oldest',
			'show_limit_notice'     => true,
			'timezone'              => 'site',
		];
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, mixed>
	 */
	public static function sanitizeSettings( array $settings ): array {
		$defaults = self::getDefaultSettings();

		$role_max = $defaults['role_max_sessions'];
		if ( isset( $settings['role_max_sessions'] ) && is_array( $settings['role_max_sessions'] ) ) {
			foreach ( $settings['role_max_sessions'] as $role => $max ) {
				$role_key = sanitize_key( (string) $role );
				if ( $role_key === '' ) {
					continue;
				}
				$role_max[ $role_key ] = max( 0, min( 50, absint( $max ) ) );
			}
		}

		$excluded = [];
		if ( isset( $settings['excluded_roles'] ) ) {
			if ( is_array( $settings['excluded_roles'] ) ) {
				foreach ( $settings['excluded_roles'] as $role ) {
					$role_key = sanitize_key( (string) $role );
					if ( $role_key !== '' ) {
						$excluded[] = $role_key;
					}
				}
			} elseif ( is_string( $settings['excluded_roles'] ) ) {
				$lines = preg_split( '/\r\n|\r|\n|,/', $settings['excluded_roles'] ) ?: [];
				foreach ( $lines as $line ) {
					$role_key = sanitize_key( trim( (string) $line ) );
					if ( $role_key !== '' ) {
						$excluded[] = $role_key;
					}
				}
			}
		}
		$action = isset( $settings['limit_exceeded_action'] ) ? sanitize_key( (string) $settings['limit_exceeded_action'] ) : 'logout_oldest';
		if ( ! in_array( $action, [ 'logout_oldest', 'block_login' ], true ) ) {
			$action = 'logout_oldest';
		}

		// A role cannot be both limited and excluded — drop exclusions when a positive cap is set.
		$excluded = array_values(
			array_filter(
				array_unique( $excluded ),
				static function ( string $role ) use ( $role_max ): bool {
					return ! isset( $role_max[ $role ] ) || (int) $role_max[ $role ] < 1;
				}
			)
		);

		$throttle = isset( $settings['init_throttle_minutes'] ) ? absint( $settings['init_throttle_minutes'] ) : 10;
		$throttle = max( 1, min( 60, $throttle ) );

		return [
			'manager_enabled'       => ! empty( $settings['manager_enabled'] ),
			'show_on_profile'       => ! empty( $settings['show_on_profile'] ),
			'show_users_column'     => ! empty( $settings['show_users_column'] ),
			'show_admin_bar'        => ! empty( $settings['show_admin_bar'] ),
			'hide_ip'               => ! empty( $settings['hide_ip'] ),
			'hide_user_agent'       => ! empty( $settings['hide_user_agent'] ),
			'init_throttle_minutes' => $throttle,
			'role_max_sessions'     => $role_max,
			'excluded_roles'        => array_values( array_unique( $excluded ) ),
			'limit_exceeded_action' => $action,
			'show_limit_notice'     => ! empty( $settings['show_limit_notice'] ),
			'timezone'              => 'site',
		];
	}

	/**
	 * @return array<string, mixed>
	 */
	public function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$stored = $this->moduleSettings()->all( self::getDefaultSettings() );
		$this->settings_cache = self::sanitizeSettings( is_array( $stored ) ? $stored : [] );

		return $this->settings_cache;
	}

	public static function cleanupOnDisable(): void {
		// No persistent module-owned data to purge; limit notices expire via transients.
	}

	private function isLicenseActive(): bool {
		return function_exists( 'wp_powersuite_is_license_valid' ) && wp_powersuite_is_license_valid();
	}

	protected function registerHooks(): void {
		if ( ! $this->isLicenseActive() ) {
			return;
		}

		$settings = $this->getSettings();

		$limiter = new Session_Limiter( $this->repository, fn (): array => $this->getSettings() );
		$limiter->register_hooks();

		$ajax = new Session_Ajax( $this->repository, fn (): array => $this->getSettings() );
		$ajax->register_hooks();

		$admin = new Session_Admin( $this->repository, $settings );
		$admin->register_hooks();

		$profile = new Session_Profile( $this->repository, $settings, $admin );
		$profile->register_hooks();

		add_action( 'login_init', [ $this, 'maybeShowBlockedNotice' ] );
	}

	public function maybeShowBlockedNotice(): void {
		if ( ! isset( $_GET['wpps_lsm_blocked'] ) || sanitize_key( wp_unslash( (string) $_GET['wpps_lsm_blocked'] ) ) !== '1' ) {
			return;
		}

		add_filter(
			'login_message',
			static function ( string $message ): string {
				$notice = '<p class="message">' . esc_html__(
					'This account has reached its maximum number of active sessions. Log out elsewhere or contact an administrator.',
					'wp-powersuite'
				) . '</p>';

				return $notice . $message;
			}
		);
	}
}
