<?php

/**
 * REST API endpoint for learner identification
 *
 * GET /wp-json/slidemind/v2/whoami             — cookie auth → { id, name } or { id: null }
 * GET /wp-json/slidemind/v2/whoami?email=x     — email resolution → { id, name } or { id: null }
 * GET /wp-json/slidemind/v2/whoami?identifier=x — universal resolution (SCORM, etc.)
 *
 * The ?email= mode serves as a backward-compatible xAPI bridge: the JS extracts
 * the email from the actor, calls /whoami?email=... to get the WordPress ID,
 * then uses the ID everywhere.
 *
 * The ?identifier= mode serves as a SCORM bridge: the generated code extracts
 * the SCORM student_id, calls /whoami?identifier=... to resolve
 * to a WordPress user.
 *
 * @package SlideMind\API
 * @since 2.2.0
 */
namespace SlideMind\API;

if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class WhoamiEndpoint {
    /**
     * API namespace
     * @var string
     */
    private $namespace = 'slidemind/v2';

    private const RATE_LIMIT_MAX = 30;

    private const RATE_LIMIT_TTL = 60;

    // seconds
    /**
     * Register REST routes
     */
    public function register_routes() {
        $args = [];
        // Public: called by e-learning modules in iframes for learner identification; rate-limited.
        register_rest_route( $this->namespace, '/whoami', [
            'methods'             => 'GET',
            'callback'            => [$this, 'handle'],
            'permission_callback' => '__return_true',
            'args'                => $args,
        ] );
    }

    /**
     * Handle whoami request.
     *
     * Free build: only cookie auth mode (returns current WP user or null).
     * Premium build: also supports ?email= and ?identifier= resolution
     * (xAPI / SCORM / LMS bridges).
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function handle( $request ) {
        $rate_check = $this->check_rate_limit();
        if ( is_wp_error( $rate_check ) ) {
            return $rate_check;
        }
        $data = [
            'id' => null,
        ];
        // Cookie mode — always available.
        if ( $data['id'] === null ) {
            $user_id = get_current_user_id();
            if ( $user_id > 0 ) {
                $user = get_userdata( $user_id );
                $data = [
                    'id'   => $user_id,
                    'name' => $user->display_name,
                ];
            }
        }
        $response = new \WP_REST_Response($data, 200);
        $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0' );
        $response->header( 'Pragma', 'no-cache' );
        return $response;
    }

    /**
     * Check rate limit by IP
     *
     * @return true|\WP_Error
     */
    private function check_rate_limit() {
        $ip = ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '0.0.0.0' );
        $key = 'slidemind_rl_whoami_' . md5( $ip );
        $current = (int) get_transient( $key );
        if ( $current >= self::RATE_LIMIT_MAX ) {
            return new \WP_Error('rate_limit', __( 'Too many requests. Please wait before trying again.', 'slidemind' ), [
                'status' => 429,
            ]);
        }
        set_transient( $key, $current + 1, self::RATE_LIMIT_TTL );
        return true;
    }

    /**
     * Resolve a SCORM/LMS identifier to a WordPress user
     *
     * Strategy is configurable via slidemind_settings['scorm_id_mapping']:
     * - auto  : cascade email → login → id (if numeric) → common usermeta
     * - email : get_user_by('email')
     * - login : get_user_by('login')
     * - id    : get_user_by('id')
     * - meta  : usermeta lookup on the key defined in scorm_id_meta_key
     *
     * @param string $identifier
     * @return \WP_User|false
     */
    private function resolve_identifier( $identifier ) {
        $settings = get_option( 'slidemind_settings', [] );
        $strategy = $settings['scorm_id_mapping'] ?? 'auto';
        switch ( $strategy ) {
            case 'email':
                return get_user_by( 'email', $identifier );
            case 'login':
                return get_user_by( 'login', $identifier );
            case 'id':
                return get_user_by( 'id', absint( $identifier ) );
            case 'meta':
                $meta_key = $settings['scorm_id_meta_key'] ?? '';
                if ( empty( $meta_key ) ) {
                    return false;
                }
                return $this->resolve_by_meta( $meta_key, $identifier );
            case 'auto':
            default:
                // Cascade: email → login → id → common usermeta
                if ( is_email( $identifier ) ) {
                    $user = get_user_by( 'email', $identifier );
                    if ( $user ) {
                        return $user;
                    }
                }
                $user = get_user_by( 'login', $identifier );
                if ( $user ) {
                    return $user;
                }
                if ( is_numeric( $identifier ) ) {
                    $user = get_user_by( 'id', absint( $identifier ) );
                    if ( $user ) {
                        return $user;
                    }
                }
                // Usermeta lookup on common keys
                $common_keys = [
                    'student_id',
                    'employee_id',
                    'learner_id',
                    'lms_user_id'
                ];
                foreach ( $common_keys as $key ) {
                    $user = $this->resolve_by_meta( $key, $identifier );
                    if ( $user ) {
                        return $user;
                    }
                }
                return false;
        }
    }

    /**
     * Resolve an identifier via usermeta
     *
     * @param string $meta_key
     * @param string $value
     * @return \WP_User|false
     */
    private function resolve_by_meta( $meta_key, $value ) {
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Single lookup by meta, not cacheable.
        $user_id = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", $meta_key, $value ) );
        if ( $user_id ) {
            return get_userdata( (int) $user_id );
        }
        return false;
    }

}
