<?php
/**
 * Volade Auth — Inscription, connexion, JWT, sync Supabase
 *
 * @package Volade\SDK
 * @license GPL-2.0-or-later
 */

trait Volade_Auth {

	private $auth_errors = array();
	/** @var string Token émis lors du dernier set_session (évite double génération). */
	private $session_token_issued = '';
	/** @var array<int, array|null> Cache requête get_user_data */
	private static $user_data_cache = array();

	public function init_auth() {
		add_action( 'wp_ajax_volade_register', array( $this, 'ajax_register' ) );
		add_action( 'wp_ajax_nopriv_volade_register', array( $this, 'ajax_register' ) );
		add_action( 'wp_ajax_volade_login', array( $this, 'ajax_login' ) );
		add_action( 'wp_ajax_nopriv_volade_login', array( $this, 'ajax_login' ) );
		add_action( 'wp_ajax_volade_logout', array( $this, 'ajax_logout' ) );
		add_action( 'wp_ajax_nopriv_volade_logout', array( $this, 'ajax_logout' ) );
		add_action( 'volade_sync_user_supabase', array( $this, 'cron_sync_user_to_supabase' ), 10, 3 );
	}

	private function clean_ajax_output_buffer() {
		if ( ob_get_length() ) {
			ob_clean();
		}
	}

	private function send_auth_json_success( $message, $redirect ) {
		$this->clean_ajax_output_buffer();
		wp_send_json_success(
			array(
				'message'  => $message,
				'redirect' => $redirect,
			)
		);
	}

	private function send_auth_json_error( $message ) {
		$this->clean_ajax_output_buffer();
		wp_send_json_error( $message );
	}

	/**
	 * Limite les tentatives d'inscription / connexion (5 par IP / 15 min).
	 *
	 * @return true|WP_Error
	 */
	private function check_auth_rate_limit() {
		$ip = isset( $_SERVER['REMOTE_ADDR'] )
			? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) )
			: '';

		$rate_key = 'volade_rate_auth_' . md5( $ip );
		$attempts = (int) get_transient( $rate_key );

		if ( $attempts > 5 ) {
			return new WP_Error(
				'rate_limited',
				__( 'Trop de tentatives. Réessayez dans quelques minutes.', 'core-version-switcher-by-volade' )
			);
		}

		set_transient( $rate_key, $attempts + 1, 15 * MINUTE_IN_SECONDS );
		return true;
	}

	public function ajax_register() {
		$this->maybe_install_db();

		if ( ! $this->volade_users_table_exists() ) {
			$this->send_auth_json_error( __( 'Tables Volade manquantes. Désactivez puis réactivez le plugin Heartbeat Manager.', 'core-version-switcher-by-volade' ) );
		}

		check_ajax_referer( 'volade_nonce', 'nonce' );

		$rate = $this->check_auth_rate_limit();
		if ( is_wp_error( $rate ) ) {
			$this->send_auth_json_error( $rate->get_error_message() );
		}

		$pseudo   = sanitize_user( wp_unslash( $_POST['pseudo'] ?? '' ), true );
		$email    = sanitize_email( wp_unslash( $_POST['email'] ?? '' ) );
		$password = wp_unslash( $_POST['password'] ?? '' );

		$result = $this->register_user( $pseudo, $email, $password );
		if ( is_wp_error( $result ) ) {
			$this->send_auth_json_error( $result->get_error_message() );
		}
		$this->send_auth_json_success(
			__( 'Compte créé avec succès.', 'core-version-switcher-by-volade' ),
			admin_url( 'admin.php?page=volade&volade_welcome=1' )
		);
	}

	public function ajax_login() {
		$this->maybe_install_db();

		if ( ! $this->volade_users_table_exists() ) {
			$this->send_auth_json_error( __( 'Tables Volade manquantes. Désactivez puis réactivez le plugin Heartbeat Manager.', 'core-version-switcher-by-volade' ) );
		}

		check_ajax_referer( 'volade_nonce', 'nonce' );

		$rate = $this->check_auth_rate_limit();
		if ( is_wp_error( $rate ) ) {
			$this->send_auth_json_error( $rate->get_error_message() );
		}

		$login    = sanitize_text_field( wp_unslash( $_POST['login'] ?? '' ) );
		$password = wp_unslash( $_POST['password'] ?? '' );

		$result = $this->login_user( $login, $password );
		if ( is_wp_error( $result ) ) {
			$this->send_auth_json_error( $result->get_error_message() );
		}
		$this->send_auth_json_success(
			__( 'Connexion réussie.', 'core-version-switcher-by-volade' ),
			admin_url( 'admin.php?page=volade' )
		);
	}

	public function ajax_logout() {
		check_ajax_referer( 'volade_nonce', 'nonce' );
		$this->destroy_session();
		$this->send_auth_json_success(
			__( 'Déconnexion réussie.', 'core-version-switcher-by-volade' ),
			admin_url( 'admin.php?page=volade-auth' )
		);
	}

	public function register_user( $pseudo, $email, $password ) {
		global $wpdb;

		$this->maybe_install_db();

		if ( ! $this->volade_users_table_exists() ) {
			return new WP_Error( 'db_missing', __( 'Tables Volade manquantes. Réactivez le plugin.', 'core-version-switcher-by-volade' ) );
		}

		if ( empty( $pseudo ) || empty( $email ) || empty( $password ) ) {
			return new WP_Error( 'missing_fields', __( 'Tous les champs sont requis.', 'core-version-switcher-by-volade' ) );
		}
		if ( strlen( $pseudo ) < 3 || strlen( $pseudo ) > 50 ) {
			return new WP_Error( 'invalid_pseudo', __( 'Le pseudo doit faire 3 à 50 caractères.', 'core-version-switcher-by-volade' ) );
		}
		if ( ! is_email( $email ) ) {
			return new WP_Error( 'invalid_email', __( 'Adresse e-mail invalide.', 'core-version-switcher-by-volade' ) );
		}
		if ( strlen( $password ) < 8 ) {
			return new WP_Error( 'weak_password', __( 'Le mot de passe doit faire au moins 8 caractères.', 'core-version-switcher-by-volade' ) );
		}

		$exists = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}volade_users WHERE pseudo = %s OR email = %s LIMIT 1",
				$pseudo,
				$email
			)
		);
		if ( $exists ) {
			return new WP_Error( 'user_exists', __( 'Ce pseudo ou cet e-mail est déjà utilisé.', 'core-version-switcher-by-volade' ) );
		}

		$referral_code = $this->generate_referral_code();
		$hash          = wp_hash_password( $password );

		$inserted = $wpdb->insert(
			"{$wpdb->prefix}volade_users",
			array(
				'pseudo'        => $pseudo,
				'email'         => $email,
				'password_hash' => $hash,
				'display_name'  => $pseudo,
				'referral_code' => $referral_code,
				'created_at'    => current_time( 'mysql' ),
				'updated_at'    => current_time( 'mysql' ),
			)
		);

		if ( ! $inserted ) {
			return new WP_Error(
				'db_error',
				__( 'Impossible de créer le compte. Vérifiez que les tables Volade existent (réactivez le plugin).', 'core-version-switcher-by-volade' )
			);
		}

		$user_id = (int) $wpdb->insert_id;
		if ( $user_id <= 0 ) {
			return new WP_Error( 'db_error', __( 'Impossible de créer le compte.', 'core-version-switcher-by-volade' ) );
		}

		$this->set_session( $user_id );
		$this->convert_referral( $user_id );
		$this->schedule_supabase_sync( $user_id, $pseudo, $email );
		$this->add_xp( $user_id, 'register' );
		$this->sync_user_level( $user_id );
		$this->clear_user_data_cache( $user_id );

		return $this->get_user_data( $user_id );
	}

	public function login_user( $login, $password ) {
		global $wpdb;

		if ( empty( $login ) || empty( $password ) ) {
			return new WP_Error( 'missing_fields', __( 'Tous les champs sont requis.', 'core-version-switcher-by-volade' ) );
		}

		$user = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}volade_users WHERE pseudo = %s OR email = %s LIMIT 1",
				$login,
				$login
			)
		);

		if ( ! $user || ! wp_check_password( $password, $user->password_hash ) ) {
			return new WP_Error( 'invalid_credentials', __( 'Pseudo/e-mail ou mot de passe invalide.', 'core-version-switcher-by-volade' ) );
		}

		$this->set_session( $user->id );
		$this->record_activity( $user->id );

		if ( empty( $user->supabase_uid ) ) {
			$this->schedule_supabase_sync( $user->id, $user->pseudo, $user->email );
			self::$user_data_cache = array();
		}

		$this->sync_user_level( $user->id );
		$this->clear_user_data_cache( $user->id );

		return $this->get_user_data( $user->id );
	}

	public function is_user_logged_in() {
		$user_id = $this->get_session_user_id();
		return $user_id > 0;
	}

	public function get_current_user() {
		$user_id = $this->get_session_user_id();
		if ( ! $user_id ) {
			return null;
		}
		return $this->get_user_data( $user_id );
	}

	public function get_user_data( $user_id ) {
		if ( isset( self::$user_data_cache[ $user_id ] ) ) {
			$cached = self::$user_data_cache[ $user_id ];
			if ( $cached && ! empty( $cached['badges'] ) ) {
				$cached['badges'] = array_map( array( $this, 'enrich_badge' ), $cached['badges'] );
			}
			return $cached;
		}

		global $wpdb;
		$user = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT id, pseudo, email, display_name, supabase_uid, xp, level, referral_code, streak, created_at
             FROM {$wpdb->prefix}volade_users WHERE id = %d",
				$user_id
			),
			ARRAY_A
		);
		if ( $user ) {
			$this->sync_user_level( $user_id );
			$user           = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT id, pseudo, email, display_name, supabase_uid, xp, level, referral_code, streak, created_at
                 FROM {$wpdb->prefix}volade_users WHERE id = %d",
					$user_id
				),
				ARRAY_A
			);
			$user['badges'] = $this->get_user_badges( $user_id );
		}
		self::$user_data_cache[ $user_id ] = $user ?: null;
		return $user;
	}

	public function get_session_token_issued() {
		return $this->session_token_issued;
	}

	public function clear_user_data_cache( $user_id = 0 ) {
		if ( $user_id ) {
			unset( self::$user_data_cache[ $user_id ] );
			return;
		}
		self::$user_data_cache = array();
	}

	private function set_session( $user_id ) {
		$this->session_token_issued = $this->generate_token( $user_id );
		$secure                     = is_ssl();
		$httponly                   = true;
		$expires                    = time() + YEAR_IN_SECONDS;
		setcookie( 'volade_token', $this->session_token_issued, $expires, COOKIEPATH, COOKIE_DOMAIN, $secure, $httponly );
		if ( COOKIEPATH !== SITECOOKIEPATH ) {
			setcookie( 'volade_token', $this->session_token_issued, $expires, SITECOOKIEPATH, COOKIE_DOMAIN, $secure, $httponly );
		}
		$_COOKIE['volade_token'] = $this->session_token_issued;
		return $this->session_token_issued;
	}

	private function destroy_session() {
		if ( ! empty( $_COOKIE['volade_token'] ) ) {
			$this->revoke_token( $_COOKIE['volade_token'] );
		}
		setcookie( 'volade_token', '', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
	}

	private function get_session_user_id() {
		if ( ! empty( $_COOKIE['volade_token'] ) ) {
			return $this->validate_token( $_COOKIE['volade_token'] );
		}
		$auth_header = $this->get_auth_header();
		if ( $auth_header ) {
			return $this->validate_token( $auth_header );
		}
		return 0;
	}

	private function get_auth_header() {
		if ( ! empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
			$parts = explode( ' ', $_SERVER['HTTP_AUTHORIZATION'] );
			if ( count( $parts ) === 2 && $parts[0] === 'Bearer' ) {
				return $parts[1];
			}
		}
		if ( function_exists( 'apache_request_headers' ) ) {
			$headers = apache_request_headers();
			if ( ! empty( $headers['Authorization'] ) ) {
				$parts = explode( ' ', $headers['Authorization'] );
				if ( count( $parts ) === 2 && $parts[0] === 'Bearer' ) {
					return $parts[1];
				}
			}
		}
		return '';
	}

	private function generate_token( $user_id ) {
		global $wpdb;
		$wpdb->delete( "{$wpdb->prefix}volade_tokens", array( 'user_id' => $user_id ), array( '%d' ) );
		$token   = bin2hex( random_bytes( 32 ) );
		$expires = gmdate( 'Y-m-d H:i:s', time() + YEAR_IN_SECONDS );
		$wpdb->insert(
			"{$wpdb->prefix}volade_tokens",
			array(
				'user_id'    => $user_id,
				'token'      => $token,
				'expires_at' => $expires,
			)
		);
		return $token;
	}

	private function revoke_token( $token ) {
		global $wpdb;
		$wpdb->delete(
			"{$wpdb->prefix}volade_tokens",
			array( 'token' => $token ),
			array( '%s' )
		);
	}

	private function validate_token( $token ) {
		global $wpdb;
		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT user_id FROM {$wpdb->prefix}volade_tokens WHERE token = %s AND expires_at > %s LIMIT 1",
				$token,
				gmdate( 'Y-m-d H:i:s' )
			)
		);
		return $row ? (int) $row->user_id : 0;
	}

	public function generate_referral_code() {
		return substr( bin2hex( random_bytes( 8 ) ), 0, 10 );
	}

	private function update_last_activity( $user_id ) {
		global $wpdb;
		$wpdb->update(
			"{$wpdb->prefix}volade_users",
			array(
				'last_activity' => current_time( 'mysql' ),
				'updated_at'    => current_time( 'mysql' ),
			),
			array( 'id' => $user_id )
		);
	}

	private function sync_user_to_supabase( $user_id, $pseudo, $email, $password_hash ) {
		$api = Volade_API::get_instance();
		if ( ! $api ) {
			return;
		}

		add_filter( 'http_request_timeout', array( $this, 'filter_short_api_timeout' ), 9999 );
		$response = $api->post(
			'/auth/sync',
			array(
				'local_id' => $user_id,
				'pseudo'   => $pseudo,
				'email'    => $email,
			)
		);
		remove_filter( 'http_request_timeout', array( $this, 'filter_short_api_timeout' ), 9999 );

		if ( ! is_wp_error( $response ) && ! empty( $response['uid'] ) ) {
			global $wpdb;
			$wpdb->update(
				"{$wpdb->prefix}volade_users",
				array( 'supabase_uid' => $response['uid'] ),
				array( 'id' => $user_id )
			);
		}
	}

	public function filter_short_api_timeout() {
		return 3;
	}

	public function cron_sync_user_to_supabase( $user_id, $pseudo, $email ) {
		$this->sync_user_to_supabase( (int) $user_id, $pseudo, $email, '' );
	}

	private function schedule_supabase_sync( $user_id, $pseudo, $email ) {
		$user_id = (int) $user_id;
		if ( $user_id <= 0 ) {
			return;
		}

		$args = array( $user_id, $pseudo, $email );

		// Évite d'appeler l'API pendant admin-ajax (réponse JSON corrompue si sortie parasite).
		if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
			if ( ! wp_next_scheduled( 'volade_sync_user_supabase', $args ) ) {
				wp_schedule_single_event( time() + 10, 'volade_sync_user_supabase', $args );
			}
			return;
		}

		add_action(
			'shutdown',
			function () use ( $user_id, $pseudo, $email ) {
				$this->sync_user_to_supabase( $user_id, $pseudo, $email, '' );
			},
			20
		);
	}

	private function volade_users_table_exists() {
		global $wpdb;
		$table = $wpdb->prefix . 'volade_users';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table;
	}

	public function rest_register( $request ) {
		$rate = $this->check_auth_rate_limit();
		if ( is_wp_error( $rate ) ) {
			return new WP_REST_Response( array( 'error' => $rate->get_error_message() ), 429 );
		}

		$params = $request->get_params();
		$pseudo = sanitize_user( $params['pseudo'] ?? '' );
		$email  = sanitize_email( $params['email'] ?? '' );
		$result = $this->register_user( $pseudo, $email, $params['password'] ?? '' );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response( array( 'error' => $result->get_error_message() ), 400 );
		}
		return new WP_REST_Response(
			array(
				'user'  => $result,
				'token' => $this->get_session_token_issued(),
			),
			201
		);
	}

	public function rest_login( $request ) {
		$rate = $this->check_auth_rate_limit();
		if ( is_wp_error( $rate ) ) {
			return new WP_REST_Response( array( 'error' => $rate->get_error_message() ), 429 );
		}

		$params = $request->get_params();
		$login  = sanitize_text_field( $params['login'] ?? '' );
		$result = $this->login_user( $login, $params['password'] ?? '' );

		if ( is_wp_error( $result ) ) {
			return new WP_REST_Response( array( 'error' => $result->get_error_message() ), 401 );
		}
		return new WP_REST_Response(
			array(
				'user'  => $result,
				'token' => $this->get_session_token_issued(),
			),
			200
		);
	}

	public function rest_me() {
		$user = $this->get_current_user();
		if ( ! $user ) {
			return new WP_REST_Response( array( 'error' => 'Not authenticated' ), 401 );
		}
		return new WP_REST_Response( $user, 200 );
	}
}
