<?php

namespace Velis;

use Velis\Filesystem\FilesystemInterface;
use Velis\Model\File;
use ZipArchive as BaseZipArchive;

/**
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 */
class ZipArchive
{
    /**
     * @var BaseZipArchive
     */
    private $wrapped;

    /**
     * @var string
     */
    private $fileName;

    /**
     * @var FilesystemInterface
     */
    private $filesystem;

    /**
     * ZipArchive constructor.
     * @throws Exception
     */
    public function __construct()
    {
        $this->wrapped = new BaseZipArchive();
        $this->fileName = tempnam(sys_get_temp_dir(), 'zip');

        if ($this->wrapped->open($this->fileName, BaseZipArchive::CREATE) !== true) {
            throw new Exception(Lang::get('UNABLE_TO_CREATE_ZIP_ARCHIVE'));
        }
    }

    /**
     * Add File instance contents into the archive
     * @param File $file
     * @param string|null $directory
     * @return $this
     * @throws Exception
     */
    public function addFile(File $file, $directory = null)
    {
        $filesystem = $this->getFilesystem();

        $key = $file->getStoragePath();
        if (!$filesystem->has($key)) {
            throw new Exception(sprintf('%s: %s', Lang::get('GENERAL_FILE_NOT_FOUND'), $key));
        }

        $localName = $file->id() . '.' . Filter::filterFilename($file->getFilename());
        if ($directory) {
            $localName = $directory . '/' . $localName;
        }

        return $this->addFromString($localName, $filesystem->read($key));
    }

    /**
     * @return FilesystemInterface
     */
    private function getFilesystem()
    {
        if (!$this->filesystem) {
            $this->filesystem = App::$di->get('filesystem');
        }

        return $this->filesystem;
    }

    /**
     * Add file from string contents
     * @param string $localName
     * @param string $contents
     * @return $this
     */
    public function addFromString(string $localName, string $contents)
    {
        $this->wrapped->addFromString($localName, $contents);

        return $this;
    }

    /**
     * Close the archive and return its temporary path
     * @return string
     */
    public function close()
    {
        $this->wrapped->close();

        return $this->fileName;
    }
}
