<?php
/**
 * Device fingerprinting and recognition.
 *
 * @package ZA_Creative_Login_Shield
 */

defined( 'ABSPATH' ) || exit;

/**
 * Handles device fingerprinting for login session tracking and trusted devices.
 *
 * Device tracking is opt-in and disabled by default. When enabled, a
 * one-way hashed device identifier is stored to recognize returning
 * browsers without exposing raw browser data.
 */
class ZACLS_Device {

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->register_hooks();
	}

	/**
	 * Register WordPress hooks.
	 */
	private function register_hooks() {
		add_action( 'wp_login', array( $this, 'track_device_on_login' ), 10, 2 );
		add_action( 'zacls_daily_cleanup', array( $this, 'cleanup_old_devices' ) );
	}

	/**
	 * Whether device tracking is enabled.
	 *
	 * @return bool
	 */
	public static function is_enabled() {
		return 'yes' === get_option( 'zacls_device_tracking', 'no' );
	}

	/**
	 * Generate a device fingerprint from available browser data.
	 *
	 * Uses a one-way hash of non-identifying browser signals.
	 * No raw browser data is stored in the resulting fingerprint.
	 *
	 * @return string
	 */
	public static function generate_fingerprint() {
		$components = array();

		if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
			$components[] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
		}

		if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
			$components[] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) );
		}

		if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
			$components[] = sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_ENCODING'] ) );
		}

		$raw = implode( '|', $components );
		return 'd_' . substr( hash( 'sha256', $raw ), 0, 61 );
	}

	/**
	 * Get a consistent device ID.
	 *
	 * Returns empty string when device tracking is disabled.
	 *
	 * @return string
	 */
	public static function get_device_id() {
		if ( ! self::is_enabled() ) {
			return '';
		}

		if ( isset( $_COOKIE['zacls_device_id'] ) ) {
			return substr( sanitize_text_field( wp_unslash( $_COOKIE['zacls_device_id'] ) ), 0, 64 );
		}

		$device_id = self::generate_fingerprint();

		if ( ! headers_sent() ) {
			setcookie(
				'zacls_device_id',
				$device_id,
				time() + YEAR_IN_SECONDS,
				COOKIEPATH,
				COOKIE_DOMAIN,
				is_ssl(),
				true
			);
		}

		return $device_id;
	}

	/**
	 * Track device on user login.
	 *
	 * Only runs when device tracking is enabled in settings.
	 *
	 * @param string  $user_login Username.
	 * @param WP_User $user       User object.
	 */
	public function track_device_on_login( $user_login, $user ) {
		if ( ! self::is_enabled() ) {
			return;
		}

		global $wpdb;

		$device_id  = self::get_device_id();
		$ip         = ZACLS_Main::get_ip();
		$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] )
			? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) )
			: '';

		$existing = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}zacls_device_fingerprints WHERE device_id = %s",
				$device_id
			)
		);

		if ( $existing ) {
			$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->prefix . 'zacls_device_fingerprints',
				array(
					'last_seen'  => current_time( 'mysql' ),
					'ip'         => $ip,
					'user_agent' => $user_agent,
				),
				array( 'device_id' => $device_id ),
				array( '%s', '%s', '%s' ),
				array( '%s' )
			);
		} else {
			$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->prefix . 'zacls_device_fingerprints',
				array(
					'device_id'  => $device_id,
					'user_id'    => $user->ID,
					'ip'         => $ip,
					'user_agent' => $user_agent,
					'trusted'    => 0,
					'first_seen' => current_time( 'mysql' ),
					'last_seen'  => current_time( 'mysql' ),
				),
				array( '%s', '%d', '%s', '%s', '%d', '%s', '%s' )
			);
		}
	}

	/**
	 * Check if current device is recognized for a user.
	 *
	 * @param int $user_id User ID.
	 * @return bool
	 */
	public function is_device_recognized( $user_id ) {
		global $wpdb;

		$device_id = self::get_device_id();

		$count = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->prefix}zacls_device_fingerprints
				WHERE device_id = %s AND user_id = %d",
				$device_id,
				$user_id
			)
		);

		return $count > 0;
	}

	/**
	 * Check if current device is trusted for a user.
	 *
	 * @param int $user_id User ID.
	 * @return bool
	 */
	public function is_device_trusted( $user_id ) {
		global $wpdb;

		$device_id = self::get_device_id();

		$trusted = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT trusted FROM {$wpdb->prefix}zacls_device_fingerprints
				WHERE device_id = %s AND user_id = %d LIMIT 1",
				$device_id,
				$user_id
			)
		);

		return (bool) $trusted;
	}

	/**
	 * Mark current device as trusted.
	 *
	 * @param int $user_id User ID.
	 */
	public function trust_device( $user_id ) {
		global $wpdb;

		$device_id = self::get_device_id();

		$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prefix . 'zacls_device_fingerprints',
			array( 'trusted' => 1 ),
			array(
				'device_id' => $device_id,
				'user_id'   => $user_id,
			),
			array( '%d' ),
			array( '%s', '%d' )
		);
	}

	/**
	 * Remove trust from a device.
	 *
	 * @param string $device_id Device ID.
	 */
	public static function untrust_device( $device_id ) {
		global $wpdb;

		$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prefix . 'zacls_device_fingerprints',
			array( 'trusted' => 0 ),
			array( 'device_id' => $device_id ),
			array( '%d' ),
			array( '%s' )
		);
	}

	/**
	 * Get devices for a user.
	 *
	 * @param int $user_id User ID.
	 * @return array
	 */
	public static function get_user_devices( $user_id ) {
		global $wpdb;

		return $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}zacls_device_fingerprints
				WHERE user_id = %d ORDER BY last_seen DESC",
				$user_id
			),
			ARRAY_A
		);
	}

	/**
	 * Cleanup device records older than 90 days.
	 */
	public static function cleanup_old_devices() {
		global $wpdb;

		$cutoff = current_time( 'mysql' );

		$wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->prepare(
				"DELETE FROM {$wpdb->prefix}zacls_device_fingerprints WHERE last_seen < %s - INTERVAL 90 DAY",
				$cutoff
			)
		);
	}
}
