<?php

namespace WPProofShield\Integration;

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


use WPProofShield\Security\RequestVerifier;
use WPProofShield\Security\RateLimiter;
use WPProofShield\Security\StatsStore;
use WPProofShield\Support\RequestInput;
use WPProofShield\Support\Settings;
use WPProofShield\Support\UiLanguage;

final class LoginGuard {

	public function __construct(
		private Settings $settings,
		private RequestVerifier $verifier,
		private RateLimiter $rate_limiter,
		private StatsStore $stats
	) {
	}

	public function register(): void {
		add_action( 'login_enqueue_scripts', array( $this, 'enqueueAssets' ) );
		add_action( 'login_form', array( $this, 'renderFields' ) );
		add_filter( 'authenticate', array( $this, 'authenticate' ), 99, 3 );
		add_action( 'wp_login', array( $this, 'onLoginSuccess' ), 10, 2 );
	}

	public function enqueueAssets(): void {
		if ( ! $this->settings->isEnabled( 'enabled_login' ) ) {
			return;
		}

		$pow_css_path = WPSH_DIR . 'assets/css/pow-client.css';
		$pow_js_path  = WPSH_DIR . 'assets/js/pow-client.js';
		$pow_worker_path = WPSH_DIR . 'assets/js/pow-worker.js';
		$pow_css_ver  = is_readable( $pow_css_path ) ? (string) filemtime( $pow_css_path ) : WPSH_VERSION;
		$pow_js_ver   = is_readable( $pow_js_path ) ? (string) filemtime( $pow_js_path ) : WPSH_VERSION;
		$pow_worker_ver = is_readable( $pow_worker_path ) ? (string) filemtime( $pow_worker_path ) : WPSH_VERSION;
		$pow_worker_url = (string) add_query_arg( 'ver', $pow_worker_ver, WPSH_URL . 'assets/js/pow-worker.js' );
		$lock_js_path = WPSH_DIR . 'assets/js/login-lock.js';
		$lock_js_ver  = is_readable( $lock_js_path ) ? (string) filemtime( $lock_js_path ) : WPSH_VERSION;

		if ( $this->isLoginLocked() ) {
			wp_enqueue_script( 'wpsh-login-lock', WPSH_URL . 'assets/js/login-lock.js', array(), $lock_js_ver, true );
			return;
		}

		wp_enqueue_style( 'wpsh-pow-client', WPSH_URL . 'assets/css/pow-client.css', array(), $pow_css_ver );
		wp_enqueue_script( 'wpsh-pow-client', WPSH_URL . 'assets/js/pow-client.js', array(), $pow_js_ver, true );
		$language = UiLanguage::current( $this->settings );
		wp_localize_script(
			'wpsh-pow-client',
			'WPSH_CONFIG',
			array(
				'restUrl'           => esc_url_raw( rest_url( 'wpshield/v1/challenge' ) ),
				'restNonce'         => wp_create_nonce( 'wp_rest' ),
				'workerUrl'         => esc_url_raw( $pow_worker_url ),
				'showDebugUi'       => $this->settings->isEnabled( 'logging_enabled' ) || ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
				'language'          => $language,
				'messages'          => UiLanguage::powMessages( $language ),
				'dynamicDomSupport' => false,
				'branding'          => array(
					'logoUrl' => esc_url_raw( $this->powLogoUrl() ),
				),
				'contexts'          => array(
					array(
						'ctx'      => 'login',
						'selector' => '#loginform',
					),
				),
			)
		);
	}

	public function renderFields(): void {
		if ( ! $this->settings->isEnabled( 'enabled_login' ) ) {
			return;
		}
		if ( $this->isLoginLocked() ) {
			return;
		}
		echo '<input type="hidden" name="wpsh_pow" value="" />';
		echo '<input type="hidden" name="wpsh_ctr" value="" />';
		echo '<input type="hidden" name="wpsh_hash" value="" />';
		$hp = $this->honeypotProfile( array( 'log', 'pwd', 'rememberme', 'wp-submit', 'redirect_to', 'testcookie' ) );
		echo '<noscript><p class="message">' . esc_html__( 'ProofShield requires JavaScript for login verification. Please enable JavaScript and try again.', 'proofshield' ) . '</p></noscript>';
		echo '<input type="text" name="' . esc_attr( $hp['name'] ) . '" value="" autocomplete="new-password" autocapitalize="none" spellcheck="false" inputmode="none" aria-label="' . esc_attr( $hp['label'] ) . '" aria-hidden="true" placeholder="' . esc_attr( $hp['placeholder'] ) . '" data-required="1" style="position:absolute;left:-9999px;opacity:0;" tabindex="-1" />';
	}
	public function authenticate( mixed $user, string $username, string $password ): mixed {

		if ( ! $this->settings->isEnabled( 'enabled_login' ) ) {
			return $user;
		}
		if ( ! $this->isCredentialLoginSubmit() ) {
			return $user;
		}

		// If credentials are posted but ProofShield fields are missing, treat as explicit no-JS login attempt.
		if ( $this->isMissingProofFields() ) {
			$this->stats->record( 'login:no_js', 'wpsh_js_required', 0, null );
			$this->logSecurityError(
				'login:no_js_blocked',
				array(
					'reason' => 'wpsh_js_required',
					'user'   => $username !== '' ? $username : '(empty)',
					'ip'     => $this->remoteIp(),
				)
			);
			return new \WP_Error( 'wpsh_js_required', __( 'ProofShield requires JavaScript for login verification. Please enable JavaScript and try again.', 'proofshield' ) );
		}

		$result = $this->verifier->verify( 'login', true );
		if ( is_wp_error( $result ) ) {
			$this->logSecurityError(
				'login:verify_failed',
				array(
					'reason' => $result->get_error_code(),
					'user'   => $username !== '' ? $username : '(empty)',
					'ip'     => $this->remoteIp(),
				)
			);
			return $result;
		}
		if ( is_wp_error( $user ) ) {
			if ( $this->shouldCountLoginFailure( $user ) ) {
				$this->rate_limiter->recordLoginCredentialFailure( $this->remoteIp() );
			}
			if ( $this->rate_limiter->inCooldown( 'login', $this->remoteIp() ) ) {
				return new \WP_Error( 'wpsh_rate_limited', __( 'Too many requests. Please try again later.', 'proofshield' ) );
			}
			return $user;
		}

		return $user;
	}

	public function onLoginSuccess( string $user_login, \WP_User $user ): void {
		if ( ! $this->settings->isEnabled( 'enabled_login' ) ) {
			return;
		}
		$this->rate_limiter->recordSuccess( 'login', $this->remoteIp() );
	}

	private function remoteIp(): string {
		$candidate     = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) ) : '';
		$host          = (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST );
		$is_local_host = in_array( $host, array( 'localhost', '127.0.0.1', '::1' ), true );
		if ( $is_local_host ) {
			$sim = isset( $_SERVER['HTTP_X_WPSH_SIM_IP'] ) ? trim( sanitize_text_field( wp_unslash( (string) $_SERVER['HTTP_X_WPSH_SIM_IP'] ) ) ) : '';
			if ( filter_var( $sim, FILTER_VALIDATE_IP ) ) {
				$candidate = $sim;
			}
		}
		return filter_var( $candidate, FILTER_VALIDATE_IP ) ? $candidate : '0.0.0.0';
	}

	private function isLoginLocked(): bool {
		return $this->rate_limiter->inCooldown( 'login', $this->remoteIp() );
	}

	private function shouldCountLoginFailure( \WP_Error $error ): bool {
		$codes = array_map( 'strval', $error->get_error_codes() );

		foreach ( $codes as $code ) {
			if ( in_array( $code, array( 'invalid_username', 'incorrect_password' ), true ) ) {
				return true;
			}
		}

		return false;
	}

	private function isCredentialLoginSubmit(): bool {
		$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( (string) $_SERVER['REQUEST_METHOD'] ) ) ) : 'GET';
		if ( $method !== 'POST' ) {
			return false;
		}

		$user = $this->postText( 'log' );
		$pass = $this->postText( 'pwd' );

		return $user !== '' && $pass !== '';
	}

	private function isMissingProofFields(): bool {

		$pow  = $this->postText( 'wpsh_pow' );
		$ctr  = $this->postText( 'wpsh_ctr' );
		$hash = $this->postText( 'wpsh_hash' );
		return $pow === '' || $ctr === '' || $hash === '';
	}

	private function postText( string $key ): string {
		return RequestInput::postText( $key );
	}
	private function powLogoUrl(): string {

		foreach ( array( 'assets/images/proofshield-free.png', 'assets/images/logo-free.png', 'assets/images/logo.png', 'assets/images/proofshield-mark.png' ) as $relative ) {
			$absolute_path = WPSH_DIR . $relative;
			if ( is_readable( $absolute_path ) ) {
				$version = (string) filemtime( $absolute_path );
				return (string) add_query_arg( 'ver', $version, WPSH_URL . $relative );
			}
		}

		return '';
	}

	/**
	 * @param array<int,string> $reserved
	 * @return array{name:string,label:string,placeholder:string}
	 */
	private function honeypotProfile( array $reserved = array() ): array {

		$profiles    = array(
			array(
				'name'        => 'address_line2',
				'label'       => 'Address line 2',
				'placeholder' => 'Address line 2 *',
			),
			array(
				'name'        => 'contact_fax',
				'label'       => 'Fax number',
				'placeholder' => 'Fax number *',
			),
			array(
				'name'        => 'company_website',
				'label'       => 'Company website',
				'placeholder' => 'Company website *',
			),
			array(
				'name'        => 'middle_name',
				'label'       => 'Middle name',
				'placeholder' => 'Middle name *',
			),
			array(
				'name'        => 'secondary_phone',
				'label'       => 'Secondary phone',
				'placeholder' => 'Secondary phone *',
			),
			array(
				'name'        => 'vat_number',
				'label'       => 'VAT number',
				'placeholder' => 'VAT number *',
			),
			array(
				'name'        => 'department_code',
				'label'       => 'Department code',
				'placeholder' => 'Department code *',
			),
			array(
				'name'        => 'office_extension',
				'label'       => 'Office extension',
				'placeholder' => 'Office extension *',
			),
		);
		$reservedMap = array_fill_keys( array_map( 'strtolower', $reserved ), true );
		foreach ( $profiles as $profile ) {
			if ( ! isset( $reservedMap[ strtolower( (string) $profile['name'] ) ] ) ) {
				return $profile;
			}
		}

		return $profiles[0];
	}

	/**
	 * @param array<string,scalar|null> $context
	 */
	private function logSecurityError( string $event, array $context = array() ): void {

		/**
		 * Allow optional custom logging/instrumentation without shipping direct PHP error_log() calls.
		 *
		 * @param string                    $event   Event name.
		 * @param array<string,scalar|null> $context Event context.
		 */
		do_action( 'wpsh_security_error', $event, $context, 'LoginGuard' );
	}
}
