<?php

declare(strict_types=1);

namespace App\Services;

use PharData;
use RuntimeException;
use Throwable;
use ZipArchive;

/**
 * Small ZIP-package adapter for XLSX/PPTX files.
 * PHP 7.4 compatible. No external Composer dependency.
 */
final class DiarOoxmlPackage
{
    /** @return array<string,string> */
    public static function read(string $path): array
    {
        if (!is_file($path)) {
            throw new RuntimeException('OOXML_PACKAGE_NOT_FOUND|' . $path);
        }
        if (class_exists(ZipArchive::class)) {
            $zip = new ZipArchive();
            if ($zip->open($path) !== true) {
                throw new RuntimeException('OOXML_PACKAGE_OPEN_FAILED|' . $path);
            }
            $entries = [];
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $name = (string)$zip->getNameIndex($i);
                if (substr($name, -1) === '/') {
                    continue;
                }
                $content = $zip->getFromIndex($i);
                if ($content === false) {
                    $zip->close();
                    throw new RuntimeException('OOXML_ENTRY_READ_FAILED|' . $name);
                }
                $entries[$name] = $content;
            }
            $zip->close();
            return $entries;
        }

        if (class_exists(PharData::class)) {
            try {
                $archive = new PharData($path);
                $entries = [];
                $iterator = new \RecursiveIteratorIterator($archive, \RecursiveIteratorIterator::SELF_FIRST);
                $prefix = 'phar://' . str_replace('\\', '/', $path) . '/';
                foreach ($iterator as $file) {
                    if ($file->isDir()) {
                        continue;
                    }
                    $full = str_replace('\\', '/', $file->getPathName());
                    $name = strpos($full, $prefix) === 0 ? substr($full, strlen($prefix)) : $file->getFilename();
                    $content = @file_get_contents($file->getPathName());
                    if ($content === false) {
                        throw new RuntimeException('OOXML_ENTRY_READ_FAILED|' . $name);
                    }
                    $entries[$name] = $content;
                }
                return $entries;
            } catch (Throwable $e) {
                throw new RuntimeException('OOXML_PACKAGE_OPEN_FAILED|' . $path, 0, $e);
            }
        }

        throw new RuntimeException('OOXML_ZIP_ENGINE_REQUIRED');
    }

    /** @param array<string,string> $entries */
    public static function write(string $path, array $entries): void
    {
        $directory = dirname($path);
        if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
            throw new RuntimeException('OOXML_OUTPUT_DIR_FAILED|' . $directory);
        }
        @unlink($path);

        if (class_exists(ZipArchive::class)) {
            $zip = new ZipArchive();
            if ($zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
                throw new RuntimeException('OOXML_PACKAGE_CREATE_FAILED|' . $path);
            }
            foreach ($entries as $name => $content) {
                if (!$zip->addFromString($name, $content)) {
                    $zip->close();
                    throw new RuntimeException('OOXML_ENTRY_WRITE_FAILED|' . $name);
                }
            }
            $zip->close();
            return;
        }

        if (class_exists(PharData::class)) {
            $tempZip = $path . '.zip';
            @unlink($tempZip);
            try {
                $archive = new PharData($tempZip);
                foreach ($entries as $name => $content) {
                    $archive[$name] = $content;
                }
                unset($archive);
                if (!@rename($tempZip, $path)) {
                    throw new RuntimeException('OOXML_PACKAGE_FINALIZE_FAILED|' . $path);
                }
                return;
            } catch (Throwable $e) {
                @unlink($tempZip);
                @unlink($path);
                throw new RuntimeException('OOXML_PACKAGE_CREATE_FAILED|' . $path, 0, $e);
            }
        }

        throw new RuntimeException('OOXML_ZIP_ENGINE_REQUIRED');
    }
}
