<?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' ) );
    }

    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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-by-volade' ) );
        }

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

        $user_id = (int) $wpdb->insert_id;
        if ( $user_id <= 0 ) {
            return new WP_Error( 'db_error', __( 'Impossible de créer le compte.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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.', 'php-compatibility-checker-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;
    }

    private function schedule_supabase_sync( $user_id, $pseudo, $email ) {
        $user_id = (int) $user_id;
        if ( $user_id <= 0 ) {
            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 );
    }
}
