<?php
namespace ModulesAbstract\ElectronicInvoice;

/**
 * Implements a temporary file management system that creates, tracks, and cleans up temporary files.
 *
 * This class manages a pool of temporary files in a dedicated subfolder (named after $baseName) with the following features:
 * - Creates temporary files with unique names based on a base name and random suffixes
 * - Tracks file creation time to enforce maximum age policies
 * - Provides automatic cleanup of expired temporary files
 * - Supports file rotation when the maximum number of files is reached
 * - Returns available filenames for writing new temporary data
 *
 * Usage: `$filename = (new TemporaryFilePicker($path, 'report.tmp'))->initialize()->getAvailableFile();`
 */
class TemporaryFilePicker
{
    /**
     * Base path of the temporary files
     *
     * @var string
     */
    protected $basePath = null;

    /**
     * Base name of the temporary files
     *
     * @var string
     */
    protected $baseName = null;

    /**
     * Base extension of the temporary files
     *
     * @var string
     */
    protected $baseExtension = null;

    /**
     * Base path and name of the temporary files. Its composed by concatenating the base path and base name
     *
     * @var string
     */
    protected $basePathAndName = null;

    /**
     * Max allowed age of the temporary files, in seconds
     *
     * @var int
     */
    protected $maxAllowedAge = 3600;

    public function __construct($basePath, $baseName, $maxAllowedAge=3600)
    {
        $this->basePath =           $basePath;
        $this->baseName =           \Engine\File::getName(preg_replace("/[^a-z0-9-_\.]/i", "", $baseName));
        $this->baseExtension =      (\Engine\File::getExtension(preg_replace("/[^a-z0-9-_\.]/i", "", $baseName))?:'tmp');
        $this->basePathAndName =    sprintf("%s/%s/", $this->basePath, $this->baseName);
        $this->maxAllowedAge =      $maxAllowedAge;
    }

    /**
     * Initialize the class, pre-populate any needed data
     *
     * @return $this
     */
    public function initialize()
    {
        // create base path if it doesn't exist
        if (!\Engine\Dir::check($this->basePathAndName)) {
            \Engine\Dir::create($this->basePathAndName);
        }

        // cleanup existing temp files
        if (\Engine\RandomGenerator::randomInt(0, 100) < 33) {  // 33% random chance of cleanup
            $this->cleanup();
        }

        return $this;
    }


    /**
     * Cleanup after itself, remove temp files from disk. Assumed to be slow. Should NOT be called on each class destruction/constructor
     *
     * @return $this
     */
    public function cleanup()
    {
        $currentTime = time();
        $dirContents = \Engine\Dir::getContents($this->basePathAndName);

        // cleanup existing files
        $existingFiles = (array)$dirContents['files'];  // use (array) because this can return a blank array in some cases

        // filter old files
        $oldFiles = array_filter($existingFiles, function($f) use ($currentTime) {
            return ($currentTime - filemtime($this->basePathAndName . $f)) > $this->maxAllowedAge;
        });
        // remove old files
        foreach ($oldFiles as $f) {
            \Engine\File::delete($this->basePathAndName . $f);
        }


        // cleanup existing folders
        $existingFolders = (array)$dirContents['dirs'];  // use (array) because this can return a blank array in some cases

        // filter old folders
        $oldFolders = array_filter($existingFolders, function($f) use ($currentTime) {
            return ($currentTime - filemtime($this->basePathAndName . $f)) > $this->maxAllowedAge;
        });
        // remove old folders
        foreach ($oldFolders as $f) {
            \Engine\Dir::delete($this->basePathAndName . $f);
        }

        return $this;
    }

    /**
     * Get the first available filename
     *
     * @return \SplFileInfo
     */
    public function getAvailableFile()
    {
        // this version doesn't use a timestamp, but it's less random
        // $loops = 10;
        // do {
        //     $f = sprintf("%s/%s.%s", $this->basePathAndName, \Engine\RandomGenerator::randomString(\Engine\RandomGenerator::randomInt(3, 6)), $this->baseExtension);
        // } while(is_file($f) && --$loops);
        // if ($loops <= 0) {
        //     throw new \Exception("Unable to create temp file! Loops exceeded!");
        // }

        // this version is timestamp-based, and it's more random, this avois collisions naturally, so no need to loop
        $f = sprintf("%s/%s-%s.%s", $this->basePathAndName, uniqid(), \Engine\RandomGenerator::randomString(\Engine\RandomGenerator::randomInt(2, 9)), $this->baseExtension);


        // create file, make sure it's empty
        \Engine\File::create($f, "");
        return $f;
    }

}
