<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Test extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function requestUpload(Request $request)
    {
        $policies = [];
        foreach($request->get('files') as $file) {
            $filenameParts = explode('.', $file['name']);
            $generatedFilename = Str::random(7) . '.' . strtolower(array_pop($filenameParts));
            $key = 'test/' . $generatedFilename;
            $policies[] = [
                'key' => $key,
                'policy' => $policy = $this->createPolicy($file, $key),
                'signature' => $this->signPolicy($policy),
                'AWSAccessKeyId' => config('filesystems.disks.s3.key'),
                'acl' => 'private',
                'endpoint' => 'https://' . config('filesystems.disks.s3.bucket') . '.s3.amazonaws.com'
            ];
        } 
        
        return $policies;
    }

    function createPolicy($file, $key) 
    {
        return base64_encode(json_encode([
            'expiration' => now()->addHour()->format('Y-m-d\TH:i:s\Z'),
            'conditions' => [
                ['bucket' => config('filesystems.disks.s3.bucket')],
                ['acl' => 'private'],
                ['eq', '$key', $key]
            ],
        ]));
    }

    function signPolicy($policy)
    {
      return with($policy, function ($policy) {
          return base64_encode(hash_hmac(
              'sha1',
              $policy,
              config('filesystems.disks.s3.secret'),
              true
          ));
      });
    }


    public function requestDownloadUrl(Request $request)
    {
        $key = $request->get('key');
        $datestamp = now()->format('Ymd');
        $timestamp = now()->format('Ymd\THis\Z');
        $accessKey = config('filesystems.disks.s3.key');
        $region = config('filesystems.disks.s3.region');
        $bucket = config('filesystems.disks.s3.bucket');

        $queryVars = [
            "X-Amz-Algorithm" => "AWS4-HMAC-SHA256",
            "X-Amz-Credential" => "$accessKey/$datestamp/$region/s3/aws4_request",
            "X-Amz-Date" => $timestamp,
            "X-Amz-Expires" => "20",
            "X-Amz-SignedHeaders" => "host"
        ];

        $canonical = $this->getCanonicalRequestForUrl($key, $queryVars);
        $stringToSign = "AWS4-HMAC-SHA256\n$timestamp\n$datestamp/$region/s3/aws4_request\n";
        $stringToSign .= hash(
            'sha256',
            $canonical
        );
        echo "\n\n\n";

        $bytes = unpack('C*', $canonical);
        for($i = 1; $i < sizeof($bytes); $i++)
        {
            echo (strlen(dechex($bytes[$i])) < 2 ? '0' : '') . dechex($bytes[$i]) . ' ';
        }
        /*
        $hexchars = bin2hex($canonical);
        for($i = 0; $i < strlen($canonical); $i++)
        {
            if ($i % 2 == 0) {
                echo ' ';
            }
            echo $hexchars[$i];
        }
        */
        echo "\n\n\n";
        var_dump($canonical);
        $signingKey = $this->getSigningKey();
        var_dump($stringToSign);
        $signature = hash_hmac('sha256', $stringToSign, $signingKey);

        $queryVars["X-Amz-Signature"] = $signature;

        $url = "https://" . $bucket . ".s3.amazonaws.com/" . $key . '?';

        $isFirst = true;
        foreach($queryVars as $queryVar => $value) {
            $url .= ($isFirst ? '' : '&') . $queryVar . '=' . $value;
            $isFirst = false;
        }

        /*
        $url .= "?X-Amz-Algorithm=AWS4-HMAC-SHA256";
        $url .= "&X-Amz-Credential=" . "$accessKey/$datestamp/$region/s3/aws4_request";
        $url .= "&X-Amz-Date=$timestamp";
        $url .= "&X-Amz-Expires=86400";
        $url .= "&X-Amz-SignedHeaders=host";
        $url .= "&X-Amz-Signature=$signature";
        */

        return $url;
    }

    public function uriEncode($input, $encodeSlash) {
        $result = "";
        for ($i = 0; $i < strlen($input); $i++) {
            $ch = $input[$i];
            if (($ch >= 'A' && $ch <= 'Z') || ($ch >= 'a' && $ch <= 'z') || ($ch >= '0' && $ch <= '9') || $ch == '_' || $ch == '-' || $ch == '~' || $ch == '.') {
                $result .= $ch;
            } else if ($ch == '/') {
                $result .= $encodeSlash ? "%2F" : $ch;
            } else {
                $result .= htmlentities($ch, ENT_COMPAT, 'UTF-8');;
            }
        }
        return $result;
    }

    /**
     * GET requests
     */
    public function requestDownload(Request $request)
    {
        $key = $request->get('key');
        $timestamp = now()->format('Ymd\THis\Z');
        $datestamp = now()->format('Ymd');
        $region = config('filesystems.disks.s3.region');

        $canonical = $this->getCanonicalRequest($key);

        $stringToSign = "AWS4-HMAC-SHA256\n$timestamp\n$datestamp/$region/s3/aws4_request\n";

        $stringToSign .= hash(
            'sha256',
            $canonical
        );

        $signingKey = $this->getSigningKey();

        $signature = hash_hmac('sha256', $stringToSign, $signingKey);

        $accessKey = config('filesystems.disks.s3.key');

        $authHeader = "AWS4-HMAC-SHA256 Credential=$accessKey/$datestamp/$region/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256,Signature=$signature";
        return $authHeader;
    }

    protected function getSigningKey()
    {
        $key = hash_hmac(
            'sha256',
            now()->format('Ymd'),
            "AWS4" . config('filesystems.disks.s3.secret'),
            true
        );

        $key = hash_hmac(
            'sha256',
            config('filesystems.disks.s3.region'),
            $key,
            true
        );

        $key = hash_hmac(
            'sha256',
            "s3",
            $key,
            true
        );

        $key = hash_hmac(
            'sha256',
            "aws4_request",
            $key,
            true
        );

        return $key;
    }

    protected function getCanonicalRequest($key)
    {
        //$key = urlencode('/' . $key);
        $canonical = "GET\n";
        $canonical .= "/$key" . "\n";
        $canonical .= "\n";
        $canonical .= "host:" . $this->uriEncode(config('filesystems.disks.s3.bucket') . ".s3.amazonaws.com", true) . "\n";
        $canonical .= "x-amz-content-sha256:UNSIGNED-PAYLOAD\n";
        $canonical .= "\n";
        $canonical .= "host;x-amz-content-sha256\n";
        $canonical .= "UNSIGNED-PAYLOAD";

        var_dump($canonical);
        /*
        $canonical .= hash(
            'sha256',
            ""
        );
        */

        return $canonical;
    }

    protected function getCanonicalRequestForUrl($key, $queryVars)
    {
        $canonical = "GET\n";
        $canonical .= "/$key" . "\n";

        $queryStrings = [];
        foreach ($queryVars as $queryVar => $value) {
            $queryStrings[] = $this->uriEncode($queryVar, true) . '=' . $this->uriEncode($value, true);
        }

        $canonical .= implode('&', $queryStrings) . "\n";
        $canonical .= "host:" . $this->uriEncode(config('filesystems.disks.s3.bucket') . ".s3.amazonaws.com", true) . "\n";
        $canonical .= "\n";
        $canonical .= "host\n";
        $canonical .= "UNSIGNED-PAYLOAD";

        var_dump($canonical);
        /*
        $canonical .= hash(
            'sha256',
            ""
        );
        */

        return $canonical;
    }


    /**
     * Code to generate signed cloudfront urls
     */
    public function getSignedUrl(Request $request)
    {   
        $expires = now()->addDay()->timestamp;
        $url = $request->get('url');
        // $url = 'https://d1albz4of00wpd.cloudfront.net/eyJidWNrZXQiOiJzZWxla3Rvci1kZXYxIiwia2V5IjoidGVzdC9IN3RIMjZnLmpwZyIsImVkaXRzIjp7InJlc2l6ZSI6eyJ3aWR0aCI6ODAwLCJoZWlnaHQiOjgwMCwiZml0IjoiaW5zaWRlIn19fQ==';
        $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");
        $priv_key = fread($fp, 8192);
        fclose($fp);
        $pkeyid = openssl_get_privatekey($priv_key);

        // 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);
    }
}
