<?php

namespace App\Services;

/**
 * Provides AWS related logic, like generating authenticated requests and
 * generating signed urls
 */
class AWS
{
    /**
     * Signs a given base64 encoded ACL policy
     *
     * @param  string $policy
     * @return string signature
     */
    public function signPolicy($policy)
    {
        return with($policy, function ($policy) {
            return base64_encode(hash_hmac(
                'sha1',
                $policy,
                config('filesystems.disks.s3.secret'),
                true
            ));
        });
    }

    /**
     * Creates a POST policy for browser based S3 uploads.
     *
     * @param  string $key bucket key for the object to upload
     * @return string base64 encoded json policy
     */
    public function createUploadPolicy(String $key, int $fileSize, String $uid)
    {
        $expiration = now()->addMinutes(config('aws.upload_request_ttl'))->format('Y-m-d\TH:i:s\Z');
        return base64_encode(json_encode([
            'expiration' => $expiration,
            'conditions' => [
                ['bucket' => config('filesystems.disks.s3.bucket')],
                ['acl' => 'private'],
                ['eq', '$key', $key],
                // ['starts-with', '$Content-Type', 'image/'],
                ['eq', '$x-amz-meta-uid', $uid],
                ["content-length-range", 0, $fileSize]
            ],
        ]));
    }

    /**
     * createUploadRequest
     *
     * @param  string $key
     * @param  string $originalFilename
     * @param  int $fileSize
     * @return array upload request
     */
    public function createUploadRequest(String $key, String $originalFilename, int $fileSize, String $uid)
    {
        return [
            'filename' => $originalFilename,
            'key' => $key,
            'policy' => $policy = $this->createUploadPolicy($key, $fileSize, $uid),
            'signature' => $this->signPolicy($policy),
            'AWSAccessKeyId' => config('filesystems.disks.s3.key'),
            'acl' => 'private',
            'endpoint' => 'https://' . config('filesystems.disks.s3.bucket') . '.s3.amazonaws.com'
        ];
    }

    /**
     * getSignedUrl generates signed cloudfront urls
     *
     * @param  mixed $request
     * @return void
     */
    public function getSignedUrl($url)
    {
        $expires = now()->addMinutes(config('aws.signed_urls_ttl'))->timestamp;

        $policy = [
            "Statement" => [
                [
                    "Resource" => $url,
                    "Condition" => [
                        "DateLessThan" => [
                            "AWS:EpochTime" => $expires
                        ]
                    ]
                ]
            ]
        ];
        $jsonPolicy = json_encode($policy, JSON_UNESCAPED_SLASHES);

        // load the private key
        $keyPairId = config('aws.key_pair_id');

        $fp = fopen(base_path("security/pk-$keyPairId.pem"), "r");
        $privatekey = fread($fp, 8192);
        fclose($fp);
        $pkeyid = openssl_get_privatekey($privatekey);

        // compute signature
        openssl_sign($jsonPolicy, $signature, $pkeyid);

        // free the key from memory
        openssl_free_key($pkeyid);

        $safeSignature = $this->url_safe_base64_encode($signature);

        $signedUrl = $url . '?Expires=' . $expires . '&Signature=' . $safeSignature . '&Key-Pair-Id=' . $keyPairId;

        return $signedUrl;
    }

    function url_safe_base64_encode($value)
    {
        $encoded = base64_encode($value);
        // replace unsafe characters +, = and / with
        // the safe characters -, _ and ~
        return str_replace(
            array('+', '=', '/'),
            array('-', '_', '~'),
            $encoded);
    }

    public function getThumbUrl(string $originalKey, int $width, int $height, string $fit)
    {
        $thumbRequest = [
            "bucket" => config('filesystems.disks.s3.bucket'),
            "key" => $originalKey,
            "edits" => [
                "resize" => [
                    "width" => $width,
                    "height" => $height,
                    "fit" => $fit
                ]
            ]
        ];
        $encodedRequest = base64_encode(json_encode($thumbRequest));
        $endpoint = config('aws.cloudfront_endpoint');
        return "$endpoint/$encodedRequest";
    }
}
