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

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

        $file_content = \PrimeLink\Helpers\FilesystemHelper::read_file( $file_path );
        $file_size    = \PrimeLink\Helpers\FilesystemHelper::file_size( $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' => 60,
        ) );

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

        return array(
            'success' => true,
            'message' => 'File uploaded to Dropbox successfully.',
            'path'    => $remote_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 ) {
        $token = $options['token'] ?? ( $settings['gdrive_access_token'] ?? '' );
        if ( ! $token ) {
            return array(
                'success' => false,
                'error'   => 'Google Drive access token is missing.',
            );
        }

        $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 );
        
        $response = \PrimeLink\Helpers\HttpHelper::post( 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'multipart/related; boundary=' . $boundary,
            ),
            'body'    => $multipart_body,
            'timeout' => 60,
        ) );

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

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

    /**
     * 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 ) {
        $token = $options['token'] ?? ( $settings['onedrive_access_token'] ?? '' );
        if ( ! $token ) {
            return array(
                'success' => false,
                'error'   => 'OneDrive access token is missing.',
            );
        }

        $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;
        }

        // 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( $token, $path, $file_path, $file_size );
        }

        $response = \PrimeLink\Helpers\HttpHelper::put( 'https://graph.microsoft.com/v1.0/me/drive/root:' . rawurlencode( $path ) . ':/content', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $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 $token      Access token.
     * @param string $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_onedrive( $token, $path, $file_path, $file_size ) {
        // Create upload session
        $session_response = \PrimeLink\Helpers\HttpHelper::post( 'https://graph.microsoft.com/v1.0/me/drive/root:' . rawurlencode( $path ) . ':/createUploadSession', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $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.',
            );
        }

        $upload_url = $session_response['body']['uploadUrl'] ?? '';
        if ( ! $upload_url ) {
            return array(
                'success' => false,
                'error'   => 'No upload URL received.',
            );
        }

        // 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;
    }
}

