<?php


namespace Mainto\RpcServer\Command\Sidecar;


use Mainto\RpcServer\Util\OS;
use RuntimeException;

class Sidecar {
    /**
     * @var array
     */
    private array $sidecarConf;

    private string $baseDir;

    private string $sidecarFilePath = "";

    private string $curOs = "";

    private string $curArch = "";
    /**
     * @var string
     */
    private string $sidecarDownloadBaseUrl;

    public function __construct (string $sidecarDownloadBaseUrl) {
        $this->sidecarDownloadBaseUrl = $sidecarDownloadBaseUrl;
        $this->baseDir = __DIR__."/../..";

        // for check arch
        $_ = $this->getCurArch();

        $this->sidecarConf = config('rpc-server.sidecar');
    }

    private function getCurArch (): string {
        if ($this->curArch === "") {
            $this->curArch = php_uname("m");
            if ($this->curArch != "x86_64") {
                dd("current not support arch {$this->curArch}");
            }
        }

        return $this->curArch;
    }

    public function downloadSidecar (): void {
        $runFile = $this->getRunFile();

        if (!file_exists($runFile)) {
            $config = trim(file_get_contents($this->getSidecarFilepath()));

            $sidecarUrl = $this->getSidecarDownloadBaseUrl()."{$config}/{$this->getCurOs()}/amd64/sidecar";

            print "download sidecar from {$sidecarUrl}...\n";

            $data = file_get_contents($sidecarUrl);
            $md5 = file_get_contents("{$sidecarUrl}.md5");

            if (md5($data) != substr($md5, 0, 32)) {
                dd("download sidecar is invalid, please try again later.");
            }

            if (!file_exists(dirname($runFile)) && !mkdir(dirname($runFile), 0777, true)) {
                dd("mkdir bin dir error");
            }

            if (!file_put_contents($runFile, $data)) {
                dd("write sidecar error");
            }

            if (!chmod($runFile, 0777)) {
                dd("chmod sidecar error");
            }

            $link = path_join(dirname($runFile), 'sidecar');
            @unlink($link);

            symlink('./'.basename($runFile), $link);

            print "download success.\n";
        }
    }

    public function getRunFile () {
        $config = trim(file_get_contents($this->getSidecarFilepath()));
        $runHash = substr(md5($config), 0, 8);

        return $this->getBaseDir()."/../bin/sidecar-{$this->getCurOs()}-{$this->curArch}-{$runHash}";
    }

    private function getSidecarFilepath (): string {
        if ($this->sidecarFilePath === "") {
            $this->sidecarFilePath = $this->baseDir."/sidecar_config";
            if (!file_exists($this->sidecarFilePath)) {
                dd("no sidecar config: {$this->sidecarFilePath}");
            }
        }

        return $this->sidecarFilePath;
    }

    /**
     * @return string
     */
    public function getBaseDir (): string {
        return $this->baseDir;
    }

    /**
     * @return string
     */
    private function getSidecarDownloadBaseUrl (): string {
        return $this->sidecarDownloadBaseUrl;
    }

    private function getCurOs (): string {
        if ($this->curOs === "") {
            $this->curOs = OS::getOs();
            if (!in_array($this->curOs, ["linux", "darwin"])) {
                throw new RuntimeException("current not support os {$this->curOs}");
            }
        }

        return $this->curOs;
    }

    public function genConfig () {
        $configDotEnv = "";
        foreach (array_dot($this->getSidecarConf()) as $key => $value) {
            if ($value !== null) {
                if (is_bool($value)) {
                    $value = var_export($value, true);
                }
                $configDotEnv .= sprintf("%s: %s\n", $key, $value);
            }
        }

        if (!file_exists(dirname($this->getConfigPath())) && !mkdir(dirname($this->getConfigPath()), 0777, true)) {
            dd("mkdir config dir error");
        }

        file_put_contents($this->getConfigPath(), $configDotEnv);
    }

    /**
     * @return array
     */
    public function getSidecarConf (): array {
        return $this->sidecarConf;
    }

    public function getConfigPath () {
        return $this->getBaseDir()."/../bin/config.local.env";
    }
}