<?php

use Aws\S3\S3Client;
use Aws\S3\ObjectUploader;
use Aws\Exception\MultipartUploadException;

class Backpack_Export
{
    /**
     * Constructor method.
     *
     * @param Backpack $backpack An instance of the Backpack class.
     *
     * @return void
     */
    public function __construct(private Backpack $backpack)
    {
        //
    }

    /**
     * Uploads a file to an Amazon S3 bucket.
     *
     * @param string $path   The destination path (key) in the S3 bucket.
     * @param string $file   The local file path to be uploaded.
     * @param bool   $delete Determines whether to delete the file after upload. Default is true.
     *
     * @return bool|string|null Returns the URL of the uploaded object on success, false on failure, or null if the URL is unavailable.
     */
    public function toS3(string $path, string $file, int $scheduleID, bool $delete = true): bool|string|null
    {
        try {
            $s3 = $this->setupClient();

            $prefix = get_option('mws_backpack_aws_backup_prefix');

            if (! empty($prefix)) {
                $path = get_option('mws_backpack_aws_backup_prefix') . "/" . $path;
            }

            $source = fopen($file, 'rb');

            $uploader = new ObjectUploader(
                $s3,
                get_option('mws_backpack_aws_bucket_name'),
                $path,
                $source,
                'private',
                [
                    'part_size' => 100 * 1024 * 1024, // 100 MB chunks
                    'params' => [
                        'ContentType' => 'application/zip',
                    ],
                ]
            );

            $result = $uploader->upload();

            // Save it to the backup history table
            $this->backpack->saveBackupHistory($scheduleID, $path);

            // Get the schedule
            $schedule = $this->backpack->getTask($scheduleID);

            do_action("mws_backpack_export_complete", $path, $file, $schedule, $result);

            // Delete the file
            if ($delete) {
                unlink($file);
            }

            return $result['ObjectURL'] ?? null;
        } catch (MultipartUploadException $e) {
            $this->backpack->log('S3 multipart upload error: ' . $e->getMessage());
            return false;
        } catch (\Aws\S3\Exception\S3Exception $e) {
            $this->backpack->log('S3 upload error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Generates a presigned URL for accessing an object in an S3 bucket.
     *
     * @param string $path The path to the object in the S3 bucket.
     *
     * @return string|false Returns the presigned URL as a string if successful, or false if an error occurs.
     */
    public function fromS3(string $path)
    {
        try {
            $s3 = $this->setupClient();


            // Generate a presigned URL
            $request = $s3->createPresignedRequest(
                $s3->getCommand('GetObject', [
                    'Bucket' => get_option('mws_backpack_aws_bucket_name'),
                    'Key' => $path,
                ]),
                '+1 hour'
            );

            // From the resulting RequestInterface object, you can get the URL.
            return (string) $request->getUri();
        } catch (\Aws\S3\Exception\S3Exception $e) {
            $this->backpack->log('S3 download error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Sets up and initializes the Amazon S3 client.
     *
     * @return S3Client An instance of the Amazon S3 client configured with the specified settings.
     */
    private function setupClient(): S3Client
    {
        return new S3Client([
            'version'     => '2006-03-01',
            'region'      => get_option('mws_backpack_aws_bucket_region'),
            'credentials' => [
                'key'    => $this->backpack->decrypt(get_option('mws_backpack_aws_access_key_id')),
                'secret' => $this->backpack->decrypt(get_option('mws_backpack_aws_secret_access_key')),
            ]
        ]);
    }

    /**
     * Deletes a backup record from the database and the corresponding file from S3 storage.
     *
     * @param int    $backupID The ID of the backup record to delete from the database.
     * @param string $path     The path to the file in the S3 bucket to be deleted.
     *
     * @return void
     */
    public function deleteBackup(int $backupID, string $path): void
    {
        global $wpdb;

        $wpdb->delete($wpdb->prefix . 'backpack_history', ['id' => $backupID]);

        // Also delete the file
        try {
            $s3 = $this->setupClient();


            // Delete the backup file
            $s3->deleteObject([
                "Bucket" => get_option('mws_backpack_aws_bucket_name'),
                "Key"    => $path,
            ]);
        } catch (\Aws\S3\Exception\S3Exception $e) {
            $this->backpack->log('S3 delete error: ' . $e->getMessage());
        }
    }
}
