<?php
/**
 * Export Destination Service - Handles uploads to various destinations.
 *
 * @package PrimeLink
 */

namespace PrimeLink\Modules\Exports\Service;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

final class ExportDestinationService {

    /**
     * Upload file to destination.
     *
     * @param string $file_path   Local file path.
     * @param string $destination Destination type (ftp, dropbox, gdrive, onedrive).
     * @param array  $options     Destination-specific options.
     * @return array Result.
     */
    public static function upload( $file_path, $destination, $options = array() ) {
        if ( ! \PrimeLink\Helpers\FilesystemHelper::file_exists( $file_path ) ) {
            return array(
                'success' => false,
                'error'   => 'File not found.',
            );
        }

        $settings = get_option( 'mt_primelink_settings', array() );

        switch ( $destination ) {
            case 'ftp':
                return self::upload_to_ftp( $file_path, $settings, $options );
            case 'dropbox':
                return self::upload_to_dropbox( $file_path, $settings, $options );
            case 'gdrive':
                return self::upload_to_google_drive( $file_path, $settings, $options );
            case 'onedrive':
                return self::upload_to_onedrive( $file_path, $settings, $options );
            default:
                return array(
                    'success' => false,
                    'error'   => 'Unknown destination: ' . $destination,
                );
        }
    }

    /**
     * Upload to FTP/SFTP.
     *
     * @param string $file_path File path.
     * @param array  $settings Settings.
     * @param array  $options  Options.
     * @return array
     */
    private static function upload_to_ftp( $file_path, $settings, $options ) {
        // Ensure PHP FTP extension is available before using ftp_* functions.
        if ( ! function_exists( 'ftp_connect' ) && ! function_exists( 'ftp_ssl_connect' ) ) {
            return array(
                'success' => false,
                'error'   => 'FTP functions are not available on this server. Please enable the PHP FTP extension (extension=ftp) or use SFTP instead.',
            );
        }

        $protocol = $options['protocol'] ?? ( $settings['ftp_protocol'] ?? 'ftp' );
        $host     = $options['host'] ?? ( $settings['ftp_host'] ?? '' );
        $port     = isset( $options['port'] ) ? absint( $options['port'] ) : ( isset( $settings['ftp_port'] ) ? absint( $settings['ftp_port'] ) : 0 );
        $user     = $options['user'] ?? ( $settings['ftp_user'] ?? '' );
        $pass     = $options['pass'] ?? ( $settings['ftp_pass'] ?? '' );
        $root     = $options['root'] ?? ( $settings['ftp_root'] ?? '' );
        $remote_path = $options['path'] ?? ( basename( $file_path ) );
        $passive  = isset( $options['passive'] ) ? (bool) $options['passive'] : ! empty( $settings['ftp_passive'] );

        if ( ! $host || ! $user || ! $pass ) {
            return array(
                'success' => false,
                'error'   => 'Missing FTP credentials.',
            );
        }

        $remote_file_path = self::normalize_ftp_path( $root, $remote_path );

        if ( 'sftp' === strtolower( $protocol ) ) {
            return self::upload_to_sftp( $host, $port ?: 22, $user, $pass, $file_path, $remote_file_path );
        }

        // FTP / FTPS
        $port = $port ?: ( 'ftps' === strtolower( $protocol ) ? 990 : 21 );
        $conn = ( 'ftps' === strtolower( $protocol ) ) ? @ftp_ssl_connect( $host, $port, 20 ) : @ftp_connect( $host, $port, 20 );
        
        if ( ! $conn ) {
            return array(
                'success' => false,
                'error'   => 'FTP connection failed.',
            );
        }

        if ( ! @ftp_login( $conn, $user, $pass ) ) {
            @ftp_close( $conn );
            return array(
                'success' => false,
                'error'   => 'FTP login failed.',
            );
        }

        if ( $passive ) {
            @ftp_pasv( $conn, true );
        }

        $ok = @ftp_put( $conn, $remote_file_path, $file_path, FTP_BINARY );
        @ftp_close( $conn );

        if ( ! $ok ) {
            return array(
                'success' => false,
                'error'   => 'FTP upload failed. Check permissions and path.',
            );
        }

        return array(
            'success' => true,
            'message' => 'File uploaded to FTP successfully.',
            'path'    => $remote_file_path,
        );
    }

    /**
     * Upload to SFTP.
     *
     * @param string $host         Host.
     * @param int    $port         Port.
     * @param string $user         Username.
     * @param string $pass         Password.
     * @param string $local_path   Local file path.
     * @param string $remote_path  Remote file path.
     * @return array
     */
    private static function upload_to_sftp( $host, $port, $user, $pass, $local_path, $remote_path ) {
        if ( ! function_exists( 'ssh2_connect' ) ) {
            return array(
                'success' => false,
                'error'   => 'SFTP requires the SSH2 PHP extension.',
            );
        }

        $connection = @ssh2_connect( $host, $port );
        if ( ! $connection ) {
            return array(
                'success' => false,
                'error'   => 'SFTP connection failed.',
            );
        }

        if ( ! @ssh2_auth_password( $connection, $user, $pass ) ) {
            return array(
                'success' => false,
                'error'   => 'SFTP login failed.',
            );
        }

        $sftp = @ssh2_sftp( $connection );
        if ( ! $sftp ) {
            return array(
                'success' => false,
                'error'   => 'Unable to initialize SFTP subsystem.',
            );
        }

        $remote_stream = @fopen( 'ssh2.sftp://' . intval( $sftp ) . $remote_path, 'w' );
        if ( ! $remote_stream ) {
            return array(
                'success' => false,
                'error'   => 'Cannot write to remote SFTP path.',
            );
        }

        $local_stream = @fopen( $local_path, 'r' );
        if ( ! $local_stream ) {
            @fclose( $remote_stream );
            return array(
                'success' => false,
                'error'   => 'Cannot read local file.',
            );
        }

        stream_copy_to_stream( $local_stream, $remote_stream );
        fclose( $remote_stream );
        fclose( $local_stream );

        return array(
            'success' => true,
            'message' => 'File uploaded to SFTP successfully.',
            'path'    => $remote_path,
        );
    }

    /**
     * Upload to Dropbox.
     *
     * @param string $file_path File path.
     * @param array  $settings  Settings.
     * @param array  $options   Options.
     * @return array
     */
    private static function upload_to_dropbox( $file_path, $settings, $options ) {
        $token = $options['token'] ?? ( $settings['dropbox_access_token'] ?? '' );
        if ( ! $token ) {
            return array(
                'success' => false,
                'error'   => 'Dropbox access token is missing. Please authenticate with Dropbox in Settings → Integrations.',
            );
        }

        $remote_path = $options['path'] ?? ( '/exports/' . basename( $file_path ) );
        if ( '/' !== $remote_path[0] ) {
            $remote_path = '/' . $remote_path;
        }

        $file_size = \PrimeLink\Helpers\FilesystemHelper::file_size( $file_path );
        
        // Dropbox API v2: For files > 150MB, use upload session
        // For smaller files, use simple upload
        $max_simple_upload_size = 150 * 1024 * 1024; // 150MB
        
        if ( $file_size > $max_simple_upload_size ) {
            // Use upload session for large files
            return self::upload_large_file_to_dropbox( $token, $remote_path, $file_path, $file_size );
        }

        // Simple upload for files <= 150MB
        $file_content = \PrimeLink\Helpers\FilesystemHelper::read_file( $file_path );

        $response = \PrimeLink\Helpers\HttpHelper::post( 'https://content.dropboxapi.com/2/files/upload', array(
            'headers' => array(
                'Authorization'     => 'Bearer ' . $token,
                'Dropbox-API-Arg'   => wp_json_encode( array(
                    'path'       => $remote_path,
                    'mode'       => 'overwrite',
                    'autorename' => false,
                ) ),
                'Content-Type'      => 'application/octet-stream',
            ),
            'body'    => $file_content,
            'timeout' => 120, // Increased timeout for larger files
        ) );

        if ( ! $response['success'] ) {
            $error_body = is_string( $response['body'] ) ? json_decode( $response['body'], true ) : $response['body'];
            $error_message = $error_body['error_summary'] ?? $error_body['error'] ?? $response['error'] ?? 'Failed to upload to Dropbox.';
            
            return array(
                'success' => false,
                'error'   => 'Dropbox upload failed: ' . ( is_string( $error_message ) ? $error_message : wp_json_encode( $error_message ) ),
            );
        }

        // Parse response from Dropbox-API-Result header or body
        $api_result = isset( $response['headers']['dropbox-api-result'] ) ? json_decode( $response['headers']['dropbox-api-result'], true ) : null;
        if ( ! $api_result && is_string( $response['body'] ) ) {
            $api_result = json_decode( $response['body'], true );
        }
        $final_path = $api_result['path_display'] ?? $api_result['path_lower'] ?? $remote_path;

        return array(
            'success' => true,
            'message' => 'File uploaded to Dropbox successfully.',
            'path'    => $final_path,
        );
    }

    /**
     * Upload large file to Dropbox using upload session.
     *
     * @param string $token       Access token.
     * @param string $remote_path Remote path.
     * @param string $file_path   Local file path.
     * @param int    $file_size   File size.
     * @return array
     */
    private static function upload_large_file_to_dropbox( $token, $remote_path, $file_path, $file_size ) {
        // Dropbox upload session: chunk size should be 4MB minimum, but we'll use 8MB for efficiency
        $chunk_size = 8 * 1024 * 1024; // 8MB chunks
        $session_id = null;
        $offset = 0;
        
        $file_handle = fopen( $file_path, 'rb' );
        if ( ! $file_handle ) {
            return array(
                'success' => false,
                'error'   => 'Failed to open file for reading.',
            );
        }

        // Step 1: Start upload session
        $chunk = fread( $file_handle, $chunk_size );
        if ( false === $chunk ) {
            fclose( $file_handle );
            return array(
                'success' => false,
                'error'   => 'Failed to read file chunk.',
            );
        }

        $start_response = \PrimeLink\Helpers\HttpHelper::post( 'https://content.dropboxapi.com/2/files/upload_session/start', array(
            'headers' => array(
                'Authorization'   => 'Bearer ' . $token,
                'Dropbox-API-Arg' => wp_json_encode( array(
                    'close' => false,
                ) ),
                'Content-Type'    => 'application/octet-stream',
            ),
            'body'    => $chunk,
            'timeout' => 60,
        ) );

        if ( ! $start_response['success'] ) {
            fclose( $file_handle );
            $error_body = is_string( $start_response['body'] ) ? json_decode( $start_response['body'], true ) : $start_response['body'];
            $error_message = $error_body['error_summary'] ?? $error_body['error'] ?? 'Failed to start upload session.';
            return array(
                'success' => false,
                'error'   => 'Dropbox upload session start failed: ' . ( is_string( $error_message ) ? $error_message : wp_json_encode( $error_message ) ),
            );
        }

        $start_data = json_decode( $start_response['body'], true );
        $session_id = $start_data['session_id'] ?? null;
        if ( ! $session_id ) {
            fclose( $file_handle );
            return array(
                'success' => false,
                'error'   => 'No session ID received from Dropbox.',
            );
        }

        $offset = strlen( $chunk );

        // Step 2: Append chunks (if file is larger than first chunk)
        while ( $offset < $file_size ) {
            $chunk = fread( $file_handle, $chunk_size );
            if ( false === $chunk || strlen( $chunk ) === 0 ) {
                break;
            }

            $append_response = \PrimeLink\Helpers\HttpHelper::post( 'https://content.dropboxapi.com/2/files/upload_session/append_v2', array(
                'headers' => array(
                    'Authorization'   => 'Bearer ' . $token,
                    'Dropbox-API-Arg' => wp_json_encode( array(
                        'cursor' => array(
                            'session_id' => $session_id,
                            'offset'     => $offset,
                        ),
                        'close'  => ( $offset + strlen( $chunk ) >= $file_size ),
                    ) ),
                    'Content-Type'    => 'application/octet-stream',
                ),
                'body'    => $chunk,
                'timeout' => 120,
            ) );

            if ( ! $append_response['success'] ) {
                fclose( $file_handle );
                $error_body = is_string( $append_response['body'] ) ? json_decode( $append_response['body'], true ) : $append_response['body'];
                $error_message = $error_body['error_summary'] ?? $error_body['error'] ?? 'Failed to append chunk.';
                return array(
                    'success' => false,
                    'error'   => 'Dropbox upload session append failed: ' . ( is_string( $error_message ) ? $error_message : wp_json_encode( $error_message ) ),
                );
            }

            $offset += strlen( $chunk );
        }

        fclose( $file_handle );

        // Step 3: Finish upload session
        $finish_response = \PrimeLink\Helpers\HttpHelper::post( 'https://content.dropboxapi.com/2/files/upload_session/finish', array(
            'headers' => array(
                'Authorization'   => 'Bearer ' . $token,
                'Dropbox-API-Arg' => wp_json_encode( array(
                    'cursor' => array(
                        'session_id' => $session_id,
                        'offset'     => $file_size,
                    ),
                    'commit' => array(
                        'path'       => $remote_path,
                        'mode'       => 'overwrite',
                        'autorename' => false,
                    ),
                ) ),
                'Content-Type'    => 'application/octet-stream',
            ),
            'body'    => '', // Empty body for finish
            'timeout' => 120,
        ) );

        if ( ! $finish_response['success'] ) {
            $error_body = is_string( $finish_response['body'] ) ? json_decode( $finish_response['body'], true ) : $finish_response['body'];
            $error_message = $error_body['error_summary'] ?? $error_body['error'] ?? 'Failed to finish upload session.';
            return array(
                'success' => false,
                'error'   => 'Dropbox upload session finish failed: ' . ( is_string( $error_message ) ? $error_message : wp_json_encode( $error_message ) ),
            );
        }

        $finish_data = json_decode( $finish_response['body'], true );
        $final_path = $finish_data['path_display'] ?? $finish_data['path_lower'] ?? $remote_path;

        return array(
            'success' => true,
            'message' => 'File uploaded to Dropbox successfully.',
            'path'    => $final_path,
        );
    }

    /**
     * Upload to Google Drive.
     *
     * @param string $file_path File path.
     * @param array  $settings  Settings.
     * @param array  $options   Options.
     * @return array
     */
    private static function upload_to_google_drive( $file_path, $settings, $options ) {
        // Google Drive upload requires OAuth access token (API key is not supported for upload)
        $access_token = $settings['gdrive_access_token'] ?? '';
        $refresh_token = $settings['gdrive_refresh_token'] ?? '';
        $client_id = $settings['gdrive_client_id'] ?? '';
        $client_secret = $settings['gdrive_client_secret'] ?? '';
        $token_expires = $settings['gdrive_token_expires'] ?? 0;
        
        // Check if token is expired and try to refresh
        if ( $access_token && $refresh_token && $client_id && $client_secret ) {
            // Refresh if expired or will expire in next 5 minutes
            if ( ! $token_expires || time() >= ( $token_expires - 300 ) ) {
                $new_access_token = self::refresh_gdrive_token( $refresh_token, $client_id, $client_secret );
                if ( ! is_wp_error( $new_access_token ) && is_string( $new_access_token ) && ! empty( $new_access_token ) ) {
                    $access_token = $new_access_token;
                    // Update settings with new token
                    $settings['gdrive_access_token'] = $access_token;
                    if ( isset( $new_access_token['expires_in'] ) ) {
                        $settings['gdrive_token_expires'] = time() + absint( $new_access_token['expires_in'] );
                    }
                    update_option( 'mt_primelink_settings', $settings );
                } elseif ( is_wp_error( $new_access_token ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'Google Drive authentication expired. Please re-authenticate using the "Authenticate with Google Drive" button in Settings → Integrations.',
                    );
                }
            }
        }
        
        if ( ! $access_token ) {
            return array(
                'success' => false,
                'error'   => 'Google Drive OAuth access token is required for upload. Please authenticate with Google Drive in Settings → Integrations.',
            );
        }

        $file_name = basename( $file_path );
        $file_content = \PrimeLink\Helpers\FilesystemHelper::read_file( $file_path );
        $mime_type = self::get_mime_type( $file_path );

        // First, create metadata
        $metadata = array(
            'name' => $file_name,
        );

        if ( ! empty( $options['folder_id'] ) ) {
            $metadata['parents'] = array( $options['folder_id'] );
        }

        $boundary = '----WebKitFormBoundary' . uniqid();
        $multipart_body = self::create_multipart_body( $metadata, $file_content, $mime_type, $boundary );
        
        $upload_url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart';
        
        $response = \PrimeLink\Helpers\HttpHelper::post( $upload_url, array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $access_token,
                'Content-Type'  => 'multipart/related; boundary=' . $boundary,
            ),
            'body'    => $multipart_body,
            'timeout' => 60,
        ) );

        if ( ! $response['success'] ) {
            $error_msg = $response['error'] ?? 'Failed to upload to Google Drive.';
            // If 401, suggest re-authentication
            if ( isset( $response['code'] ) && $response['code'] === 401 ) {
                $error_msg = 'Google Drive authentication expired. Please re-authenticate using the "Authenticate with Google Drive" button in Settings → Integrations.';
            }
            return array(
                'success' => false,
                'error'   => $error_msg,
            );
        }

        return array(
            'success' => true,
            'message' => 'File uploaded to Google Drive successfully.',
        );
    }
    
    /**
     * Refresh Google Drive access token.
     *
     * @param string $refresh_token Refresh token.
     * @param string $client_id     Client ID.
     * @param string $client_secret Client Secret.
     * @return string|\WP_Error New access token or error.
     */
    private static function refresh_gdrive_token( $refresh_token, $client_id, $client_secret ) {
        $token_url = 'https://oauth2.googleapis.com/token';
        
        $response = wp_remote_post( $token_url, array(
            'body' => array(
                'refresh_token' => $refresh_token,
                'client_id'     => $client_id,
                'client_secret' => $client_secret,
                'grant_type'    => 'refresh_token',
            ),
            'timeout' => 30,
        ) );
        
        if ( is_wp_error( $response ) ) {
            return $response;
        }
        
        $response_code = wp_remote_retrieve_response_code( $response );
        $body = wp_remote_retrieve_body( $response );
        $data = json_decode( $body, true );
        
        if ( $response_code !== 200 || ! isset( $data['access_token'] ) ) {
            $error_msg = isset( $data['error_description'] ) ? $data['error_description'] : ( isset( $data['error'] ) ? $data['error'] : 'Failed to refresh token' );
            return new \WP_Error( 'token_refresh_failed', $error_msg );
        }
        
        // Update settings with new token
        $settings = get_option( 'mt_primelink_settings', array() );
        $settings['gdrive_access_token'] = $data['access_token'];
        if ( isset( $data['expires_in'] ) ) {
            $settings['gdrive_token_expires'] = time() + absint( $data['expires_in'] );
        }
        update_option( 'mt_primelink_settings', $settings );
        
        return $data['access_token'];
    }
    
    /**
     * Refresh OneDrive access token.
     *
     * @param string $refresh_token Refresh token.
     * @param string $client_id     Client ID.
     * @param string $client_secret Client Secret.
     * @param string $tenant_id     Tenant ID.
     * @return string|\WP_Error New access token or error.
     */
    private static function refresh_onedrive_token( $refresh_token, $client_id, $client_secret, $tenant_id ) {
        if ( ! $client_id || ! $client_secret ) {
            return new \WP_Error( 'missing_credentials', __( 'Client ID and Client Secret are required for token refresh.', 'primelink' ) );
        }

        // Use 'common' if tenant_id is empty (allows both personal and business accounts)
        if ( empty( $tenant_id ) ) {
            $tenant_id = 'common';
        }

        $token_url = 'https://login.microsoftonline.com/' . $tenant_id . '/oauth2/v2.0/token';

        $response = wp_remote_post( $token_url, array(
            'body' => array(
                'client_id'     => $client_id,
                'client_secret' => $client_secret,
                'refresh_token' => $refresh_token,
                'grant_type'    => 'refresh_token',
            ),
            'timeout' => 30,
        ) );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $body = wp_remote_retrieve_body( $response );
        $data = json_decode( $body, true );

        if ( ! isset( $data['access_token'] ) ) {
            return new \WP_Error( 'token_refresh_failed', __( 'Failed to refresh OneDrive access token.', 'primelink' ) );
        }

        // Update token expiration in settings
        $settings = get_option( 'mt_primelink_settings', array() );
        if ( isset( $data['expires_in'] ) ) {
            $settings['onedrive_token_expires'] = time() + absint( $data['expires_in'] );
        }
        if ( isset( $data['refresh_token'] ) ) {
            $settings['onedrive_refresh_token'] = $data['refresh_token'];
        }
        $settings['onedrive_access_token'] = $data['access_token'];
        update_option( 'mt_primelink_settings', $settings );

        return $data['access_token'];
    }
    
    /**
     * Upload to OneDrive.
     *
     * @param string $file_path File path.
     * @param array  $settings  Settings.
     * @param array  $options   Options.
     * @return array
     */
    private static function upload_to_onedrive( $file_path, $settings, $options ) {
        // OneDrive upload requires OAuth access token
        $access_token = $options['token'] ?? ( $settings['onedrive_access_token'] ?? '' );
        $refresh_token = $settings['onedrive_refresh_token'] ?? '';
        $client_id = $settings['onedrive_client_id'] ?? '';
        $client_secret = $settings['onedrive_client_secret'] ?? '';
        $tenant_id = $settings['onedrive_tenant_id'] ?? '';
        $token_expires = $settings['onedrive_token_expires'] ?? 0;
        
        // Use 'common' if tenant_id is empty (allows both personal and business accounts)
        if ( empty( $tenant_id ) ) {
            $tenant_id = 'common';
        }
        
        // Check if token is expired and try to refresh
        if ( $access_token && $refresh_token && $client_id && $client_secret ) {
            // Refresh if expired or will expire in next 5 minutes
            if ( ! $token_expires || time() >= ( $token_expires - 300 ) ) {
                $new_access_token = self::refresh_onedrive_token( $refresh_token, $client_id, $client_secret, $tenant_id );
                if ( ! is_wp_error( $new_access_token ) && is_string( $new_access_token ) && ! empty( $new_access_token ) ) {
                    $access_token = $new_access_token;
                    // Update settings with new token
                    $settings['onedrive_access_token'] = $access_token;
                    update_option( 'mt_primelink_settings', $settings );
                } elseif ( is_wp_error( $new_access_token ) ) {
                    return array(
                        'success' => false,
                        'error'   => 'OneDrive authentication expired. Please re-authenticate using the "Authenticate with OneDrive" button in Settings → Integrations.',
                    );
                }
            }
        }
        
        if ( ! $access_token ) {
            return array(
                'success' => false,
                'error'   => 'OneDrive OAuth access token is required for upload. Please authenticate with OneDrive in Settings → Integrations.',
            );
        }

        // Get drive ID (works for both personal and business accounts)
        $base_url = 'https://graph.microsoft.com/v1.0';
        $drive_id = null;
        
        // Try to get drive info using /me/drive (works for personal accounts)
        $drive_info_url = $base_url . '/me/drive';
        $drive_info_response = \PrimeLink\Helpers\HttpHelper::get( $drive_info_url, array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $access_token,
            ),
            'timeout' => 30,
        ) );
        
        if ( $drive_info_response['success'] ) {
            $drive_info = json_decode( $drive_info_response['body'], true );
            $drive_id = $drive_info['id'] ?? null;
        }
        
        // If /me/drive fails (e.g., business account without SPO), try /me/drives
        if ( ! $drive_id ) {
            $drives_url = $base_url . '/me/drives';
            $drives_response = \PrimeLink\Helpers\HttpHelper::get( $drives_url, array(
                'headers' => array(
                    'Authorization' => 'Bearer ' . $access_token,
                ),
                'timeout' => 30,
            ) );
            
            if ( $drives_response['success'] ) {
                $drives_data = json_decode( $drives_response['body'], true );
                if ( isset( $drives_data['value'] ) && ! empty( $drives_data['value'] ) ) {
                    // Use the first available drive
                    $first_drive = $drives_data['value'][0];
                    $drive_id = $first_drive['id'] ?? null;
                }
            }
        }

        $file_name = basename( $file_path );
        $file_content = \PrimeLink\Helpers\FilesystemHelper::read_file( $file_path );
        $file_size = \PrimeLink\Helpers\FilesystemHelper::file_size( $file_path );

        $path = $options['path'] ?? ( '/exports/' . $file_name );
        if ( '/' !== $path[0] ) {
            $path = '/' . $path;
        }

        // Build upload URL with drive ID if available
        if ( $drive_id ) {
            $upload_url = $base_url . '/drives/' . rawurlencode( $drive_id ) . '/root:' . rawurlencode( $path ) . ':/content';
            $session_url = $base_url . '/drives/' . rawurlencode( $drive_id ) . '/root:' . rawurlencode( $path ) . ':/createUploadSession';
        } else {
            // Fallback to /me/drive (for personal accounts)
            $upload_url = $base_url . '/me/drive/root:' . rawurlencode( $path ) . ':/content';
            $session_url = $base_url . '/me/drive/root:' . rawurlencode( $path ) . ':/createUploadSession';
        }

        // For large files, use upload session, for small files use simple upload
        if ( $file_size > 4 * 1024 * 1024 ) {
            // Use upload session for files > 4MB
            return self::upload_large_file_to_onedrive( $access_token, $session_url, $file_path, $file_size );
        }

        $response = \PrimeLink\Helpers\HttpHelper::put( $upload_url, array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $access_token,
                'Content-Type' => self::get_mime_type( $file_path ),
            ),
            'body'    => $file_content,
            'timeout' => 60,
        ) );

        if ( ! $response['success'] ) {
            return array(
                'success' => false,
                'error'   => $response['error'] ?? 'Failed to upload to OneDrive.',
            );
        }

        return array(
            'success' => true,
            'message' => 'File uploaded to OneDrive successfully.',
        );
    }

    /**
     * Upload large file to OneDrive using upload session.
     *
     * @param string $access_token Access token.
     * @param string $session_url  Upload session URL (with drive ID if available).
     * @param string $file_path    Local file path.
     * @param int    $file_size    File size.
     * @return array
     */
    private static function upload_large_file_to_onedrive( $access_token, $session_url, $file_path, $file_size ) {
        // Create upload session
        $session_response = \PrimeLink\Helpers\HttpHelper::post( $session_url, array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $access_token,
                'Content-Type'  => 'application/json',
            ),
            'body'    => wp_json_encode( array(
                'item' => array(
                    '@microsoft.graph.conflictBehavior' => 'replace',
                ),
            ) ),
        ) );

        if ( ! $session_response['success'] ) {
            return array(
                'success' => false,
                'error'   => 'Failed to create upload session.',
            );
        }

        $session_data = json_decode( $session_response['body'], true );
        $upload_url = $session_data['uploadUrl'] ?? '';
        if ( ! $upload_url && is_array( $session_response['body'] ) ) {
            $upload_url = $session_response['body']['uploadUrl'] ?? '';
        }
        if ( ! $upload_url ) {
            return array(
                'success' => false,
                'error'   => 'No upload URL received. Response: ' . ( is_string( $session_response['body'] ) ? substr( $session_response['body'], 0, 200 ) : print_r( $session_response, true ) ),
            );
        }

        // Upload file in chunks (simplified - upload entire file)
        $file_content = \PrimeLink\Helpers\FilesystemHelper::read_file( $file_path );
        $response = \PrimeLink\Helpers\HttpHelper::put( $upload_url, array(
            'headers' => array(
                'Content-Length' => $file_size,
                'Content-Range'  => 'bytes 0-' . ( $file_size - 1 ) . '/' . $file_size,
            ),
            'body'    => $file_content,
            'timeout' => 120,
        ) );

        if ( ! $response['success'] ) {
            return array(
                'success' => false,
                'error'   => 'Failed to upload file chunk.',
            );
        }

        return array(
            'success' => true,
            'message' => 'File uploaded to OneDrive successfully.',
        );
    }

    /**
     * Normalize FTP path.
     *
     * @param string $root Root path.
     * @param string $file File path.
     * @return string
     */
    private static function normalize_ftp_path( $root, $file ) {
        $root = trim( (string) $root );
        $file = trim( (string) $file );
        if ( $root && '/' !== $root[0] ) {
            $root = '/' . $root;
        }
        if ( $file && '/' !== $file[0] ) {
            $file = '/' . $file;
        }
        $path = $root . $file;
        $path = preg_replace( '#/+#', '/', $path );
        return $path ?: $file;
    }

    /**
     * Get MIME type for file.
     *
     * @param string $file_path File path.
     * @return string
     */
    private static function get_mime_type( $file_path ) {
        $ext = strtolower( pathinfo( $file_path, PATHINFO_EXTENSION ) );
        $mime_types = array(
            'csv'  => 'text/csv',
            'xml'  => 'application/xml',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        );
        return $mime_types[ $ext ] ?? 'application/octet-stream';
    }

    /**
     * Create multipart body for Google Drive upload.
     *
     * @param array  $metadata    Metadata.
     * @param string $file_content File content.
     * @param string $mime_type   MIME type.
     * @param string $boundary    Boundary string.
     * @return string
     */
    private static function create_multipart_body( $metadata, $file_content, $mime_type, $boundary = null ) {
        if ( ! $boundary ) {
            $boundary = '----WebKitFormBoundary' . uniqid();
        }
        $body = '--' . $boundary . "\r\n";
        $body .= 'Content-Disposition: form-data; name="metadata"' . "\r\n";
        $body .= 'Content-Type: application/json' . "\r\n\r\n";
        $body .= wp_json_encode( $metadata ) . "\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $metadata['name'] ) . '"' . "\r\n";
        $body .= 'Content-Type: ' . $mime_type . "\r\n\r\n";
        $body .= $file_content . "\r\n";
        $body .= '--' . $boundary . '--';
        return $body;
    }
}

