<?php
/**
 * JWT Service for Care API
 *
 * @package Care_API
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * JWT Service class
 * 
 * Handles JWT token generation and validation for API authentication
 */
class Care_API_JWT_Service {

    /**
     * JWT secret key
     *
     * @var string
     */
    private static $secret_key = null;

    /**
     * Token expiration time (24 hours in seconds)
     *
     * @var int
     */
    private static $expiration = 86400;

    /**
     * Initialize the service
     */
    public static function init() {
        self::$secret_key = self::get_secret_key();
    }

    /**
     * Get the secret key for JWT signing
     *
     * @return string
     */
    private static function get_secret_key() {
        $key = get_option( 'care_api_jwt_secret' );
        
        if ( empty( $key ) ) {
            // Generate a new secret key
            $key = wp_generate_password( 64, true, true );
            update_option( 'care_api_jwt_secret', $key );
        }
        
        return $key;
    }

    /**
     * Generate JWT token for a user
     *
     * @param int $user_id WordPress user ID
     * @return string|WP_Error JWT token or error
     */
    public function generate_token( $user_id ) {
        $user = get_user_by( 'id', $user_id );
        
        if ( ! $user ) {
            return new WP_Error( 'invalid_user', 'User not found', array( 'status' => 404 ) );
        }

        $issued_at = current_time( 'timestamp' );
        $expires_at = $issued_at + self::$expiration;

        $payload = array(
            'iss' => get_bloginfo( 'url' ),  // Issuer
            'aud' => get_bloginfo( 'url' ),  // Audience
            'iat' => $issued_at,             // Issued at
            'exp' => $expires_at,            // Expiration
            'user_id' => $user_id,
            'username' => $user->user_login,
            'user_email' => $user->user_email,
            'user_roles' => $user->roles,
        );

        return $this->encode_token( $payload );
    }

    /**
     * Validate JWT token
     *
     * @param string $token JWT token
     * @return array|WP_Error Decoded payload or error
     */
    public function validate_token( $token ) {
        try {
            $payload = $this->decode_token( $token );
            
            // Check if token has expired
            if ( isset( $payload['exp'] ) && $payload['exp'] < current_time( 'timestamp' ) ) {
                return new WP_Error( 'token_expired', 'Token has expired', array( 'status' => 401 ) );
            }
            
            // Verify user still exists
            $user = get_user_by( 'id', $payload['user_id'] );
            if ( ! $user ) {
                return new WP_Error( 'invalid_user', 'User no longer exists', array( 'status' => 401 ) );
            }
            
            return $payload;
            
        } catch ( Exception $e ) {
            return new WP_Error( 'invalid_token', 'Invalid token: ' . $e->getMessage(), array( 'status' => 401 ) );
        }
    }

    /**
     * Simple JWT encoding (without external library)
     * 
     * @param array $payload Token payload
     * @return string Encoded JWT token
     */
    private function encode_token( $payload ) {
        $header = json_encode( array( 'typ' => 'JWT', 'alg' => 'HS256' ) );
        $payload = json_encode( $payload );
        
        $header_encoded = $this->base64_url_encode( $header );
        $payload_encoded = $this->base64_url_encode( $payload );
        
        $signature = hash_hmac( 'sha256', $header_encoded . '.' . $payload_encoded, self::$secret_key, true );
        $signature_encoded = $this->base64_url_encode( $signature );
        
        return $header_encoded . '.' . $payload_encoded . '.' . $signature_encoded;
    }

    /**
     * Simple JWT decoding
     * 
     * @param string $token JWT token
     * @return array Decoded payload
     * @throws Exception If token is invalid
     */
    private function decode_token( $token ) {
        $parts = explode( '.', $token );
        
        if ( count( $parts ) !== 3 ) {
            throw new Exception( 'Invalid token structure' );
        }
        
        list( $header_encoded, $payload_encoded, $signature_encoded ) = $parts;
        
        // Verify signature
        $signature = $this->base64_url_decode( $signature_encoded );
        $expected_signature = hash_hmac( 'sha256', $header_encoded . '.' . $payload_encoded, self::$secret_key, true );
        
        if ( ! hash_equals( $signature, $expected_signature ) ) {
            throw new Exception( 'Invalid signature' );
        }
        
        $payload = json_decode( $this->base64_url_decode( $payload_encoded ), true );
        
        if ( json_last_error() !== JSON_ERROR_NONE ) {
            throw new Exception( 'Invalid JSON in payload' );
        }
        
        return $payload;
    }

    /**
     * Base64 URL-safe encode
     * 
     * @param string $data Data to encode
     * @return string Encoded data
     */
    private function base64_url_encode( $data ) {
        return rtrim( strtr( base64_encode( $data ), '+/', '-_' ), '=' );
    }

    /**
     * Base64 URL-safe decode
     * 
     * @param string $data Data to decode
     * @return string Decoded data
     */
    private function base64_url_decode( $data ) {
        return base64_decode( strtr( $data, '-_', '+/' ) . str_repeat( '=', 3 - ( 3 + strlen( $data ) ) % 4 ) );
    }

    /**
     * Extract token from Authorization header
     * 
     * @param string $authorization_header Authorization header value
     * @return string|null Token or null if not found
     */
    public static function extract_token_from_header( $authorization_header ) {
        if ( empty( $authorization_header ) ) {
            return null;
        }
        
        // Remove "Bearer " prefix
        if ( strpos( $authorization_header, 'Bearer ' ) === 0 ) {
            return substr( $authorization_header, 7 );
        }
        
        return $authorization_header;
    }

    /**
     * Get current user ID from JWT token in request
     * 
     * @return int|null User ID or null if not authenticated
     */
    public static function get_current_user_from_token() {
        $headers = getallheaders();
        $authorization = $headers['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? null;
        
        if ( empty( $authorization ) ) {
            return null;
        }
        
        $token = self::extract_token_from_header( $authorization );
        if ( empty( $token ) ) {
            return null;
        }
        
        $service = new self();
        $payload = $service->validate_token( $token );
        
        if ( is_wp_error( $payload ) ) {
            return null;
        }
        
        return $payload['user_id'] ?? null;
    }

    /**
     * Refresh a JWT token
     * 
     * @param string $token Current token
     * @return string|WP_Error New token or error
     */
    public function refresh_token( $token ) {
        $payload = $this->validate_token( $token );
        
        if ( is_wp_error( $payload ) ) {
            return $payload;
        }
        
        // Generate new token for the same user
        return $this->generate_token( $payload['user_id'] );
    }

    /**
     * Check if current request has valid JWT authentication
     * 
     * @return bool|WP_Error True if authenticated, WP_Error if not
     */
    public static function check_jwt_authentication() {
        $headers = getallheaders();
        $authorization = $headers['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? null;
        
        if ( empty( $authorization ) ) {
            return new WP_Error( 'missing_authorization', 'Authorization header is missing', array( 'status' => 401 ) );
        }
        
        $token = self::extract_token_from_header( $authorization );
        if ( empty( $token ) ) {
            return new WP_Error( 'invalid_authorization_format', 'Invalid authorization format', array( 'status' => 401 ) );
        }
        
        $service = new self();
        $payload = $service->validate_token( $token );
        
        if ( is_wp_error( $payload ) ) {
            return $payload;
        }
        
        // Set current user for WordPress
        wp_set_current_user( $payload['user_id'] );
        
        return true;
    }
}

// Initialize the service
Care_API_JWT_Service::init();