<?php
/**
 * Fingerprint_Service — resolves and persists fingerprint identities.
 *
 * Uses the existing wp_trustlens_fingerprints table with sentinel values
 * (email_hash='', fingerprint_type='tl_device_session') to maintain one
 * row per unique device hash for card-testing defense, separate from
 * linked-accounts rows which use real email_hash + type='canvas' etc.
 *
 * @package TrustLens
 * @since   1.2.2
 */

defined( 'ABSPATH' ) || exit;

class TrustLens_Fingerprint_Service {

	private const FP_LEN   = 22;
	public const FP_TYPE  = 'tl_device_session';
	public const FP_OWNER = '';

	public function resolve_from_rest_request(
		WP_REST_Request $request,
		string $ip,
		string $user_agent,
		string $accept_language
	): array {
		$header      = sanitize_text_field( (string) $request->get_header( 'X-TL-FP' ) );
		$server_hash = $this->server_fallback_hash( $ip, $user_agent, $accept_language, '' );

		if ( $this->looks_like_valid_fingerprint( $header ) ) {
			return array(
				'hash'        => $header,
				'source'      => 'browser',
				'server_hash' => $server_hash,
			);
		}

		return array(
			'hash'        => $server_hash,
			'source'      => 'server',
			'server_hash' => $server_hash,
		);
	}

	public function resolve_from_classic_post( array $post_data, string $ip, string $user_agent, string $accept_language ): array {
		$fp          = isset( $post_data['_tl_fp'] ) ? sanitize_text_field( (string) $post_data['_tl_fp'] ) : '';
		$server_hash = $this->server_fallback_hash( $ip, $user_agent, $accept_language, '' );

		if ( $this->looks_like_valid_fingerprint( $fp ) ) {
			return array(
				'hash'        => $fp,
				'source'      => 'browser',
				'server_hash' => $server_hash,
			);
		}

		return array(
			'hash'        => $server_hash,
			'source'      => 'server',
			'server_hash' => $server_hash,
		);
	}

	public function server_fallback_hash( string $ip, string $user_agent, string $accept_language, string $accept_encoding ): string {
		$raw = $ip . '|' . $user_agent . '|' . $accept_language . '|' . $accept_encoding;
		$bin = substr( hash( 'sha256', $raw, true ), 0, 16 );
		return rtrim( strtr( base64_encode( $bin ), '+/', '-_' ), '=' );
	}

	public function record( string $hash, string $source ): void {
		global $wpdb;
		$table = $wpdb->prefix . 'trustlens_fingerprints';
		$now   = current_time( 'mysql', true );

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe; fingerprint counters are real-time, no caching.
		// Single UPDATE with inline increment — one round-trip when the row exists.
		$updated = $wpdb->query(
			$wpdb->prepare(
				"UPDATE {$table}
				 SET last_seen = %s, times_seen = times_seen + 1
				 WHERE email_hash = %s AND fingerprint_type = %s AND fingerprint_hash = %s",
				$now,
				self::FP_OWNER,
				self::FP_TYPE,
				$hash
			)
		);

		if ( $updated > 0 ) {
			return;
		}

		$wpdb->insert(
			$table,
			array(
				'email_hash'       => self::FP_OWNER,
				'fingerprint_type' => self::FP_TYPE,
				'fingerprint_hash' => $hash,
				'first_seen'       => $now,
				'last_seen'        => $now,
				'times_seen'       => 1,
				'fp_source'        => $source,
			),
			array( '%s', '%s', '%s', '%s', '%s', '%d', '%s' )
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
	}

	public function lookup( string $hash ): ?object {
		global $wpdb;
		$table = $wpdb->prefix . 'trustlens_fingerprints';
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix, safe; fingerprint lookups are real-time, no caching.
		$row   = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$table} WHERE email_hash = %s AND fingerprint_type = %s AND fingerprint_hash = %s",
				self::FP_OWNER,
				self::FP_TYPE,
				$hash
			)
		);
		// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
		return $row ?: null;
	}

	private function looks_like_valid_fingerprint( string $s ): bool {
		return strlen( $s ) === self::FP_LEN && 1 === preg_match( '/^[A-Za-z0-9_\-]+$/', $s );
	}
}
