<?php
/**
 * ComposerJson Class
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Composer
 * @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\Composer;


use BplanBase\Globals\Json\JsonFile;


/**
 * ComposerJson Class
 *
 * Reads, manipulates and writes composer.json / composer.local.json files.
 * All mutating methods return $this for fluent chaining; call save() to persist.
 *
 * @version     3.0.0 / 2026-04-26
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class ComposerJson extends JsonFile
{


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


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


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


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


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


    /**
     * Adds a file path to autoload.files.
     *
     * Does nothing if the path is already present.
     *
     * @param       string $file
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-26
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function addAutoloadFile(string $file): self
    {
        if (!isset($this->_data['autoload']['files'])) {
            $this->_data['autoload']['files'] = [];
        }
        if (!in_array($file, $this->_data['autoload']['files'], true)) {
            $this->_data['autoload']['files'][] = $file;
        }
        return $this;

    } // addAutoloadFile()


    /**
     * Adds a package to require or require-dev.
     *
     * Does nothing if the package is already present in the target section.
     *
     * @param       string $package
     *
     * @param       string $constraint
     *
     * @param       bool   $dev
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function addPackage(string $package, string $constraint = '*', bool $dev = false): self
    {
        $key = $dev === true ? 'require-dev' : 'require';

        if (!isset($this->_data[$key])) {
            $this->_data[$key] = [];
        }
        if (!isset($this->_data[$key][$package])) {
            $this->_data[$key][$package] = $constraint;
        }
        return $this;

    } // addPackage()


    /**
     * Adds a repository entry.
     *
     * Does nothing if a repository with the same name already exists.
     *
     * @param       string $name
     *
     * @param       string $type
     *
     * @param       string $url
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function addRepository(string $name, string $type, string $url): self
    {
        if (!isset($this->_data['repositories'])) {
            $this->_data['repositories'] = [];
        }
        if (!isset($this->_data['repositories'][$name])) {
            $this->_data['repositories'][$name] = [
                'type' => $type,
                'url'  => $url,
            ];
        }
        return $this;

    } // addRepository()


    /**
     * Adds a command to a composer script event.
     *
     * Does nothing if the command is already present for that event.
     *
     * @param       string $event
     *
     * @param       string $command
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function addScript(string $event, string $command): self
    {
        if (!isset($this->_data['scripts'][$event])) {
            $this->_data['scripts'][$event] = [];
        }
        if (!in_array($command, $this->_data['scripts'][$event], true)) {
            $this->_data['scripts'][$event][] = $command;
        }
        return $this;

    } // addScript()


    /**
     * Checks whether a file path is present in autoload.files.
     *
     * @param       string $file
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-26
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasAutoloadFile(string $file): bool
    {
        return in_array($file, $this->_data['autoload']['files'] ?? [], true);

    } // hasAutoloadFile()


    /**
     * Checks whether a package exists in require, require-dev, or both.
     *
     * @param       string    $package
     *
     * @param       bool|null $dev
     *              null = both, false = require only, true = require-dev only
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasPackage(string $package, bool|null $dev = null): bool
    {
        if ($dev === false || $dev === null) {
            if (isset($this->_data['require'][$package])) {
                return true;
            }
        }
        if ($dev === true || $dev === null) {
            if (isset($this->_data['require-dev'][$package])) {
                return true;
            }
        }
        return false;

    } // hasPackage()


    /**
     * Checks whether a repository with the given name exists.
     *
     * @param       string $name
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasRepository(string $name): bool
    {
        return isset($this->_data['repositories'][$name]);

    } // hasRepository()


    /**
     * Checks whether a command is registered for a script event.
     *
     * @param       string $event
     *
     * @param       string $command
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function hasScript(string $event, string $command): bool
    {
        return in_array($command, $this->_data['scripts'][$event] ?? [], true);

    } // hasScript()


    /**
     * Removes a package from require and/or require-dev.
     *
     * @param       string $package
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function removePackage(string $package): self
    {
        unset($this->_data['require'][$package]);
        unset($this->_data['require-dev'][$package]);

        return $this;

    } // removePackage()


    /**
     * Removes a repository entry by name.
     *
     * @param       string $name
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function removeRepository(string $name): self
    {
        unset($this->_data['repositories'][$name]);

        return $this;

    } // removeRepository()


    /**
     * Removes a command from a composer script event.
     *
     * @param       string $event
     *
     * @param       string $command
     *
     * @return      $this
     *
     * @version     1.0.0 / 2026-04-19
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function removeScript(string $event, string $command): self
    {
        if (!isset($this->_data['scripts'][$event])) {
            return $this;
        }
        $this->_data['scripts'][$event] = array_values(
            array_filter($this->_data['scripts'][$event], fn($c) => $c !== $command)
        );

        return $this;

    } // removeScript()


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


} // class ComposerJson extends JsonFile {}
