<?php

declare(strict_types=1);

namespace EasyChat\Library\Android;

final class AndroidReleaseCatalog
{
    private const PRODUCTION_ENDPOINT = 'https://api.easychat24.ru/api/v1/android/latest';
    private const STAGE_ENDPOINT = 'https://stage.api.easychat24.ru/api/v1/android/latest';
    private const SUCCESS_TTL = 21600;
    private const STALE_IF_ERROR_TTL = 604800;
    private const FAILED_RETRY = 1800;

    private $settings;
    private $request;
    private $now;
    private string $endpoint;

    public function __construct($settings, callable $request, ?callable $now = null, ?string $endpoint = null)
    {
        $this->settings = $settings;
        $this->request = $request;
        $this->now = $now;
        $this->endpoint = $this->resolveEndpoint($endpoint);
    }

     
    public function latest(): array
    {
        $now = $this->now ? (int)call_user_func($this->now) : time();
        $this->resetCacheForChangedEndpoint();
        $lastSuccess = (int)$this->settings->get('android_release_check_time', '0');
        $lastAttempt = (int)$this->settings->get('android_release_check_attempt_time', '0');
        $cached = $this->decode((string)$this->settings->get('android_release_catalog', ''));
        if ($cached !== [] && $lastSuccess > 0 && $lastSuccess <= $now && ($now - $lastSuccess) <= self::SUCCESS_TTL) {
            return $cached;
        }

        if ($lastAttempt > 0 && $lastAttempt <= $now && ($now - $lastAttempt) < self::FAILED_RETRY) {
            return $this->isUsableStaleCache($cached, $lastSuccess, $now) ? $cached : [];
        }

        $this->settings->set('android_release_check_attempt_time', (string)$now);
        $response = call_user_func($this->request, $this->endpoint);
        $fresh = is_array($response) && !empty($response['transport_ok'])
            && (int)($response['http_code'] ?? 0) >= 200
            && (int)($response['http_code'] ?? 0) < 300
            ? $this->decode((string)($response['raw'] ?? ''))
            : [];
        if ($fresh === []) {
             
             
             
            return $this->isUsableStaleCache($cached, $lastSuccess, $now) ? $cached : [];
        }

        $this->settings->set('android_release_catalog', json_encode($fresh, JSON_UNESCAPED_SLASHES));
        $this->settings->set('android_release_check_time', (string)$now);
        return $fresh;
    }

    private function isUsableStaleCache(array $cached, int $lastSuccess, int $now): bool
    {
        return $cached !== []
            && $lastSuccess > 0
            && $lastSuccess <= $now
            && ($now - $lastSuccess) <= self::STALE_IF_ERROR_TTL;
    }

     
    private function decode(string $json): array
    {
        $data = json_decode($json, true);
        if (!is_array($data)) {
            return [];
        }
        $code = (int)($data['version_code'] ?? 0);
        $minimum = (int)($data['minimum_supported_version_code'] ?? 0);
        $name = trim((string)($data['version_name'] ?? ''));
        $publishedAt = trim((string)($data['published_at'] ?? ''));
        $rustore = $this->safeUrl((string)($data['rustore_url'] ?? ''), 'www.rustore.ru');
        $notes = $this->safeUrl((string)($data['release_notes_url'] ?? ''), 'docs.easychat24.ru');
        if ($code <= 0 || $minimum < 0 || $minimum > $code
            || preg_match('/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.]+)?$/D', $name) !== 1
            || $publishedAt === '' || strtotime($publishedAt) === false
            || $rustore === '' || $notes === '') {
            return [];
        }
        return [
            'version_code' => $code,
            'version_name' => $name,
            'minimum_supported_version_code' => $minimum,
            'published_at' => $publishedAt,
            'rustore_url' => $rustore,
            'release_notes_url' => $notes,
        ];
    }

    private function safeUrl(string $url, string $host): string
    {
        $parts = parse_url(trim($url));
        return is_array($parts)
            && strtolower((string)($parts['scheme'] ?? '')) === 'https'
            && strtolower((string)($parts['host'] ?? '')) === $host
            && !isset($parts['user']) && !isset($parts['pass'])
            ? trim($url)
            : '';
    }

    private function resolveEndpoint(?string $endpoint): string
    {
        if ($endpoint === null && defined('EASYCHAT_ANDROID_RELEASE_ENDPOINT')) {
            $endpoint = (string)constant('EASYCHAT_ANDROID_RELEASE_ENDPOINT');
        }

         
         
        foreach ([self::PRODUCTION_ENDPOINT, self::STAGE_ENDPOINT] as $trusted) {
            if (is_string($endpoint) && hash_equals($trusted, trim($endpoint))) {
                return $trusted;
            }
        }

        return self::PRODUCTION_ENDPOINT;
    }

    private function resetCacheForChangedEndpoint(): void
    {
        $scope = hash('sha256', $this->endpoint);
        $storedScope = (string)$this->settings->get('android_release_catalog_endpoint_scope', '');
        if ($storedScope !== '' && hash_equals($scope, $storedScope)) {
            return;
        }

         
         
        $this->settings->set('android_release_catalog', '');
        $this->settings->set('android_release_check_time', '0');
        $this->settings->set('android_release_check_attempt_time', '0');
        $this->settings->set('android_release_catalog_endpoint_scope', $scope);
    }
}
