#!/usr/bin/env php
<?php

declare(strict_types=1);

error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', '1');

class Spinner
{
    protected array $frames = [];

    protected int $length;

    protected int $current = 0;

    public function __construct(array $frames)
    {
        $this->frames = $frames;
        $this->length = count($this->frames);
    }

    public function tick(string $message): void
    {
        $next = $this->next();

        echo chr(27) . '[0G';
        echo sprintf('%s %s', $this->frames[$next], $message);
    }

    private function next(): int
    {
        $prev = $this->current;
        $this->current = $prev + 1;

        if ($this->current >= $this->length) {
            $this->current = 0;
        }

        return $prev;
    }
}

class UptimeWait
{
    protected Spinner $spinner;

    protected int $timeout = 180;

    protected int $retryInterval = 1;

    protected bool $debugMode = false;

    public function __construct()
    {
        $this->spinner = new Spinner(
                [
                        '🖥️🎶－🎵－📻',
                        '🖥️－🎶－🎵📻',
                        '🖥️🎵－🎶－📻',
                        '🖥️－🎵－🎶📻',
                ]
        );

        $_ENV = getenv();

        $applicationEnv = $_ENV['LOG_LEVEL'] ?? 'notice';
        $this->debugMode = ('debug' === $applicationEnv);
    }

    public function run(): void
    {
        $this->println('Starting up AzuraCast services...');

        $elapsed = 0;
        while ($elapsed <= $this->timeout) {
            if ($this->checkRedis() && $this->checkDatabase()) {
                $this->println('Services started up and ready!');
                die(0);
            }

            sleep($this->retryInterval);
            $elapsed += $this->retryInterval;

            $this->spinner->tick('Waiting...');
        }

        $this->println('Timed out waiting for services to start.');
        die(1);
    }

    protected function checkDatabase(): bool
    {
        try {
            $dbOptions = [
                    'host' => $_ENV['MYSQL_HOST'] ?? 'mariadb',
                    'port' => (int)($_ENV['MYSQL_PORT'] ?? 3306),
                    'dbname' => $_ENV['MYSQL_DATABASE'] ?? 'azuracast',
                    'user' => $_ENV['MYSQL_USER'] ?? 'azuracast',
                    'password' => $_ENV['MYSQL_PASSWORD'] ?? 'azur4c457',
            ];

            $dsn = 'mysql:host=' . $dbOptions['host'] . ';port=' . $dbOptions['port']
                    . ';dbname=' . $dbOptions['dbname'];

            $dbh = new PDO($dsn, $dbOptions['user'], $dbOptions['password']);

            $dbh->exec('SELECT 1');

            return true;
        } catch (Throwable $e) {
            if ($this->debugMode) {
                $this->println($e->getMessage());
            }

            return false;
        }
    }

    protected function checkRedis(): bool
    {
        $enableRedis = $this->envToBool($_ENV['ENABLE_REDIS'] ?? true);
        $redisHost = $_ENV['REDIS_HOST'] ?? 'redis';
        $redisPort = (int)($_ENV['REDIS_PORT'] ?? 6379);
        $redisDb = (int)($_ENV['REDIS_DB'] ?? 1);

        if (!$enableRedis) {
            $this->println('Redis disabled; skipping Redis check...');
            return true;
        }

        try {
            $redis = new Redis();
            $redis->connect($redisHost, $redisPort, 15);
            $redis->select($redisDb);

            $redis->ping();

            return true;
        } catch (Throwable $e) {
            if ($this->debugMode) {
                $this->println($e->getMessage());
            }

            return false;
        }
    }

    protected function envToBool(string|bool $value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        return str_starts_with(strtolower($value), 'y')
                || 'true' === strtolower($value)
                || '1' === $value;
    }

    protected function println(string $line): void
    {
        echo $line . "\n";
    }
}

$uptimeWait = new UptimeWait;
$uptimeWait->run();
