<?php

    /**
     * Name: R5_Reloaded_Updater
     * Author: Damian972
     * Version: 1.0.
     */
    ini_set('memory_limit', '256M');
    ini_set('user_agent', 'Mozilla/5.0 (Windows NT 5.1; rv:5.0.1) Gecko/20100101 Firefox/5.0.1');

    const CACHE_DIRECTORY = __DIR__.DIRECTORY_SEPARATOR.'r5_updater';
    const VERSION_FILE_NAME = '{prefix}_version.txt';
    const DELETE_DOWNLOADED_FILES_AFTER_INSTALL = true;
    const SEVEN_ZIP_BINARY_ABSPATH = 'C:/Program Files/7-Zip/7z.exe';

    $assets = [
        [
            'name' => 'detours_r5',
            'repo_url' => 'https://github.com/Mauler125/detours_r5',
            'type' => 'release',
            'workdir' => '',
        ],
        [
            'name' => 'scripts_r5',
            'repo_url' => 'https://github.com/Mauler125/scripts_r5',
            'type' => 'vcs',
            'workdir' => 'platform/scripts',
        ],
    ];

    if (!file_exists(CACHE_DIRECTORY)) {
        mkdir(CACHE_DIRECTORY, 0777, true);
    }

    foreach ($assets as $asset) {
        try {
            $workdir = __DIR__.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $asset['workdir']);
            $localFileVersion = $workdir.DIRECTORY_SEPARATOR.str_replace('{prefix}', $asset['name'], VERSION_FILE_NAME);
            $hasLocalVersion = file_exists($localFileVersion);
            echo "[!] Checking {$asset['name']}...".PHP_EOL;
            echo '[!] Local version found: '.($hasLocalVersion ? 'Yes' : 'No').PHP_EOL;

            if ($hasLocalVersion) {
                $localVersion = trim(file_get_contents($localFileVersion));
            }

            [
                'name' => $fileName,
                'link' => $downloadLink,
                'version' => $lastVersion,
                'extension' => $fileExtension,
            ] = getMetadata($asset['repo_url'], $asset['type']);

            if ($hasLocalVersion && $localVersion === $lastVersion) {
                echo '[!] Update required: No'.PHP_EOL.PHP_EOL;

                continue;
            }

            echo '[*] Update required: Yes'.PHP_EOL.PHP_EOL;

            $cacheFolder = CACHE_DIRECTORY.DIRECTORY_SEPARATOR.$asset['name'];
            if (!file_exists($cacheFolder)) {
                mkdir($cacheFolder, 0777, true);
            }

            $downloadFile = "{$fileName}.{$fileExtension}";
            $downloadFilePath = $cacheFolder.DIRECTORY_SEPARATOR.$downloadFile;
            echo "[!] Downloading from {$downloadLink}".PHP_EOL;
            file_put_contents($downloadFilePath, file_get_contents($downloadLink));
            echo "[+] Download finished, see: {$downloadFilePath}".PHP_EOL;

            $extractedDownloadedFileFolder = $cacheFolder.DIRECTORY_SEPARATOR.$fileName;
            echo "[!] Extracting into {$extractedDownloadedFileFolder}".PHP_EOL;
            unzip($downloadFilePath, $cacheFolder);

            echo PHP_EOL."[!] Copying files into {$workdir}".PHP_EOL;
            copyDirectory($extractedDownloadedFileFolder, $workdir);

            // store version identifier in a file
            file_put_contents($localFileVersion, $lastVersion);

            if (DELETE_DOWNLOADED_FILES_AFTER_INSTALL) {
                echo '[-] Removing cached files'.PHP_EOL;
                removeDirectory($extractedDownloadedFileFolder);
                unlink($downloadFilePath);
            }
            echo PHP_EOL.PHP_EOL;
        } catch (Exception $e) {
            echo "[-] {$e->getMessage()}".PHP_EOL;
        }
    }
    echo 'Done!'.PHP_EOL;

    /**
     * @param string $type {vcs, release}
     */
    function getMetadata(string $repositoryUrl, string $type = 'vcs'): array
    {
        $repository = str_replace('https://github.com/', '', $repositoryUrl);
        [,$repositoryName] = explode('/', $repository);

        if ('vcs' === $type) {
            $endpoint = "https://api.github.com/repos/{$repository}";
            $json = fetchJson($endpoint);

            $output = [
                'name' => "{$repositoryName}-{$json['default_branch']}",
                'version' => (new DateTimeImmutable($json['updated_at']))->getTimestamp().'',
                'link' => "{$json['html_url']}/archive/refs/heads/{$json['default_branch']}.zip",
                'extension' => 'zip',
            ];
        } elseif ('release' === $type) {
            $endpoint = "https://api.github.com/repos/{$repository}/releases";
            $json = fetchJson($endpoint);
            if (false === $lastRelease = reset($json)) {
                throw new Exception('No release available.');
            }

            if (false === $lastAsset = reset($lastRelease['assets'])) {
                throw new Exception('No asset available.');
            }

            $version = preg_replace('"\.(rar|zip)$"', '', $lastAsset['name']);
            $output = [
                'name' => $version,
                'version' => $version,
                'link' => $lastAsset['browser_download_url'],
                'extension' => substr(strrchr($lastAsset['browser_download_url'], '.'), 1),
            ];
        } else {
            throw new InvalidArgumentException('Unsupported metadata type');
        }

        return $output;
    }

    function fetchJson(string $url): array
    {
        $json = json_decode(file_get_contents($url), true);
        if (!is_array($json)) {
            throw new RuntimeException('Invalid JSON');
        }

        return $json;
    }

    function unzip(string $filePath, string $destination): void
    {
        echo exec('"'.SEVEN_ZIP_BINARY_ABSPATH.'"'.' x -y -o"'.$destination.'" '.$filePath).PHP_EOL;
    }

    function removeDirectory(string $directory): void
    {
        if (false === $files = scandir($directory)) {
            throw new RuntimeException('Unable to scan directory "'.$directory.'"');
        }

        foreach ($files as $file) {
            if ('.' === $file || '..' === $file) {
                continue;
            }

            if (is_dir($directory.DIRECTORY_SEPARATOR.$file)) {
                removeDirectory($directory.DIRECTORY_SEPARATOR.$file);

                continue;
            }
            unlink($directory.DIRECTORY_SEPARATOR.$file);
        }
        rmdir($directory);
    }

    function copyDirectory(string $source, string $destination): void
    {
        if (false === $files = scandir($source)) {
            throw new RuntimeException('Unable to scan directory "'.$source.'"');
        }

        if (!file_exists($destination)) {
            mkdir($destination, 0777, true);
        }

        foreach ($files as $file) {
            if ('.' === $file || '..' === $file) {
                continue;
            }

            if (is_dir($source.DIRECTORY_SEPARATOR.$file)) {
                copyDirectory($source.DIRECTORY_SEPARATOR.$file, $destination.DIRECTORY_SEPARATOR.$file);

                continue;
            }
            copy($source.DIRECTORY_SEPARATOR.$file, $destination.DIRECTORY_SEPARATOR.$file);
        }
    }