<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use PharData;
use RuntimeException;

/**
 * 将 tar.gz 解压为临时 .tar 后通过 PharData 读取（兼容 Windows，避免 compress.zlib:// 限制）。
 */
final class HostSystemUpdateTarGzPharAccessor
{
    private ?PharData $phar = null;

    private ?string $tempTarPath = null;

    public function __construct(string $tarGzPath)
    {
        if (! class_exists(PharData::class)) {
            throw new RuntimeException('Phar extension is required for tar.gz packages.');
        }

        if (! function_exists('gzopen')) {
            throw new RuntimeException('zlib extension is required for tar.gz packages.');
        }

        $resolved = realpath($tarGzPath);

        if ($resolved === false || ! is_file($resolved)) {
            throw new RuntimeException('tar.gz package file is missing.');
        }

        $this->tempTarPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'dberp-update-'.uniqid('', true).'.tar';
        $this->decompressGzipToTar($resolved, $this->tempTarPath);

        $previousPharReadonly = ini_get('phar.readonly');
        ini_set('phar.readonly', '0');

        try {
            $this->phar = new PharData($this->tempTarPath);
        } finally {
            ini_set('phar.readonly', (string) $previousPharReadonly);
        }
    }

    public function phar(): PharData
    {
        if (! $this->phar instanceof PharData) {
            throw new RuntimeException('tar.gz archive is not open.');
        }

        return $this->phar;
    }

    public function close(): void
    {
        $this->phar = null;

        if ($this->tempTarPath !== null && is_file($this->tempTarPath)) {
            @unlink($this->tempTarPath);
        }

        $this->tempTarPath = null;
    }

    public function __destruct()
    {
        $this->close();
    }

    private function decompressGzipToTar(string $tarGzPath, string $tarPath): void
    {
        $gz = @gzopen($tarGzPath, 'rb');

        if ($gz === false) {
            throw new RuntimeException('Unable to open tar.gz package.');
        }

        $out = fopen($tarPath, 'wb');

        if ($out === false) {
            gzclose($gz);

            throw new RuntimeException('Unable to create temporary tar extraction file.');
        }

        try {
            while (! gzeof($gz)) {
                $chunk = gzread($gz, 65_536);

                if ($chunk === false) {
                    break;
                }

                if ($chunk !== '' && fwrite($out, $chunk) === false) {
                    throw new RuntimeException('Unable to write temporary tar extraction file.');
                }
            }
        } finally {
            gzclose($gz);
            fclose($out);
        }

        if (! is_file($tarPath) || filesize($tarPath) < 1) {
            throw new RuntimeException('tar.gz package decompressed to an empty archive.');
        }
    }
}
