<?php

// Wersja pliku: 0.0.13
declare(strict_types=1);

@ini_set('display_errors', '0');
@ini_set('display_startup_errors', '0');
@ini_set('html_errors', '0');
@ini_set('log_errors', '1');
header_remove('X-Powered-By');

const TE8_BOOTSTRAP_VERSION = '1.0.10';
const TE8_BOOTSTRAP_RELEASE = '1.0.11';
const TE8_INSTALLER_PRODUCT = 'install-system-te8';
const TE8_INSTALLER_NAME = 'System TE8';
const TE8_INSTALLER_DIRECTORY = 'install_system_te8';
const TE8_BOOTSTRAP_ACCESS_FILE = '.te8-bootstrap-access';
const TE8_INSTALLER_HANDOFF_MODE = 'cookie-v1';
const TE8_PUBLIC_LATEST_URL = 'https://update.te8.pl/api/latest.php';
const TE8_PUBLIC_DOWNLOAD_URL = 'https://update.te8.pl/api/download.php';
const TE8_RELEASE_KEY_ID = 'te8-ed25519-b64e127719e2c66c';
const TE8_RELEASE_PUBLIC_KEY = 'deHHEB7unw9nxQRL38AY5DNdubx1cVg2nE7lIMc0QBQ=';

bootstrapSecurityHeaders();
$locale = bootstrapLocale();
$error = null;
$bootstrapReady = false;
$selectedInstallerVersion = null;
$claimedAccessProof = null;
$handoff = null;

try {
    $selectedInstallerVersion = requestedInstallerVersion();
    bootstrapInstallerHandoffMode();
    if (bootstrapHttpsReady()) {
        bootstrapStartSession();
        $bootstrapReady = true;
    }
} catch (Throwable $exception) {
    error_log('[TE8 install bootstrap] ' . $exception::class . ': ' . $exception->getMessage());
    $error = bootstrapText(
        'Bootstrap nie może uruchomić bezpiecznej sesji albo ma nieprawidłową konfigurację. Sprawdź konfigurację PHP i pliku instalacyjnego.',
        'The bootstrap cannot start a secure session or has an invalid configuration. Check PHP and installer file configuration.',
        $locale
    );
}

if (bootstrapScalar($_SERVER['REQUEST_METHOD'] ?? null, 'GET') === 'POST') {
    try {
        if (!$bootstrapReady) {
            throw new RuntimeException('Bootstrap nie jest gotowy do bezpiecznego uruchomienia.');
        }
        bootstrapRequireHttps();
        $handoffMode = bootstrapInstallerHandoffMode();
        bootstrapVerifyStartRequest();
        $claimedAccessProof = bootstrapClaimAccessProof();
        $installation = runBootstrap();
        if ($handoffMode === 'cookie-v1') {
            $handoff = bootstrapCreateInstallerHandoff($installation, $locale);
            bootstrapSetInstallerHandoffCookie($handoff);
        }
        bootstrapDeleteAccessProofClaim($claimedAccessProof);
        $claimedAccessProof = null;
        scheduleSelfDelete(__FILE__);
        header('Location: ' . (string) $installation['url'], true, 303);
        exit;
    } catch (Throwable $exception) {
        if (is_array($handoff)) {
            bootstrapDiscardInstallerHandoff($handoff);
            $handoff = null;
        }
        if (is_string($claimedAccessProof)) {
            bootstrapRestoreAccessProof($claimedAccessProof);
            $claimedAccessProof = null;
        }
        error_log('[TE8 install bootstrap] ' . $exception::class . ': ' . $exception->getMessage());
        $error = bootstrapText(
            'Nie udało się bezpiecznie pobrać albo zweryfikować instalatora. Sprawdź konfigurację serwera i spróbuj ponownie.',
            'The installer could not be downloaded or verified safely. Check the server configuration and try again.',
            $locale
        );
    }
}

renderPage($error, $locale, $selectedInstallerVersion, $bootstrapReady);

/** @return array{url: string, version: string, target_path: string} */
function runBootstrap(): array
{
    if (!class_exists(ZipArchive::class)) {
        throw new RuntimeException('Na serwerze PHP brakuje rozszerzenia ZipArchive.');
    }

    $workRoot = dirname(__DIR__) . DIRECTORY_SEPARATOR . '.te8-bootstrap-runtime';
    ensureBootstrapRuntimeDirectory($workRoot);
    $lockFile = $workRoot . DIRECTORY_SEPARATOR . 'operation.lock';
    if (is_link($lockFile)) {
        throw new RuntimeException('Blokada bootstrapu nie moze byc dowiazaniem symbolicznym.');
    }
    $lock = @fopen($lockFile, 'c');
    if (!is_resource($lock)) {
        throw new RuntimeException('Nie udalo sie utworzyc blokady bootstrapu.');
    }
    if (!@chmod($lockFile, 0600)) {
        fclose($lock);
        throw new RuntimeException('Nie udalo sie zabezpieczyc blokady bootstrapu.');
    }
    clearstatcache(true, $lockFile);
    $lockPermissions = @fileperms($lockFile);
    if (is_link($lockFile) || !is_file($lockFile)
        || (DIRECTORY_SEPARATOR === '/' && (!is_int($lockPermissions) || ($lockPermissions & 0777) !== 0600))
    ) {
        fclose($lock);
        throw new RuntimeException('Blokada bootstrapu ma nieprawidlowe uprawnienia.');
    }
    if (!flock($lock, LOCK_EX | LOCK_NB)) {
        fclose($lock);
        throw new RuntimeException('Inne uruchomienie bootstrapu jest juz w toku.');
    }

    try {
        $metadataPayload = [
            'product' => TE8_INSTALLER_PRODUCT,
            'package_kind' => 'installer',
        ];
        $requestedVersion = requestedInstallerVersion();
        if ($requestedVersion !== null) {
            $metadataPayload['version'] = $requestedVersion;
        }

        $latest = requestJson(TE8_PUBLIC_LATEST_URL, $metadataPayload, 30);
        if (empty($latest['ok'])) {
            throw new RuntimeException('Update TE8 nie zwrocil aktywnego instalatora: ' . (string) ($latest['error'] ?? 'brak szczegolow'));
        }
        if ((string) ($latest['package_kind'] ?? '') !== 'installer') {
            throw new RuntimeException('Serwer update zwrocil nieprawidlowy typ paczki.');
        }

        $version = trim((string) ($latest['version'] ?? ''));
        $checksum = strtolower(trim((string) ($latest['checksum_sha256'] ?? '')));
        if ($version === '') {
            throw new RuntimeException('Serwer update nie zwrocil wersji instalatora.');
        }
        if ($requestedVersion !== null && $version !== $requestedVersion) {
            throw new RuntimeException('Serwer update zwrocil inna wersje instalatora niz wskazana w bootstrapie.');
        }
        verifyInstallerMetadataSignature($latest, $version, $checksum);

        $binary = requestRaw(TE8_PUBLIC_DOWNLOAD_URL, [
            'product' => TE8_INSTALLER_PRODUCT,
            'package_kind' => 'installer',
            'version' => $version,
        ], 60);
        if (!hash_equals($checksum, hash('sha256', $binary))) {
            throw new RuntimeException('Suma SHA-256 pobranego instalatora jest niezgodna.');
        }

        $runId = gmdate('YmdHis') . '-' . bin2hex(random_bytes(8));
        $zipFile = $workRoot . DIRECTORY_SEPARATOR . TE8_INSTALLER_PRODUCT . '-' . $version . '-' . $runId . '.zip';
        $extractPath = $workRoot . DIRECTORY_SEPARATOR . 'extract-' . $runId;
        $preparedPath = $workRoot . DIRECTORY_SEPARATOR . 'prepared-' . $runId;
        if (file_put_contents($zipFile, $binary, LOCK_EX) === false) {
            throw new RuntimeException('Nie udalo sie zapisac pobranego ZIP instalatora.');
        }

        try {
            safeExtractZip($zipFile, $extractPath);
            $packageRoot = resolveInstallerPackageRoot($extractPath);
            validateInstallerPackageRoot($packageRoot, $version);
            copyTree($packageRoot, $preparedPath);
            installPreparedInstaller(
                $preparedPath,
                __DIR__ . DIRECTORY_SEPARATOR . TE8_INSTALLER_DIRECTORY,
                $workRoot,
                $runId
            );
        } finally {
            bootstrapCleanupPath($preparedPath);
            bootstrapCleanupPath($extractPath);
            bootstrapCleanupPath($zipFile);
        }

        $targetPath = __DIR__ . DIRECTORY_SEPARATOR . TE8_INSTALLER_DIRECTORY;
        $resolvedTarget = realpath($targetPath);
        if (!is_string($resolvedTarget) || !is_dir($resolvedTarget) || is_link($resolvedTarget)) {
            throw new RuntimeException('Opublikowany instalator ma nieprawidlowa sciezke.');
        }
        return [
            'url' => './' . TE8_INSTALLER_DIRECTORY . '/',
            'version' => $version,
            'target_path' => $resolvedTarget,
        ];
    } finally {
        flock($lock, LOCK_UN);
        fclose($lock);
        clearstatcache(true, $lockFile);
        if (is_file($lockFile) && !is_link($lockFile)) {
            @unlink($lockFile);
        }
        @rmdir($workRoot);
    }
}

/** @param array<string, mixed> $metadata */
function verifyInstallerMetadataSignature(array $metadata, string $version, string $checksum): void
{
    $signature = $metadata['signature'] ?? null;
    $zipFilename = trim((string) ($metadata['zip_filename'] ?? ''));
    if (!is_array($signature)
        || strtolower(trim((string) ($signature['algorithm'] ?? ''))) !== 'ed25519'
        || trim((string) ($signature['payload_version'] ?? '')) !== 'te8-release/v1'
        || !hash_equals(TE8_RELEASE_KEY_ID, trim((string) ($signature['key_id'] ?? '')))
        || preg_match('/^[a-zA-Z0-9][a-zA-Z0-9._+~-]{0,39}$/', $version) !== 1
        || preg_match('/^[a-f0-9]{64}$/', $checksum) !== 1
        || $zipFilename !== basename($zipFilename)
        || preg_match('/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,254}\.zip$/i', $zipFilename) !== 1) {
        throw new RuntimeException('Metadane instalatora nie zawieraja prawidlowego podpisu cyfrowego.');
    }

    $publicKey = base64_decode(TE8_RELEASE_PUBLIC_KEY, true);
    $detached = base64_decode(trim((string) ($signature['signature_base64'] ?? '')), true);
    if (!is_string($publicKey) || strlen($publicKey) !== 32
        || !is_string($detached) || strlen($detached) !== 64
        || !hash_equals(TE8_RELEASE_KEY_ID, 'te8-ed25519-' . substr(hash('sha256', $publicKey), 0, 16))) {
        throw new RuntimeException('Podpis albo klucz publiczny instalatora ma nieprawidlowy format.');
    }

    $payload = implode("\n", [
        'TE8-RELEASE-SIGNATURE-V1',
        'product=' . TE8_INSTALLER_PRODUCT,
        'package_kind=installer',
        'version=' . $version,
        'zip_filename=' . $zipFilename,
        'sha256=' . $checksum,
        '',
    ]);
    if (!verifyEd25519Detached($detached, $payload, $publicKey)) {
        throw new RuntimeException('Podpis cyfrowy instalatora jest nieprawidlowy. Pobieranie zostalo zatrzymane.');
    }
}

function verifyEd25519Detached(string $signature, string $payload, string $publicKey): bool
{
    if (function_exists('sodium_crypto_sign_verify_detached')) {
        return sodium_crypto_sign_verify_detached($signature, $payload, $publicKey);
    }
    if (function_exists('openssl_verify') && function_exists('openssl_pkey_get_public')) {
        $der = hex2bin('302a300506032b6570032100') . $publicKey;
        $pem = "-----BEGIN PUBLIC KEY-----\n" . chunk_split(base64_encode($der), 64, "\n") . "-----END PUBLIC KEY-----\n";
        $key = @openssl_pkey_get_public($pem);
        if ($key !== false) {
            return @openssl_verify($payload, $signature, $key, 0) === 1;
        }
    }
    throw new RuntimeException('PHP nie ma obslugi Ed25519 przez Sodium ani OpenSSL.');
}

function requestedInstallerVersion(): ?string
{
    if (TE8_BOOTSTRAP_VERSION === null) {
        return null;
    }

    $version = trim((string) TE8_BOOTSTRAP_VERSION);
    if ($version === '') {
        return null;
    }

    if (preg_match('/^\d+\.\d+\.\d+$/', $version) !== 1) {
        throw new RuntimeException('TE8_BOOTSTRAP_VERSION musi byc null albo wersja w formacie x.x.x.');
    }

    return $version;
}

/**
 * @param array<string, scalar> $payload
 * @return array<string, mixed>
 */
function requestJson(string $url, array $payload, int $timeout): array
{
    $body = requestRaw($url, $payload, $timeout);
    $decoded = json_decode($body, true);

    if (!is_array($decoded)) {
        throw new RuntimeException('Serwer update zwrocil nieprawidlowa odpowiedz JSON.');
    }

    return $decoded;
}

/**
 * @param array<string, scalar> $payload
 */
function requestRaw(string $url, array $payload, int $timeout): string
{
    if (!filter_var($url, FILTER_VALIDATE_URL)
        || (!hash_equals(TE8_PUBLIC_LATEST_URL, $url) && !hash_equals(TE8_PUBLIC_DOWNLOAD_URL, $url))
        || strtolower((string) parse_url($url, PHP_URL_SCHEME)) !== 'https'
        || strtolower((string) parse_url($url, PHP_URL_HOST)) !== 'update.te8.pl'
        || parse_url($url, PHP_URL_USER) !== null
        || parse_url($url, PHP_URL_PASS) !== null
        || parse_url($url, PHP_URL_FRAGMENT) !== null
    ) {
        throw new RuntimeException('Nieprawidlowy adres API update.');
    }

    $json = json_encode($payload, JSON_UNESCAPED_SLASHES);
    if (!is_string($json)) {
        throw new RuntimeException('Nie udalo sie przygotowac danych zapytania.');
    }

    if (function_exists('curl_init')) {
        $curl = curl_init($url);
        if ($curl === false) {
            throw new RuntimeException('Nie udalo sie uruchomic cURL.');
        }

        $options = [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $json,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_MAXREDIRS => 0,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Accept: application/json, application/octet-stream',
            ],
        ];
        if (!@curl_setopt_array($curl, $options)) {
            curl_close($curl);
            throw new RuntimeException('Nie udalo sie ustawic bezpiecznych opcji cURL.');
        }

        $response = curl_exec($curl);
        $status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
        $error = curl_error($curl);
        curl_close($curl);

        if (!is_string($response)) {
            throw new RuntimeException('Blad polaczenia z update.te8.pl: ' . ($error !== '' ? $error : 'brak odpowiedzi'));
        }

        if ($status !== 200) {
            throw new RuntimeException(formatUpdateHttpError($status, $response, $url, $payload));
        }

        return $response;
    }

    $context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: application/json\r\nAccept: application/json, application/octet-stream\r\n",
            'content' => $json,
            'timeout' => $timeout,
            'ignore_errors' => true,
            'follow_location' => 0,
            'max_redirects' => 0,
            'protocol_version' => 1.1,
        ],
        'ssl' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
        ],
    ]);

    $response = @file_get_contents($url, false, $context);
    if (!is_string($response)) {
        throw new RuntimeException('Nie udalo sie polaczyc z update.te8.pl. Wlacz cURL albo allow_url_fopen.');
    }

    $status = responseStatus($http_response_header ?? []);
    if ($status !== 200) {
        throw new RuntimeException(formatUpdateHttpError($status, $response, $url, $payload));
    }

    return $response;
}

/**
 * @param array<string, scalar> $payload
 */
function formatUpdateHttpError(int $status, string $response, string $url, array $payload): string
{
    $decoded = json_decode($response, true);
    $serverError = is_array($decoded)
        ? trim((string) ($decoded['error'] ?? $decoded['message'] ?? ''))
        : trim(strip_tags($response));
    $path = (string) (parse_url($url, PHP_URL_PATH) ?: $url);
    $product = (string) ($payload['product'] ?? TE8_INSTALLER_PRODUCT);

    if ($status === 404) {
        return 'Nie znaleziono endpointu instalatora w update.te8.pl (HTTP 404). '
            . 'Najczestsza przyczyna: update.te8.pl nie ma jeszcze wdrozonego publicznego trybu pobierania instalatora. '
            . 'Wymagany endpoint: ' . $path . '. '
            . 'Produkt: ' . $product . '. '
            . 'Odpowiedz serwera: ' . ($serverError !== '' ? $serverError : 'brak szczegolow') . '.';
    }

    return 'Update TE8 odrzucil zapytanie HTTP ' . $status . '. '
        . 'Endpoint: ' . $path . '. '
        . 'Produkt: ' . $product . '. '
        . 'Odpowiedz serwera: ' . ($serverError !== '' ? substr($serverError, 0, 300) : 'brak szczegolow') . '.';
}

/**
 * @param list<string> $headers
 */
function responseStatus(array $headers): int
{
    $status = 0;
    foreach ($headers as $header) {
        if (preg_match('/^HTTP\/\S+\s+(\d{3})/', $header, $match) === 1) {
            $status = (int) $match[1];
        }
    }

    return $status;
}

function safeExtractZip(string $zipFile, string $extractPath): void
{
    ensureDirectory($extractPath);
    $zip = new ZipArchive();

    if ($zip->open($zipFile) !== true) {
        throw new RuntimeException('Nie udalo sie otworzyc ZIP instalatora.');
    }

    try {
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $stat = $zip->statIndex($i);
            $name = str_replace('\\', '/', (string) ($stat['name'] ?? ''));
            if ($name === '' || str_ends_with($name, '/')) {
                continue;
            }

            $relative = safeZipRelativePath($name);
            $contents = $zip->getFromIndex($i);
            if (!is_string($contents)) {
                throw new RuntimeException('Nie udalo sie odczytac pliku z ZIP instalatora.');
            }

            $target = $extractPath . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relative);
            ensureDirectory(dirname($target));
            if (file_put_contents($target, $contents, LOCK_EX) === false) {
                throw new RuntimeException('Nie udalo sie zapisac pliku z ZIP instalatora.');
            }
        }
    } finally {
        $zip->close();
    }
}

function safeZipRelativePath(string $path): string
{
    $path = trim(str_replace('\\', '/', $path), '/');
    if ($path === '' || str_starts_with($path, '/') || preg_match('/(^|\/)\.\.(\/|$)/', $path) === 1) {
        throw new RuntimeException('ZIP instalatora zawiera niebezpieczna sciezke.');
    }

    return $path;
}

function resolveInstallerPackageRoot(string $extractPath): string
{
    $candidates = [$extractPath];
    $items = @scandir($extractPath) ?: [];

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }

        $path = $extractPath . DIRECTORY_SEPARATOR . $item;
        if (is_dir($path)) {
            $candidates[] = $path;
        }
    }

    foreach ($candidates as $candidate) {
        if (is_file($candidate . DIRECTORY_SEPARATOR . 'index.php')
            && is_file($candidate . DIRECTORY_SEPARATOR . 'InstallerService.php')
            && is_file($candidate . DIRECTORY_SEPARATOR . 'te8-package.json')) {
            return $candidate;
        }
    }

    throw new RuntimeException('Pobrany ZIP nie wyglada jak instalator TE8.');
}

function validateInstallerPackageRoot(string $packageRoot, string $version): void
{
    $manifest = json_decode((string) file_get_contents($packageRoot . DIRECTORY_SEPARATOR . 'te8-package.json'), true);
    if (!is_array($manifest)) {
        throw new RuntimeException('Manifest te8-package.json instalatora jest niepoprawny.');
    }

    $product = trim((string) ($manifest['product'] ?? $manifest['product_code'] ?? ''));
    $kind = trim((string) ($manifest['package_kind'] ?? ''));
    $manifestVersion = trim((string) ($manifest['version'] ?? ''));

    if ($product !== TE8_INSTALLER_PRODUCT || $kind !== 'installer' || $manifestVersion !== $version) {
        throw new RuntimeException('Manifest pobranego instalatora nie zgadza sie z oczekiwana paczka.');
    }
    if (bootstrapInstallerHandoffMode() === 'cookie-v1') {
        $handoffMarker = $packageRoot . DIRECTORY_SEPARATOR . 'INSTALLER_HANDOFF';
        if (!is_file($handoffMarker) || is_link($handoffMarker)
            || trim((string) @file_get_contents($handoffMarker)) !== 'te8-installer-handoff/v1'
        ) {
            throw new RuntimeException('Instalator nie obsluguje bezpiecznego przekazania kontroli v1.');
        }
    }
}

function assertSafeTargetDirectory(string $targetPath): void
{
    $base = realpath(__DIR__);
    $parent = realpath(dirname($targetPath));

    if (!is_string($base) || !is_string($parent) || $base !== $parent) {
        throw new RuntimeException('Docelowy katalog instalatora musi byc w tym samym webroot.');
    }
    if (is_link($targetPath)) {
        throw new RuntimeException('Docelowy katalog instalatora nie moze byc dowiazaniem symbolicznym.');
    }
    if (file_exists($targetPath) && !is_dir($targetPath)) {
        throw new RuntimeException('Docelowa sciezka instalatora nie jest katalogiem.');
    }
}

function installPreparedInstaller(string $preparedPath, string $targetPath, string $workRoot, string $runId): void
{
    assertSafeTargetDirectory($targetPath);
    if (!is_dir($preparedPath) || is_link($preparedPath)) {
        throw new RuntimeException('Przygotowany instalator ma nieprawidlowa postac.');
    }

    $backupPath = $workRoot . DIRECTORY_SEPARATOR . 'previous-' . $runId;
    $hadPrevious = is_dir($targetPath);
    if ($hadPrevious && !@rename($targetPath, $backupPath)) {
        throw new RuntimeException('Nie udalo sie bezpiecznie odsunac poprzedniego instalatora.');
    }

    try {
        if (!@rename($preparedPath, $targetPath)) {
            throw new RuntimeException('Nie udalo sie atomowo opublikowac instalatora.');
        }
    } catch (Throwable $exception) {
        if ($hadPrevious && !file_exists($targetPath) && !is_link($targetPath)
            && !@rename($backupPath, $targetPath)
        ) {
            throw new RuntimeException('Nie udalo sie przywrocic poprzedniego instalatora.', 0, $exception);
        }
        throw $exception;
    }

    if ($hadPrevious) {
        bootstrapCleanupPath($backupPath);
    }
}

function ensureBootstrapRuntimeDirectory(string $directory): void
{
    $parent = realpath(dirname($directory));
    if (!is_string($parent) || is_link($directory) || (file_exists($directory) && !is_dir($directory))) {
        throw new RuntimeException('Prywatny katalog roboczy bootstrapu jest niebezpieczny.');
    }
    if (!is_dir($directory) && !mkdir($directory, 0700, true) && !is_dir($directory)) {
        throw new RuntimeException('Nie mozna utworzyc prywatnego katalogu roboczego bootstrapu.');
    }
    $resolved = realpath($directory);
    if (!is_string($resolved) || dirname($resolved) !== $parent) {
        throw new RuntimeException('Prywatny katalog roboczy bootstrapu ma nieprawidlowa sciezke.');
    }
    if (DIRECTORY_SEPARATOR === '/') {
        if (!@chmod($directory, 0700)) {
            throw new RuntimeException('Nie mozna zabezpieczyc prywatnego katalogu roboczego bootstrapu.');
        }
        $permissions = @fileperms($directory);
        if (!is_int($permissions) || ($permissions & 0777) !== 0700) {
            throw new RuntimeException('Prywatny katalog roboczy bootstrapu ma nieprawidlowe uprawnienia.');
        }
    }
}

function ensureDirectory(string $directory): void
{
    if (is_link($directory)) {
        throw new RuntimeException('Katalog roboczy nie moze byc dowiazaniem symbolicznym.');
    }
    if (!is_dir($directory) && !mkdir($directory, 0775, true) && !is_dir($directory)) {
        throw new RuntimeException('Nie mozna utworzyc katalogu: ' . $directory);
    }
}

function copyTree(string $source, string $target): void
{
    ensureDirectory($target);
    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($items as $item) {
        $relative = substr($item->getPathname(), strlen($source) + 1);
        $destination = $target . DIRECTORY_SEPARATOR . $relative;

        if ($item->isLink()) {
            throw new RuntimeException('Pobrany instalator zawiera niedozwolone dowiazanie symboliczne.');
        }
        if ($item->isDir()) {
            ensureDirectory($destination);
            continue;
        }

        ensureDirectory(dirname($destination));
        if (!copy($item->getPathname(), $destination)) {
            throw new RuntimeException('Nie udalo sie skopiowac pliku instalatora.');
        }
    }
}

function removeTree(string $path): void
{
    if ($path === '' || (!file_exists($path) && !is_link($path))) {
        return;
    }

    if (is_file($path) || is_link($path)) {
        if (!unlink($path)) {
            throw new RuntimeException('Nie udalo sie usunac pliku roboczego bootstrapu.');
        }
        return;
    }

    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );

    foreach ($items as $item) {
        if ($item->isLink()) {
            if (!unlink($item->getPathname())) {
                throw new RuntimeException('Nie udalo sie usunac dowiazania z katalogu roboczego.');
            }
            continue;
        }
        if ($item->isDir()) {
            if (!rmdir($item->getPathname())) {
                throw new RuntimeException('Nie udalo sie usunac katalogu roboczego bootstrapu.');
            }
            continue;
        }

        if (!unlink($item->getPathname())) {
            throw new RuntimeException('Nie udalo sie usunac pliku z katalogu roboczego.');
        }
    }

    if (!rmdir($path)) {
        throw new RuntimeException('Nie udalo sie usunac katalogu roboczego bootstrapu.');
    }
}

function bootstrapCleanupPath(string $path): void
{
    try {
        removeTree($path);
    } catch (Throwable $exception) {
        error_log('[TE8 install bootstrap] Niepelne sprzatanie katalogu roboczego: ' . $exception->getMessage());
    }
}

function scheduleSelfDelete(string $file): void
{
    register_shutdown_function(static function () use ($file): void {
        @unlink($file);
    });
}

function bootstrapSecurityHeaders(): void
{
    header_remove('X-Powered-By');
    header('X-Content-Type-Options: nosniff');
    header('X-Frame-Options: DENY');
    header('Referrer-Policy: no-referrer');
    header("Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'");
    header('Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()');
    header('Cache-Control: no-store, max-age=0');
    if (bootstrapHttpsReady()) {
        header('Strict-Transport-Security: max-age=31536000');
    }
}

function bootstrapHttpsReady(): bool
{
    $https = strtolower(trim(bootstrapScalar($_SERVER['HTTPS'] ?? null)));
    return in_array($https, ['on', '1'], true) || bootstrapScalar($_SERVER['SERVER_PORT'] ?? null) === '443';
}

function bootstrapRequireHttps(): void
{
    if (!bootstrapHttpsReady()) {
        throw new RuntimeException('Bootstrap instalatora wymaga bezposredniego polaczenia HTTPS.');
    }
}

function bootstrapStartSession(): void
{
    if (session_status() !== PHP_SESSION_NONE) {
        throw new RuntimeException('Sesja PHP zostala uruchomiona przed bootstrapem.');
    }
    bootstrapRequireSessionIni('session.use_strict_mode', true);
    bootstrapRequireSessionIni('session.use_cookies', true);
    bootstrapRequireSessionIni('session.use_only_cookies', true);
    bootstrapRequireSessionIni('session.use_trans_sid', false);
    if (session_name('TE8BOOTSTRAP') === false || !session_set_cookie_params([
        'lifetime' => 0,
        'path' => '/',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict',
    ])) {
        throw new RuntimeException('Nie udalo sie ustawic bezpiecznych parametrow sesji bootstrapu.');
    }
    $cookie = session_get_cookie_params();
    if (($cookie['secure'] ?? false) !== true
        || ($cookie['httponly'] ?? false) !== true
        || ($cookie['samesite'] ?? '') !== 'Strict'
        || ($cookie['path'] ?? '') !== '/'
    ) {
        throw new RuntimeException('Parametry cookie sesji bootstrapu nie zostaly zastosowane.');
    }
    if (!@session_start() || session_status() !== PHP_SESSION_ACTIVE) {
        throw new RuntimeException('Nie udalo sie uruchomic bezpiecznej sesji bootstrapu.');
    }
    if (!isset($_SESSION['te8_bootstrap_csrf']) || !is_string($_SESSION['te8_bootstrap_csrf'])
        || preg_match('/^[a-f0-9]{64}$/', $_SESSION['te8_bootstrap_csrf']) !== 1
    ) {
        $_SESSION['te8_bootstrap_csrf'] = bin2hex(random_bytes(32));
    }
}

function bootstrapRequireSessionIni(string $name, bool $enabled): void
{
    @ini_set($name, $enabled ? '1' : '0');
    $current = strtolower(trim((string) ini_get($name)));
    $isEnabled = in_array($current, ['1', 'on', 'yes', 'true'], true);
    if ($isEnabled !== $enabled) {
        throw new RuntimeException('Serwer PHP nie pozwala ustawic bezpiecznej konfiguracji sesji bootstrapu.');
    }
}

function bootstrapVerifyStartRequest(): void
{
    $expected = bootstrapScalar($_SESSION['te8_bootstrap_csrf'] ?? null);
    $provided = trim(bootstrapScalar($_POST['csrf'] ?? null));
    if ($expected === '' || $provided === '' || !hash_equals($expected, $provided)
        || bootstrapScalar($_POST['confirm_install'] ?? null) !== '1'
    ) {
        throw new RuntimeException('Odrzucono niepotwierdzone uruchomienie bootstrapu.');
    }
    $_SESSION['te8_bootstrap_csrf'] = bin2hex(random_bytes(32));
}

function bootstrapAccessProofPath(): string
{
    return dirname(__DIR__) . DIRECTORY_SEPARATOR . TE8_BOOTSTRAP_ACCESS_FILE;
}

function bootstrapClaimAccessProof(): string
{
    $provided = trim(bootstrapScalar($_POST['operator_key'] ?? null));
    if (preg_match('/^[A-Za-z0-9._:@+~=\-]{32,128}$/D', $provided) !== 1) {
        throw new RuntimeException('Odrzucono nieprawidlowe potwierdzenie dostepu operatora.');
    }

    $proof = bootstrapAccessProofPath();
    clearstatcache(true, $proof);
    $size = @filesize($proof);
    if (!is_file($proof) || is_link($proof) || !is_int($size) || $size < 32 || $size > 256) {
        throw new RuntimeException('Brakuje bezpiecznego potwierdzenia dostepu operatora.');
    }
    if (DIRECTORY_SEPARATOR === '/') {
        $permissions = @fileperms($proof);
        if (!is_int($permissions) || !in_array($permissions & 0777, [0600, 0640], true)) {
            throw new RuntimeException('Plik potwierdzenia operatora musi miec tryb 0600 albo 0640.');
        }
    }

    $expected = @file_get_contents($proof);
    $expected = is_string($expected) ? trim($expected) : '';
    if (preg_match('/^[A-Za-z0-9._:@+~=\-]{32,128}$/D', $expected) !== 1
        || !hash_equals($expected, $provided)
    ) {
        throw new RuntimeException('Odrzucono nieprawidlowe potwierdzenie dostepu operatora.');
    }

    $claimed = $proof . '.claimed-' . bin2hex(random_bytes(12));
    if (file_exists($claimed) || is_link($claimed) || !@rename($proof, $claimed)) {
        throw new RuntimeException('Nie udalo sie bezpiecznie zajac potwierdzenia operatora.');
    }

    clearstatcache(true, $claimed);
    $claimedContents = @file_get_contents($claimed);
    $claimedPermissions = @fileperms($claimed);
    if (!is_file($claimed) || is_link($claimed) || !is_string($claimedContents)
        || (DIRECTORY_SEPARATOR === '/' && (!is_int($claimedPermissions)
            || !in_array($claimedPermissions & 0777, [0600, 0640], true)))
        || !hash_equals($expected, trim($claimedContents))
    ) {
        bootstrapRestoreAccessProof($claimed);
        throw new RuntimeException('Potwierdzenie operatora zmienilo sie podczas uruchomienia.');
    }

    return $claimed;
}

function bootstrapRestoreAccessProof(string $claimed): void
{
    if (!bootstrapIsAccessProofClaim($claimed)) {
        return;
    }
    $proof = bootstrapAccessProofPath();
    clearstatcache(true, $claimed);
    if ((is_file($claimed) || is_link($claimed)) && !file_exists($proof) && !is_link($proof)) {
        if (!@rename($claimed, $proof)) {
            error_log('[TE8 install bootstrap] Nie udalo sie przywrocic pliku potwierdzenia operatora.');
        }
    }
}

function bootstrapDeleteAccessProofClaim(string $claimed): void
{
    if (!bootstrapIsAccessProofClaim($claimed)) {
        throw new RuntimeException('Nieprawidlowe potwierdzenie operatora nie zostalo usuniete.');
    }
    clearstatcache(true, $claimed);
    if ((is_file($claimed) || is_link($claimed)) && !@unlink($claimed)) {
        throw new RuntimeException('Nie udalo sie usunac wykorzystanego potwierdzenia operatora.');
    }
}

function bootstrapIsAccessProofClaim(string $claimed): bool
{
    $proof = bootstrapAccessProofPath();
    return dirname($claimed) === dirname($proof)
        && preg_match('/^' . preg_quote(basename($proof), '/') . '\.claimed-[a-f0-9]{24}$/D', basename($claimed)) === 1;
}

/**
 * @param array{url: string, version: string, target_path: string} $installation
 * @return array{record_path: string, cookie_value: string, cookie_path: string, expires_at: int}
 */
function bootstrapCreateInstallerHandoff(array $installation, string $locale): array
{
    $target = realpath((string) ($installation['target_path'] ?? ''));
    $version = trim((string) ($installation['version'] ?? ''));
    if (!is_string($target) || !is_dir($target) || is_link($target)
        || basename($target) !== TE8_INSTALLER_DIRECTORY
        || preg_match('/^\d+\.\d+\.\d+$/D', $version) !== 1
    ) {
        throw new RuntimeException('Nie mozna przygotowac przekazania kontroli do instalatora.');
    }

    $privateRoot = dirname(__DIR__) . DIRECTORY_SEPARATOR . '.te8-installer-private';
    $handoffRoot = $privateRoot . DIRECTORY_SEPARATOR . 'handoffs';
    bootstrapEnsurePrivateDirectory($privateRoot);
    bootstrapEnsurePrivateDirectory($handoffRoot);

    $recordId = bin2hex(random_bytes(16));
    $secret = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
    $issuedAt = time();
    $expiresAt = $issuedAt + 300;
    $cookiePath = '/' . TE8_INSTALLER_DIRECTORY . '/';
    $record = [
        'schema' => 'te8-installer-handoff/v1',
        'record_id' => $recordId,
        'secret_hash' => hash('sha256', $secret),
        'product' => TE8_INSTALLER_PRODUCT,
        'installer_version' => $version,
        'installer_directory' => TE8_INSTALLER_DIRECTORY,
        'target_hash' => hash('sha256', $target),
        'host_hash' => hash('sha256', bootstrapRequestHost()),
        'cookie_path' => $cookiePath,
        'locale' => in_array($locale, ['pl', 'en'], true) ? $locale : 'pl',
        'issued_at' => $issuedAt,
        'expires_at' => $expiresAt,
    ];
    $json = json_encode($record, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
    $recordPath = $handoffRoot . DIRECTORY_SEPARATOR . $recordId . '.json';
    $temporaryPath = $handoffRoot . DIRECTORY_SEPARATOR . '.new-' . $recordId . '-' . bin2hex(random_bytes(8));
    $handle = @fopen($temporaryPath, 'x');
    if (!is_resource($handle)) {
        throw new RuntimeException('Nie mozna utworzyc prywatnego rekordu przekazania instalatora.');
    }
    $writeFailure = @chmod($temporaryPath, 0600)
        ? null
        : new RuntimeException('Nie mozna zabezpieczyc rekordu przekazania instalatora.');
    $locked = false;
    try {
        if ($writeFailure instanceof Throwable) {
            throw $writeFailure;
        }
        if (!flock($handle, LOCK_EX)) {
            throw new RuntimeException('Nie mozna zapisac prywatnego rekordu przekazania instalatora.');
        }
        $locked = true;
        $payload = $json . PHP_EOL;
        $offset = 0;
        $payloadLength = strlen($payload);
        while ($offset < $payloadLength) {
            $written = @fwrite($handle, substr($payload, $offset));
            if (!is_int($written) || $written < 1) {
                throw new RuntimeException('Nie mozna zapisac prywatnego rekordu przekazania instalatora.');
            }
            $offset += $written;
        }
        if (!@fflush($handle)) {
            throw new RuntimeException('Nie mozna utrwalic prywatnego rekordu przekazania instalatora.');
        }
        if (function_exists('fsync') && !@fsync($handle)) {
            throw new RuntimeException('Nie mozna zsynchronizowac prywatnego rekordu przekazania instalatora.');
        }
    } catch (Throwable $exception) {
        $writeFailure = $exception;
    } finally {
        if ($locked) {
            @flock($handle, LOCK_UN);
        }
        if (!@fclose($handle) && $writeFailure === null) {
            $writeFailure = new RuntimeException('Nie mozna zamknac prywatnego rekordu przekazania instalatora.');
        }
    }
    if ($writeFailure instanceof Throwable) {
        @unlink($temporaryPath);
        throw $writeFailure;
    }
    clearstatcache(true, $temporaryPath);
    $permissions = @fileperms($temporaryPath);
    if (is_link($temporaryPath)
        || !is_file($temporaryPath)
        || (DIRECTORY_SEPARATOR === '/' && (!is_int($permissions) || ($permissions & 0777) !== 0600))
    ) {
        @unlink($temporaryPath);
        throw new RuntimeException('Rekord przekazania instalatora ma nieprawidlowe uprawnienia.');
    }
    if (file_exists($recordPath) || is_link($recordPath) || !@rename($temporaryPath, $recordPath)) {
        @unlink($temporaryPath);
        throw new RuntimeException('Nie mozna opublikowac rekordu przekazania instalatora.');
    }
    clearstatcache(true, $recordPath);
    $publishedPermissions = @fileperms($recordPath);
    $publishedContents = @file_get_contents($recordPath);
    if (is_link($recordPath) || !is_file($recordPath)
        || !is_int($publishedPermissions)
        || (DIRECTORY_SEPARATOR === '/' && ($publishedPermissions & 0777) !== 0600)
        || !is_string($publishedContents)
        || !hash_equals($json . PHP_EOL, $publishedContents)
    ) {
        @unlink($recordPath);
        throw new RuntimeException('Opublikowany rekord przekazania instalatora jest nieprawidlowy.');
    }

    return [
        'record_path' => $recordPath,
        'cookie_value' => $recordId . '.' . $secret,
        'cookie_path' => $cookiePath,
        'expires_at' => $expiresAt,
    ];
}

/** @param array{record_path: string, cookie_value: string, cookie_path: string, expires_at: int} $handoff */
function bootstrapSetInstallerHandoffCookie(array $handoff): void
{
    $value = (string) ($handoff['cookie_value'] ?? '');
    $path = (string) ($handoff['cookie_path'] ?? '');
    if (preg_match('/^[a-f0-9]{32}\.[A-Za-z0-9_-]{43}$/D', $value) !== 1
        || $path !== '/' . TE8_INSTALLER_DIRECTORY . '/'
        || !setcookie('TE8INSTALLERHANDOFF', $value, [
            'expires' => (int) $handoff['expires_at'],
            'path' => $path,
            'secure' => true,
            'httponly' => true,
            'samesite' => 'Strict',
        ])
    ) {
        throw new RuntimeException('Nie mozna bezpiecznie przekazac sesji do instalatora.');
    }
}

/** @param array{record_path: string, cookie_value: string, cookie_path: string, expires_at: int} $handoff */
function bootstrapDiscardInstallerHandoff(array $handoff): void
{
    $recordPath = (string) ($handoff['record_path'] ?? '');
    $handoffRoot = dirname(__DIR__) . DIRECTORY_SEPARATOR . '.te8-installer-private' . DIRECTORY_SEPARATOR . 'handoffs';
    if (dirname($recordPath) === $handoffRoot
        && preg_match('/^[a-f0-9]{32}\.json$/D', basename($recordPath)) === 1
        && (is_file($recordPath) || is_link($recordPath))
    ) {
        @unlink($recordPath);
    }
    $cookiePath = (string) ($handoff['cookie_path'] ?? ('/' . TE8_INSTALLER_DIRECTORY . '/'));
    setcookie('TE8INSTALLERHANDOFF', '', [
        'expires' => time() - 3600,
        'path' => $cookiePath,
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict',
    ]);
}

function bootstrapEnsurePrivateDirectory(string $directory): void
{
    $parent = realpath(dirname($directory));
    if (!is_string($parent)) {
        throw new RuntimeException('Nie mozna potwierdzic nadrzednego katalogu prywatnego instalatora.');
    }
    if (is_link($directory) || (file_exists($directory) && !is_dir($directory))) {
        throw new RuntimeException('Prywatny katalog instalatora ma niebezpieczna postac.');
    }
    if (!is_dir($directory) && !mkdir($directory, 0700, true) && !is_dir($directory)) {
        throw new RuntimeException('Nie mozna utworzyc prywatnego katalogu instalatora.');
    }
    $resolved = realpath($directory);
    if (!is_string($resolved) || dirname($resolved) !== $parent) {
        throw new RuntimeException('Prywatny katalog instalatora ma nieprawidlowa sciezke.');
    }
    if (DIRECTORY_SEPARATOR === '/') {
        if (!@chmod($directory, 0700)) {
            throw new RuntimeException('Nie mozna zabezpieczyc prywatnego katalogu instalatora.');
        }
        $permissions = @fileperms($directory);
        if (!is_int($permissions) || ($permissions & 0777) !== 0700) {
            throw new RuntimeException('Prywatny katalog instalatora ma nieprawidlowe uprawnienia.');
        }
    }
}

function bootstrapInstallerHandoffMode(): string
{
    $mode = TE8_INSTALLER_HANDOFF_MODE;
    if (!in_array($mode, ['cookie-v1', 'external-token'], true)
        || preg_match('/^[a-z0-9][a-z0-9._-]{1,63}$/D', TE8_INSTALLER_PRODUCT) !== 1
        || preg_match('/^[a-z0-9][a-z0-9_]{1,63}$/D', TE8_INSTALLER_DIRECTORY) !== 1
    ) {
        throw new RuntimeException('Bootstrap ma nieprawidlowy tryb przekazania kontroli do instalatora.');
    }

    $profiles = [
        'install-system-te8' => ['directory' => 'install_system_te8', 'mode' => 'cookie-v1'],
        'install-sso' => ['directory' => 'install_sso', 'mode' => 'cookie-v1'],
        'install-te8-pl' => ['directory' => 'install_te8_pl', 'mode' => 'cookie-v1'],
        'install-update' => ['directory' => 'install_update', 'mode' => 'external-token'],
    ];
    $profile = $profiles[TE8_INSTALLER_PRODUCT] ?? null;
    if (is_array($profile)
        && ($mode !== $profile['mode'] || TE8_INSTALLER_DIRECTORY !== $profile['directory'])
    ) {
        throw new RuntimeException('Produkt instalatora ma nieprawidlowy katalog albo tryb przekazania kontroli.');
    }
    if ($mode === 'external-token' && TE8_INSTALLER_PRODUCT !== 'install-update') {
        throw new RuntimeException('Tryb external-token jest przeznaczony wylacznie dla instalatora update.te8.pl.');
    }

    return $mode;
}

function bootstrapRequestHost(): string
{
    $header = trim(bootstrapScalar($_SERVER['HTTP_HOST'] ?? null));
    if ($header === '' || str_contains($header, ',') || preg_match('/[\x00-\x20\x7F]/', $header) === 1
        || preg_match('/^(?:[a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?|\[[0-9a-f:.]+\])(?::([0-9]{1,5}))?$/Di', $header, $match) !== 1
        || (isset($match[1]) && $match[1] !== '' && ((int) $match[1] < 1 || (int) $match[1] > 65535))
    ) {
        throw new RuntimeException('Nie mozna potwierdzic hosta instalatora.');
    }
    $host = parse_url('https://' . $header, PHP_URL_HOST);
    $host = is_string($host) ? strtolower(trim(rtrim($host, '.'), '[]')) : '';
    if ($host === '' || (filter_var($host, FILTER_VALIDATE_IP) === false
        && preg_match('/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/D', $host) !== 1)
    ) {
        throw new RuntimeException('Host instalatora ma nieprawidlowy format.');
    }
    return $host;
}

function bootstrapLocale(): string
{
    $requested = strtolower(trim(bootstrapScalar($_GET['lang'] ?? null)));
    if (in_array($requested, ['pl', 'en'], true)) {
        if (bootstrapHttpsReady()) {
            setcookie('TE8BOOTSTRAPLANG', $requested, [
                'expires' => time() + 31536000,
                'path' => '/',
                'secure' => true,
                'httponly' => false,
                'samesite' => 'Lax',
            ]);
        }
        return $requested;
    }
    $cookie = strtolower(trim(bootstrapScalar($_COOKIE['TE8BOOTSTRAPLANG'] ?? null)));
    return in_array($cookie, ['pl', 'en'], true) ? $cookie : 'pl';
}

function bootstrapScalar(mixed $value, string $fallback = ''): string
{
    return is_string($value) || is_int($value) || is_float($value)
        ? (string) $value
        : $fallback;
}

function bootstrapText(string $polish, string $english, string $locale): string
{
    return $locale === 'en' ? $english : $polish;
}

function renderPage(?string $error, string $locale, ?string $selectedInstallerVersion, bool $bootstrapReady): void
{
    header('Content-Type: text/html; charset=UTF-8');
    ?>
<!doctype html>
<html lang="<?= h($locale) ?>">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?= h(bootstrapText('Instalator ' . TE8_INSTALLER_NAME, TE8_INSTALLER_NAME . ' installer', $locale)) ?></title>
    <style>
        :root{--bg:#f9f4e8;--paper:#fffaf0;--ink:#211d18;--muted:#6d6258;--line:#dfd2bf;--accent:#9c4f2f;--danger:#8b2339;--ok:#1e6f4f}
        *{box-sizing:border-box}body{margin:0;font-family:Segoe UI,Arial,sans-serif;background:radial-gradient(circle at 12% 0%,rgba(156,79,47,.13),transparent 28rem),var(--bg);color:var(--ink)}
        main{max-width:920px;margin:0 auto;padding:36px 18px 64px;display:grid;gap:18px}
        .hero,.card{border:1px solid var(--line);border-radius:24px;background:rgba(255,250,240,.92);box-shadow:0 18px 48px rgba(72,52,36,.12)}
        .hero{padding:28px}.card{padding:24px;display:grid;gap:16px}
        .eyebrow{text-transform:uppercase;letter-spacing:.18em;font-size:12px;color:var(--accent);font-weight:700}
        h1{font-size:clamp(34px,5vw,58px);line-height:.96;margin:8px 0 10px}h2{margin:0;font-size:22px}p,li{line-height:1.6;color:var(--muted)}
        ul{margin:0;padding-left:20px;display:grid;gap:8px}.notice{border:1px solid rgba(30,111,79,.22);border-radius:18px;background:rgba(30,111,79,.08);padding:14px 16px;color:var(--ok)}
        .error{border:1px solid rgba(139,35,57,.28);border-radius:18px;background:rgba(139,35,57,.08);color:var(--danger);padding:14px 16px}
        button{border:0;border-radius:999px;padding:14px 22px;background:linear-gradient(135deg,var(--accent),#c26e47);color:#fff8f1;font:inherit;cursor:pointer}.language{display:flex;justify-content:flex-end;gap:8px}.language a{padding:7px 10px;border:1px solid var(--line);border-radius:999px;color:var(--ink);text-decoration:none}.field{display:grid;gap:7px}.field label{font-weight:700}.field input{width:100%;border:1px solid var(--line);border-radius:12px;background:#fff;padding:12px 14px;color:var(--ink);font:inherit}.field small{line-height:1.45;color:var(--muted)}.confirm{display:flex;align-items:flex-start;gap:10px}.confirm input{margin-top:.35rem}
        code{font-family:Consolas,monospace;font-size:.94em}
        @media(max-width:760px){main{padding:20px 12px 44px}.hero,.card{border-radius:18px;padding:18px}}
    </style>
</head>
<body>
<main>
    <nav class="language" aria-label="<?= h(bootstrapText('Wybór języka', 'Language selection', $locale)) ?>">
        <a href="?lang=pl" lang="pl"<?= $locale === 'pl' ? ' aria-current="page"' : '' ?>>PL</a>
        <a href="?lang=en" lang="en"<?= $locale === 'en' ? ' aria-current="page"' : '' ?>>EN</a>
    </nav>
    <section class="hero">
        <div class="eyebrow">TE8 install bootstrap <?= h(TE8_BOOTSTRAP_RELEASE) ?></div>
        <h1><?= h(bootstrapText('Instalator ' . TE8_INSTALLER_NAME, TE8_INSTALLER_NAME . ' installer', $locale)) ?></h1>
        <p><?= h(bootstrapText(
            'Ten plik pobierze właściwy, podpisany instalator i rozpakuje go do katalogu ',
            'This file downloads the correct signed installer and extracts it into ',
            $locale
        )) ?><code><?= h(TE8_INSTALLER_DIRECTORY) ?></code><?= h(bootstrapText(' i przejdzie do kreatora.', ' and opens its setup wizard.', $locale)) ?></p>
        <p><?= h(bootstrapText('Wybrana wersja instalatora:', 'Selected installer version:', $locale)) ?> <code><?= h($selectedInstallerVersion ?? bootstrapText('najnowsza', 'latest', $locale)) ?></code>.</p>
    </section>

    <section class="card">
        <?php if ($error !== null): ?>
            <div class="error"><?= h($error) ?></div>
        <?php endif; ?>
        <div class="notice"><?= h(bootstrapText(
            'Bootstrap nie tworzy katalogów hostingu, bazy, domeny ani konfiguracji serwera. Środowisko przygotowuje operator przed uruchomieniem.',
            'The bootstrap does not create hosting directories, a database, a domain or server configuration. The operator prepares the environment before running it.',
            $locale
        )) ?></div>
        <?php if (!bootstrapHttpsReady()): ?>
            <div class="error"><?= h(bootstrapText(
                'Uruchom ten adres przez HTTPS. Formularz pozostaje zablokowany, aby jednorazowy kod operatora nie został wysłany jawnym połączeniem.',
                'Open this address over HTTPS. The form remains locked so the one-time operator code is never sent over a clear-text connection.',
                $locale
            )) ?></div>
        <?php endif; ?>
        <h2><?= h(bootstrapText('Przed uruchomieniem', 'Before you start', $locale)) ?></h2>
        <ul>
            <li><?= h(bootstrapText('Umieść ten plik bezpośrednio w przygotowanym public_html właściwej domeny.', 'Place this file directly in the prepared public_html of the correct domain.', $locale)) ?></li>
            <li><?= h(bootstrapText('Poziom wyżej niż public_html utwórz plik .te8-bootstrap-access, ustaw jego uprawnienia na 0600 albo 0640 i wpisz do niego losowy kod o długości co najmniej 32 znaków. Ten sam kod podaj poniżej.', 'One level above public_html, create a .te8-bootstrap-access file, set its permissions to 0600 or 0640, and add a random code of at least 32 characters. Enter the same code below.', $locale)) ?></li>
            <li><?= h(bootstrapText('Przygotuj dane istniejącej bazy i osobnego użytkownika, jeżeli kreator danego produktu ich wymaga.', 'Prepare an existing database and a dedicated database user if this product wizard requires them.', $locale)) ?></li>
            <li><?= h(bootstrapText('Po zakończeniu bootstrap usuwa jednorazowy plik potwierdzenia oraz własny install.php; nie pozostawiaj go publicznie dłużej niż to konieczne.', 'After completion, the bootstrap removes the one-time proof file and its own install.php; do not leave it publicly available longer than necessary.', $locale)) ?></li>
        </ul>
        <?php if ($bootstrapReady): ?>
        <form method="post" autocomplete="off">
            <input type="hidden" name="csrf" value="<?= h(bootstrapScalar($_SESSION['te8_bootstrap_csrf'] ?? null)) ?>">
            <div class="field">
                <label for="operator_key"><?= h(bootstrapText('Jednorazowy kod operatora', 'One-time operator code', $locale)) ?></label>
                <input id="operator_key" name="operator_key" type="password" minlength="32" maxlength="128" pattern="[A-Za-z0-9._:@+~=\-]{32,128}" required autocomplete="off" spellcheck="false">
                <small><?= h(bootstrapText('Kod jest porównywany z plikiem poza public_html i nie jest zapisywany w aplikacji.', 'The code is compared with the file outside public_html and is not stored by the application.', $locale)) ?></small>
            </div>
            <label class="confirm"><input type="checkbox" name="confirm_install" value="1" required> <span><?= h(bootstrapText('Potwierdzam, że środowisko jest przygotowane i chcę pobrać instalator.', 'I confirm that the environment is ready and I want to download the installer.', $locale)) ?></span></label>
            <button type="submit"><?= h(bootstrapText('Pobierz instalator i przejdź do kreatora', 'Download the installer and open the wizard', $locale)) ?></button>
        </form>
        <?php endif; ?>
    </section>
</main>
</body>
</html>
    <?php
}

function h(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
