<?php
/**
 * Setup Class
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Setup
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025, 2026 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Setup;


use Illuminate\Support\Facades\File;
use InvalidArgumentException;


/**
 * Setup Class
 *
 * @version     2.0.0 / 2026-05-15
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class FilePublisher
{


/* +++ TRAITS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ CLASS CONSTANTS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     string STATUS_DONE
     */
    const STATUS_DONE    = 'DONE';


    /**
     * @var     string STATUS_MISSING
     */
    const STATUS_MISSING = 'MISSING';


    /**
     * @var     string STATUS_SKIPPED
     */
    const STATUS_SKIPPED = 'SKIPPED';


/* +++ OBJECT MEMBERS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var         string $_basePath
     */
    protected string $_basePath;


    /**
     * @var         null|callable $_callback
     */
    protected $_callback;


    /**
     * @var         array $_files
     */
    protected array $_files;


    /**
     * @var         bool $_force
     */
    protected bool $_force = false;


    /**
     * @var         array<string, string>
     */
    protected array $_replacements = [];


/* +++ CLASS MEMBERS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ OBJECT METHODS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *
     * @param       string $sourceBasePath
     *
     * @param       array $files
     *
     * @param       callable|null $callback
     *
     * @throws      InvalidArgumentException
     *
     * @version     1.0.1 / 2025-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function __construct(string $sourceBasePath, array $files, null|callable $callback = null)
    {
        $sourceBasePath = str_replace('\\', '/', $sourceBasePath);

        if (!File::exists($sourceBasePath)) {
            throw new InvalidArgumentException('Path ['.$sourceBasePath.'] does not exist.');
        }
        if ($callback !== null && !is_callable($callback)) {
            throw new InvalidArgumentException('Invalid callable given.');
        }
        $this->_callback = $callback;
        $this->_basePath = $sourceBasePath;
        $this->_files = $files;

    } // __construct()


    /**
     *
     * @param       string $status
     *
     * @param       string $message
     *
     * @param       string $source
     *
     * @param       string $target
     *
     * @return      void
     *
     * @version     1.0.0 / 2025-06-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    protected function _emit(string $status, string $message, string $source = '', string $target = ''): void
    {
        if ($this->_callback !== null) {
            call_user_func($this->_callback, [
                'status' => $status,
                'message' => $message,
                'source' => $source,
                'target' => $target,
            ]);
        }
    } // _emit()


    /**
     *
     * @return      self
     *
     * @version     1.0.0 / 2025-06-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function force(): self
    {
        $this->_force = true;

        return $this;

    } // force()


    /**
     * Registers placeholder replacements applied to every published file.
     *
     * Placeholders use {{KEY}} syntax. Files that contain none of the
     * placeholders are written unchanged.
     *
     * @param       array<string, string> $replacements   Keys are placeholders, values are replacements.
     *
     * @return      self
     *
     * @version     1.0.0 / 2026-05-15
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function withReplacements(array $replacements): self
    {
        $this->_replacements = $replacements;

        return $this;

    } // withReplacements()


    /**
     *
     * @return      void
     *
     * @version     1.1.0 / 2026-05-15
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function publish(): void
    {
        foreach ($this->_files as $source => $target) {
            $sourceFile     = $this->_basePath.'/'.$source;
            $sourceFileName = basename($source);
            $targetFile     = base_path($target);
            $targetPath     = dirname($targetFile);

            if (!File::exists($sourceFile)) {
                $this->_emit(self::STATUS_MISSING, 'Missing source', $sourceFileName, $target);

                continue;
            }
            if ($this->_force === false && File::exists($targetFile)) {
                $this->_emit(self::STATUS_SKIPPED, 'Target exists', $sourceFileName, $target);

                continue;
            }
            File::ensureDirectoryExists($targetPath);

            $content = File::get($sourceFile);

            if (!empty($this->_replacements)) {
                $content = str_replace(
                    array_keys($this->_replacements),
                    array_values($this->_replacements),
                    $content
                );
            }
            File::put($targetFile, $content);

            $this->_emit(self::STATUS_DONE, 'Published', $sourceFileName, $target);
        }
    } // publish()


/* +++ CLASS METHODS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


} // class FilePublisher {}
