<?php

namespace BitApps\PiPro\src\Integrations\Dropbox;

// Prevent direct script access
if (!\defined('ABSPATH')) {
    exit;
}

use BitApps\Pi\Helpers\MixInputHandler;
use BitApps\Pi\Helpers\Utility;
use BitApps\PiPro\Deps\BitApps\WPKit\Helpers\JSON;
use BitApps\PiPro\Deps\BitApps\WPKit\Http\Client\HttpClient;

class DropboxService
{
    private const API_BASE_URL = 'https://api.dropboxapi.com/2';
    private const CONTENT_BASE_URL = 'https://content.dropboxapi.com/2';

    private $http;

    private $accessToken;

    /**
     * DropboxService constructor.
     *
     * @param HttpClient $httpClient
     */
    public function __construct(HttpClient $httpClient, $accessToken)
    {
        $this->http = $httpClient;
        $this->accessToken = $accessToken;
    }

    /**
     * Send HTTP request and handle response.
     *
     * @param string $endpoint
     * @param array $payload
     * @param string $body
     * @param array $headers
     * @param string $successMessage
     * @param string $errorMessage
     * @param string $method
     *
     * @return array
     */
    private function sendRequest(
        string $endpoint,
        array $payload,
        string $body,
        array $headers,
        string $successMessage,
        string $errorMessage = '',
        string $method = 'POST'
    ): array {
        $response = $this->http->request($endpoint, $method, $body, $headers);
        $responseCode = $this->http->getResponseCode();
        $success = $responseCode >= 200 && $responseCode < 300;

        $extractedErrorMessage = null;
        if (!$success && \is_object($response)) {
            if (isset($response->error_summary)) {
                $extractedErrorMessage = $response->error_summary;
            } elseif (isset($response->error)) {
                $errorArray = (array) $response->error;
                $extractedErrorMessage = isset($errorArray['.tag']) ? $errorArray['.tag'] : (string) $response->error;
            }
        }

        return Utility::formatResponseData(
            $responseCode,
            $payload,
            $response,
            $success ? $successMessage : ($extractedErrorMessage ?? $errorMessage)
        );
    }

    /**
     * Upload a file to Dropbox.
     *
     * @param string|null $filePath
     * @param string|null $fileContent
     * @param string|null $uploadMode
     * @param string|null $fileRev
     * @param bool|null $autoRename
     *
     * @return array
     */
    public function uploadFile($nodeInfoProvider)
    {
        $filePath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('file-path.value'));
        $fileUpload = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('file-content.value'));
        $uploadMode = $nodeInfoProvider->getFieldMapConfigs('upload-mode.value');
        $fileRev = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('file-rev.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $path = $filePath ?? '';
        $mode = $uploadMode ?? 'add';
        $autorename = $autoRename ?? false;
        $rev = $fileRev ?? null;

        // Validate file upload parameter
        if (!$fileUpload || empty($fileUpload)) {
            return Utility::formatResponseData(
                400,
                ['file-upload' => $fileUpload, 'file-path' => $path, 'mode' => $mode],
                ['error' => 'File path or URL is required'],
                __('File path or URL is required', 'bit-pi')
            );
        }

        // Get file content
        $fileContent = null;

        if (filter_var($fileUpload, FILTER_VALIDATE_URL)) {
            // Handle URL
            $fileContent = @file_get_contents($fileUpload);
            if ($fileContent === false) {
                return Utility::formatResponseData(
                    400,
                    ['file-upload' => $fileUpload, 'file-path' => $path, 'mode' => $mode],
                    ['error' => 'Failed to download file from URL'],
                    __('Failed to download file from URL', 'bit-pi')
                );
            }
        } else {
            // Handle local file path
            $localPath = Utility::getFilePath(trim($fileUpload));
            if (!file_exists($localPath)) {
                return Utility::formatResponseData(
                    400,
                    ['file-upload' => $fileUpload, 'file-path' => $path, 'mode' => $mode],
                    ['error' => 'File not found at the specified path'],
                    __('File not found at the specified path', 'bit-pi')
                );
            }

            $fileContent = @file_get_contents($localPath);
            if ($fileContent === false) {
                return Utility::formatResponseData(
                    400,
                    ['file-upload' => $fileUpload, 'file-path' => $path, 'mode' => $mode],
                    ['error' => 'Failed to read file from path'],
                    __('Failed to read file from path', 'bit-pi')
                );
            }
        }

        $endpoint = self::CONTENT_BASE_URL . '/files/upload';

        // Build Dropbox-API-Arg parameters
        $dropboxApiArg = [
            'path'       => $path,
            'mode'       => $mode,
            'autorename' => $autorename,
            'mute'       => false,
        ];

        // Add rev parameter when mode is 'update'
        if ($mode === 'update' && !empty($rev)) {
            $dropboxApiArg['rev'] = $rev;
        }

        $payload = [
            'path'       => $path,
            'mode'       => $mode,
            'autorename' => $autorename,
        ];

        if ($mode === 'update') {
            $payload['rev'] = $rev;
        }

        $headers = [
            'Content-Type'   => 'application/octet-stream',
            'Authorization' => $this->accessToken,
            'Dropbox-API-Arg' => JSON::encode($dropboxApiArg),
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $fileContent,
            $headers,
            __('File uploaded successfully', 'bit-pi'),
            __('Failed to upload file', 'bit-pi')
        );
    }

    /**
     * Download a file from Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function downloadFile($nodeInfoProvider)
    {
        $filePath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('file-path.value'));
        $path = $filePath ?? '';

        $endpoint = self::CONTENT_BASE_URL . '/files/download';

        $payload = ['path' => $path];

        $headers = [
            'Authorization' => $this->accessToken,
            'Dropbox-API-Arg' => JSON::encode(['path' => $path]),
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            '',
            $headers,
            __('File downloaded successfully', 'bit-pi'),
            __('Failed to download file', 'bit-pi')
        );
    }

    /**
     * Delete a file from Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function deleteFile($nodeInfoProvider)
    {
        $filePath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('file-path.value'));
        $path = $filePath ?? '';

        $endpoint = self::API_BASE_URL . '/files/delete_v2';

        $payload = ['path' => $path];
        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('File deleted successfully', 'bit-pi'),
            __('Failed to delete file', 'bit-pi')
        );
    }

    /**
     * Move a file in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function moveFile($nodeInfoProvider)
    {
        $fromPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('from-path.value'));
        $toPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('to-path.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $fromPath = $fromPath ?? '';
        $toPath = $toPath ?? '';
        $autorename = $autoRename ?? false;

        $endpoint = self::API_BASE_URL . '/files/move_v2';

        $payload = [
            'from_path' => $fromPath,
            'to_path'   => $toPath,
            'autorename' => $autorename,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('File moved successfully', 'bit-pi'),
            __('Failed to move file', 'bit-pi')
        );
    }

    /**
     * Copy a file in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function copyFile($nodeInfoProvider)
    {
        $fromPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('from-path.value'));
        $toPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('to-path.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $fromPath = $fromPath ?? '';
        $toPath = $toPath ?? '';
        $autorename = $autoRename ?? false;

        $endpoint = self::API_BASE_URL . '/files/copy_v2';

        $payload = [
            'from_path' => $fromPath,
            'to_path'   => $toPath,
            'autorename' => $autorename,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('File copied successfully', 'bit-pi'),
            __('Failed to copy file', 'bit-pi')
        );
    }

    /**
     * Create a folder in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function createFolder($nodeInfoProvider)
    {
        $folderPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('folder-path.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $path = $folderPath ?? '';
        $autorename = $autoRename ?? false;

        $endpoint = self::API_BASE_URL . '/files/create_folder_v2';

        $payload = [
            'path'      => $path,
            'autorename' => $autorename,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Folder created successfully', 'bit-pi'),
            __('Failed to create folder', 'bit-pi')
        );
    }

    /**
     * Delete a folder from Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function deleteFolder($nodeInfoProvider)
    {
        $folderPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('folder-path.value'));
        $path = $folderPath ?? '';

        $endpoint = self::API_BASE_URL . '/files/delete_v2';

        $payload = ['path' => $path];
        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Folder deleted successfully', 'bit-pi'),
            __('Failed to delete folder', 'bit-pi')
        );
    }

    /**
     * Move a folder in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function moveFolder($nodeInfoProvider)
    {
        $fromPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('from-path.value'));
        $toPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('to-path.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $fromPath = $fromPath ?? '';
        $toPath = $toPath ?? '';
        $autorename = $autoRename ?? false;

        $endpoint = self::API_BASE_URL . '/files/move_v2';

        $payload = [
            'from_path' => $fromPath,
            'to_path'   => $toPath,
            'autorename' => $autorename,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Folder moved successfully', 'bit-pi'),
            __('Failed to move folder', 'bit-pi')
        );
    }

    /**
     * Copy a folder in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function copyFolder($nodeInfoProvider)
    {
        $fromPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('from-path.value'));
        $toPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('to-path.value'));
        $autoRename = $nodeInfoProvider->getFieldMapConfigs('auto-rename.value');

        $fromPath = $fromPath ?? '';
        $toPath = $toPath ?? '';
        $autorename = $autoRename ?? false;

        $endpoint = self::API_BASE_URL . '/files/copy_v2';

        $payload = [
            'from_path' => $fromPath,
            'to_path'   => $toPath,
            'autorename' => $autorename,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Folder copied successfully', 'bit-pi'),
            __('Failed to copy folder', 'bit-pi')
        );
    }

    /**
     * List folder contents in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function listFolder($nodeInfoProvider)
    {
        $folderPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('folder-path.value'));
        $recursive = $nodeInfoProvider->getFieldMapConfigs('recursive.value');
        $includeMediaInfo = $nodeInfoProvider->getFieldMapConfigs('include-media-info.value');
        $includeDeleted = $nodeInfoProvider->getFieldMapConfigs('include-deleted.value');

        $path = $folderPath ?? '';
        $recursive = $recursive ?? false;
        $includeMediaInfo = $includeMediaInfo ?? false;
        $includeDeleted = $includeDeleted ?? false;
        $includeHasExplicitSharedMembers = false;
        $includeMountedFolders = true;
        $includeNonDownloadableFiles = true;

        $endpoint = self::API_BASE_URL . '/files/list_folder';

        $payload = [
            'path'                              => $path,
            'recursive'                         => $recursive,
            'include_media_info'                => $includeMediaInfo,
            'include_deleted'                   => $includeDeleted,
            'include_has_explicit_shared_members' => $includeHasExplicitSharedMembers,
            'include_mounted_folders'           => $includeMountedFolders,
            'include_non_downloadable_files'    => $includeNonDownloadableFiles,
        ];

        $body = JSON::encode($payload);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Folder contents listed successfully', 'bit-pi'),
            __('Failed to list folder contents', 'bit-pi')
        );
    }

    /**
     * Search for files and folders in Dropbox.
     *
     * @param $nodeInfoProvider
     *
     * @return array
     */
    public function searchFiles($nodeInfoProvider)
    {
        $searchQuery = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('search-query.value'));
        $searchPath = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('search-path.value'));
        $searchMode = $nodeInfoProvider->getFieldMapConfigs('search-mode.value');
        $maxResults = MixInputHandler::replaceMixTagValue($nodeInfoProvider->getFieldMapConfigs('max-results.value'));

        $query = $searchQuery ?? '';
        $path = $searchPath ?? '';
        $maxResultsValue = $maxResults ?? 100;
        $mode = $searchMode ?? 'filename';

        $endpoint = self::API_BASE_URL . '/files/search_v2';

        $requestBody = [
            'query'   => $query,
            'options' => [
                'path'        => $path,
                'max_results' => (int) $maxResultsValue,
                'file_status' => [
                    '.tag' => 'deleted_filename' === $mode ? 'deleted' : 'active',
                ],
                'filename_only' => 'filename_and_content' !== $mode,
            ],
        ];

        $payload = [
            'query'       => $query,
            'path'        => $path,
            'max_results' => $maxResultsValue,
            'mode'        => $mode,
        ];

        $body = JSON::encode($requestBody);

        $headers = [
            'Content-Type' => 'application/json',
            'Authorization' => $this->accessToken,
        ];

        return $this->sendRequest(
            $endpoint,
            $payload,
            $body,
            $headers,
            __('Search completed successfully', 'bit-pi'),
            __('Failed to search files', 'bit-pi')
        );
    }
}
