<?php

use \GuzzleHttp\Client;

return [

    'name' => 'assets',
    'icon' => 'assets:icon.svg',

    'sync:push' => function(array $settings = [], ?array $target = null, ?Client $client = null) {

        // push folders
        $folders = $this->dataStorage->find('assets/folders', [])->toArray();

        if (count($folders)) {

            $this->helper('sync')->log('Pushing folders...');

            $client->request('POST', 'api/sync/job', [
                'json' => [
                    'job' => 'assets',
                    'mode' => 'push',
                    'payload' => $this->helper('jwt')->encode(['folders' => $folders], $target['syncKey'])
                ]
            ]);

        }

        // push assets
        $run = 0;
        $limit = 20;
        $synced = 0;

        $this->helper('sync')->log('Pushing assets...');

        while (true) {

            $assets = $this->dataStorage->find('assets', [
                'skip' => $run * $limit,
                'limit' => $limit
            ])->toArray();

            $payload = [
                'run' => $run,
                'uploads' => $this->fileStorage->getURL('uploads://'),
                'assets' => $assets
            ];

            if (count($assets)) {

                $synced += count($assets);

                $jwt = $this->helper('jwt')->encode($payload, $target['syncKey']);

                $this->helper('utils')->retry(3, function() use($client, $jwt) {

                    $client->request('POST', 'api/sync/job', [
                        'json' => [
                            'job' => 'assets',
                            'mode' => 'push',
                            'payload' => $jwt
                        ]
                    ]);
                }, 1);

                $this->helper('sync')->log("{$synced} assets synced...");
            }

            if (!count($assets)) {
                break;
            }

            $run += 1;
        };
    },

    'on:push' => function($payload = null) {

        if (isset($payload['folders']) && is_array($payload['folders'])) {

            foreach ($payload['folders'] as $folder) {

                $this->dataStorage->remove('assets/folders', ['_id' => $folder['_id']]);
                $this->dataStorage->insert('assets/folders', $folder);
            }

            return ['success' => true];
        }

        if (!isset($payload['assets'])) {
            return ['message' => 'no assets'];
        }

        foreach ($payload['assets'] as $asset) {

            $path = trim($asset['path'], '/');
            $url = "{$payload['uploads']}/{$path}";

            if (!$this->fileStorage->fileExists("uploads://{$path}")) {

                // check if resource exists
                $headers = get_headers($url, 1);

                if ($headers === false || !str_contains($headers[0], ' 200')) {
                    continue;
                }

                $context = stream_context_create([
                    'ssl' => [
                        'verify_peer' => true,
                        'verify_peer_name' => true,
                        'SNI_enabled' => true,
                        'disable_compression' => true,
                    ],
                ]);

                $stream = fopen($url, 'rb', false, $context);

                if ($stream === false) {
                    continue;
                }

                $this->fileStorage->writeStream("uploads://{$path}", $stream, ['mimetype' => $asset['mime']]);

                if (is_resource($stream)) {
                    fclose($stream);
                }
            }

            $this->dataStorage->remove('assets', ['_id' => $asset['_id']]);
            $this->dataStorage->insert('assets', $asset);
        }

        return ['success' => true];
    },

    'sync:pull' => function(array $settings = [], ?array $target = null, ?Client $client = null) {

        // pull folders
        $response = $client->request('POST', 'api/sync/job', [
            'json' => [
                'job' => 'assets',
                'mode' => 'pull',
                'payload' => $this->helper('jwt')->encode([
                    'folders' => true
                ], $target['syncKey'])
            ]
        ]);

        $payload = json_decode($response->getBody()->getContents(), true);

        if (isset($payload['folders']) && is_array($payload['folders'])) {

            foreach ($payload['folders'] as $folder) {
                $this->dataStorage->remove('assets/folders', ['_id' => $folder['_id']]);
                $this->dataStorage->insert('assets/folders', $folder);
            }
        }

        $run = 0;
        $synced = 0;

        while (true) {

            $response = $client->request('POST', 'api/sync/job', [
                'json' => [
                    'job' => 'assets',
                    'mode' => 'pull',
                    'payload' => $this->helper('jwt')->encode([
                        'run' => $run
                    ], $target['syncKey'])
                ]
            ]);

            $payload = json_decode($response->getBody()->getContents(), true);

            if (!isset($payload['assets']) || !count($payload['assets'])) {
                return true;
            }

            $streamContext = stream_context_create( [
                'ssl' => [
                    'verify_peer' => false,
                    'verify_peer_name' => false,
                ],
            ]);

            foreach ($payload['assets'] as $asset) {

                $path = trim($asset['path'], '/');
                $url = "{$payload['uploads']}/{$path}";

                if (!$this->fileStorage->fileExists("uploads://{$path}")) {

                    // check if resource exists
                    $headers = get_headers($url, 1, $streamContext);

                    if ($headers === false || !str_contains($headers[0], ' 200')) {
                        continue;
                    }

                    $stream = fopen($url, 'rb', false, $streamContext);

                    if ($stream === false) {
                        continue;
                    }

                    $this->fileStorage->writeStream("uploads://{$path}", $stream, ['mimetype' => $asset['mime']]);

                    if (is_resource($stream)) {
                        fclose($stream);
                    }
                }

                $this->dataStorage->remove('assets', ['_id' => $asset['_id']]);
                $this->dataStorage->insert('assets', $asset);
            }

            if (count($payload['assets'])) {
                $synced += count($payload['assets']);
                $this->helper('sync')->log("{$synced} assets synced...");
            }

            $run += 1;
        }
    },

    'on:pull' => function($payload = null) {

        if (isset($payload['folders']) && $payload['folders']) {
            return ['folders' => $this->dataStorage->find('assets/folders', [])->toArray()];
        }

        $run = $payload['run'] ?? 0;
        $limit = 20;

        $assets = $this->dataStorage->find('assets', [
            'skip' => $run * $limit,
            'limit' => $limit
        ])->toArray();

        $payload = [
            'assets' => $assets,
            'uploads' => $this->fileStorage->getURL('uploads://'),
        ];

        return $payload;
    }
];
