#!/usr/bin/env php
<?php

/**
 * This Software is the property of Data Development and is protected
 * by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * https://www.d3data.de
 *
 * @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
 * @author    D3 Data Development - Daniel Seifert <support@shopmodule.com>
 * @link      https://www.oxidmodule.com
 */

use D3\ModCfg\Application\Model\d3bitmask;
use D3\ModCfg\Application\Model\d3cliutils;
use D3\ModCfg\Application\Model\d3database;
use D3\ModCfg\Application\Model\Log\d3log;
use D3\ModCfg\Application\Model\Log\d3LogLevel;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\DateIntervalUnit;
use Exception as ExceptionAlias;
use OxidEsales\ComposerPlugin\Installer\Package\ShopPackageInstaller;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Configuration\Bridge\ShopConfigurationDaoBridgeInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use splitbrain\phpcli\CLI;
use splitbrain\phpcli\Exception;
use splitbrain\phpcli\Options;

// @codeCoverageIgnoreStart

require_once(__DIR__.'/../../../../../vendor/autoload.php');

$bootstrapFileName = getenv('ESHOP_BOOTSTRAP_PATH');
if (!empty($bootstrapFileName)) {
    $bootstrapFileName = realpath(trim(getenv('ESHOP_BOOTSTRAP_PATH')));
} else {
    $count = 0;
    $bootstrapFileName = '../../../'. ShopPackageInstaller::SHOP_SOURCE_DIRECTORY .'/bootstrap.php';
    $currentDirectory = __DIR__ . '/';
    while ($count < 5) {
        $count++;
        if (file_exists($currentDirectory . $bootstrapFileName)) {
            $bootstrapFileName = $currentDirectory . $bootstrapFileName;
            break;
        }
        $bootstrapFileName = '../' . $bootstrapFileName;
    }
}

if (!(file_exists($bootstrapFileName) && !is_dir($bootstrapFileName))) {
    $items = [
        "Unable to find eShop bootstrap.php file.",
        "You can override the path by using ESHOP_BOOTSTRAP_PATH environment variable.",
        "\n"
    ];

    $message = implode(" ", $items);

    die($message);
}

if (false === defined('OXID_PHP_UNIT')) {
    require_once($bootstrapFileName);

    // required for recalculating order and generating pdf
    define('OX_IS_ADMIN', true);
}

if (false == function_exists('isAdmin')) {
    /**
     * @return bool
     */
    function isAdmin()
    {
        if (defined('OX_IS_ADMIN')) {
            return OX_IS_ADMIN;
        }

        return true;
    }
}

// set language
$langs = getopt(null, ["lang:"]);
$searchedValue = $langs['lang'] ?? '';
$languages = Registry::getLang()->getLanguageArray();

Registry::getLang()->setTplLanguage(
    current(
        array_merge(
            array_filter(   // selected
                $languages,
                function ($e) use (&$searchedValue) {
                    return $e->abbr == $searchedValue;
                }
            ),
            [reset($languages)] // first item, if no language is selected
        )
    )->id
);
// @codeCoverageIgnoreEnd

class d3maintenance extends CLI
{
    const OPTION_VERSION = 'version';
    const OPTION_QUIET = 'quiet';
    const OPTION_LANG = 'lang';

    const COMMAND_CLEANUPLOG = 'clearlog';
    const COMMAND_LOGSTATUS = 'logstatus';

    const ARGUMENT_CLEANUP_VALUE = 'value';
    const ARGUMENT_CLEANUP_UNIT  = 'unit';

    public function __construct()
    {
        // there are argv setting in CLI mode only
        if ($this->isCLI()) {
            parent::__construct();
        }
    }

    /**
     * @return bool
     */
    public function isCLI()
    {
        return 'cli' == php_sapi_name();
    }

    /**
     * @param Options $options
     * @throws \Exception
     */
    protected function setup(Options $options)
    {
        $lang = Registry::getLang();

        $sLangList = implode(
            $lang->translateString('D3_LOG_CLI_ARGUMENT_ENCLOSER'),
            array_map(
                function ($e) {
                    return $e->abbr;
                },
                Registry::getLang()->getLanguageArray()
            )
        );

        $sUnitList = implode(
            $lang->translateString('D3_LOG_CLI_ARGUMENT_ENCLOSER'),
            $this->getTimeUnitList()
        );

        $options->registerArgument(self::ARGUMENT_CLEANUP_VALUE, $lang->translateString('D3_LOG_CLI_ARGUMENT_VALUE'), false);
        $options->registerArgument(self::ARGUMENT_CLEANUP_UNIT, sprintf($lang->translateString('D3_LOG_CLI_ARGUMENT_UNIT'), $sUnitList), false);

        $options->setHelp($lang->translateString('D3_LOG_CLI_HELP'));
        $options->registerOption(self::OPTION_VERSION, $lang->translateString('D3_LOG_CLI_OPTION_VERSION'), 'v');
        $options->registerOption(self::OPTION_QUIET, $lang->translateString('D3_LOG_CLI_OPTION_QUIET'), 'q');
        $options->registerOption(self::OPTION_LANG, sprintf($lang->translateString('D3_LOG_CLI_OPTION_LANG'), $sLangList), null, 'language');

        $options->registerCommand(self::COMMAND_CLEANUPLOG, $lang->translateString('D3_LOG_CLI_COMMAND_CLEANUPLOG'));
        $options->registerCommand(self::COMMAND_LOGSTATUS, $lang->translateString('D3_LOG_CLI_COMMAND_LOGSTATUS'));
    }

    /**
     * retranslate default messages
     */
    protected function parseOptions()
    {
        $lang = Registry::getLang();

        parent::parseOptions();

        $this->options->registerOption('help', $lang->translateString('D3_LOG_CLI_OPTION_HELP'), 'h');
        $this->options->registerOption('no-colors', $lang->translateString('D3_LOG_CLI_OPTION_NOCOLORS'));
        $this->options->registerOption('loglevel', $lang->translateString('D3_LOG_CLI_OPTION_LOGLEVELS'), null, 'level');
    }

    /**
     * @param Options $options
     * @throws \Exception
     */
    protected function main(Options $options)
    {
        if ( $options->getOpt( self::OPTION_VERSION ) ) {
            $container = ContainerFactory::getInstance()->getContainer();
            $shopConfiguration = $container->get(ShopConfigurationDaoBridgeInterface::class)->get();
            $moduleConfiguration = $shopConfiguration->getModuleConfiguration('d3modcfg_lib');
            $this->info($moduleConfiguration->getVersion());

            return;
        }

        if ( $options->getOpt( self::OPTION_QUIET ) ) {
            d3GetOxidDIC()->get('d3ox.modcfg.'.Session::class)->setVariable( 'd3modcfg_quiet', true );
        }

        $arguments = $options->getArgs();
        $aTranslation         = [];
        $aTranslation['value']  = isset( $arguments[0] ) ? trim($arguments[0]) : '';
        $aTranslation['unit']  = isset( $arguments[1] ) ? trim($arguments[1]) : '';

        $_GET = array_merge( $_GET, $aTranslation );

        try {
            $value = $aTranslation['value'];
            $unit = $aTranslation['unit'];

            if (((bool) strlen($value) || (bool) strlen($value)) &&
                (
                    false === is_numeric($value) ||
                    false === in_array(strtoupper($unit), array_map('strtoupper', $this->getTimeUnitList()), true)
                )
            ){
                throw oxNew(RuntimeException::class, Registry::getLang()->translateString('D3_LOG_CLI_UNVALID_PARAMETERS'));
            }

            switch ( $options->getCmd() ) {
                case self::COMMAND_CLEANUPLOG:
                    $this->clearlog( $value, $unit, $options );
                    break;
                case self::COMMAND_LOGSTATUS:
                    $this->logstatus( $value, $unit );
                    break;
                default:
                    echo $this->translateFixedStrings( $options->help() );
            }
        } catch ( \Exception $oEx ) {
            if (!Registry::getSession()->getVariable('d3modcfg_quiet')) {
                $this->error( $oEx->getMessage() );
            }
        } finally {
            /** @var Config $config */
            $config = d3GetOxidDIC()->get( 'd3ox.modcfg.' . Config::class );
            // @codeCoverageIgnoreStart
            if (false === defined('OXID_PHP_UNIT')) {
                $config->pageClose();
            }
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * @param int     $value
     * @param string  $unit
     * @param Options $options
     *
     * @throws DatabaseConnectionException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    protected function clearlog($value, $unit, Options $options)
    {
        $lang = Registry::getLang();

        if ( (bool) strlen( $value ) || (bool) strlen( $value ) ) {
            $queryBuilder = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
            $queryBuilder->delete( d3GetOxidDIC()->get( 'd3.modcfg.log_get' )->getCoreTableName() )
                         ->where( 'oxtime < DATE_SUB(NOW(), INTERVAL ' . $queryBuilder->createNamedParameter( $value, ParameterType::INTEGER ) . ' ' . $unit . ')' );

            $this->debug( d3database::getInstance()->getPreparedStatementQuery( $queryBuilder->getSQL(), $queryBuilder->getParameters() ) );

            $affected = $queryBuilder->execute()->columnCount();

            if ( ! $options->getOpt( self::OPTION_QUIET ) ) {
                $this->info( sprintf( $lang->translateString( 'D3_LOG_CLI_FINISHED_AFFECTED' ), $affected ) );
                $this->success( $lang->translateString( 'D3_LOG_CLI_FINISHED_SUCCFESSFULLY' ) );
            }
        } else {
            throw oxNew( RuntimeException::class, $lang->translateString( 'D3_LOG_CLI_UNVALID_PARAMETERS' ) );
        }
    }

    /**
     * @param $value
     * @param $unit
     *
     * @throws ContainerExceptionInterface
     * @throws DatabaseConnectionException
     * @throws NotFoundExceptionInterface
     */
    protected function logstatus($value, $unit)
    {
        $lang = Registry::getLang();

        $queryBuilder = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();

        $queryBuilder->select('count(*)', 'oxlogtype', 'DATE_FORMAT(OXTIME, \'%Y-%m\')')
                     ->from(d3GetOxidDIC()->get('d3.modcfg.log_get')->getCoreTableName())
                     ->groupBy('DATE_FORMAT(OXTIME, \'%Y-%m\')')
                     ->addGroupBy('OXLOGTYPE')
                     ->orderBy('DATE_FORMAT(OXTIME, \'%Y-%m\')', 'DESC')
                     ->addOrderBy("FIELD(OXLOGTYPE, '".$this->getLogTypeListString()."')");

        if ((bool) strlen($value) || (bool) strlen($value)) {
            $queryBuilder->where(
                'oxtime < DATE_SUB(NOW(), INTERVAL ' .
                $queryBuilder->createNamedParameter( $value, ParameterType::INTEGER ) . ' ' . $unit . ')'
            );
        }

        $this->debug(d3database::getInstance()->getPreparedStatementQuery($queryBuilder->getSQL(), $queryBuilder->getParameters()));

        $result = array_merge(
            ['-1' => [
                $lang->translateString('D3_LOG_CLI_COMMAND_LOGSTATUS_COUNT'),
                $lang->translateString('D3_LOG_CLI_COMMAND_LOGSTATUS_TYPE'),
                $lang->translateString('D3_LOG_CLI_COMMAND_LOGSTATUS_DATE')
            ]],
            $queryBuilder->execute()->fetchAllNumeric()
        );

        /** @var d3cliutils $cliUtils */
        $cliUtils = oxNew(d3cliutils::class);
        $this->info(PHP_EOL.$cliUtils->table($result, [STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_RIGHT]));
    }

    /**
     * prevent code exit while coverage check
     *
     * @codeCoverageIgnore
     * @throws \Exception
     */
    public function run()
    {
        if (false === $this->isCLI()) {
            throw new Exception(Registry::getLang()->translateString('D3_LOG_CLI_COMMON_RUNFROMCLI'));
        }

        if (false === defined('OXID_PHP_UNIT')) {
            parent::run();
        } else {
            $this->setup($this->options);
            $this->registerDefaultOptions();
            $this->parseOptions();
            $this->handleDefaultOptions();
            $this->setupLogging();
            $this->checkArguments();
            $this->execute();
        }
    }

    /**
     * @param $text
     * @return string
     */
    public function translateFixedStrings($text)
    {
        $search = [
            'This tool accepts a command as first parameter as outlined below:'
        ];

        $replace = [
            Registry::getLang()->translateString('D3_LOG_CLI_COMMAND')
        ];

        return str_replace($search, $replace, $text);
    }

    /**
     * @return array
     */
    public function getLogTypeList()
    {
        /** @var d3log $log */
        $log = d3GetOxidDIC()->get('d3.modcfg.log_get');

        $bitmask = oxNew( d3bitmask::class);
        $range = $bitmask->removeBit(d3LogLevel::ERROR_AND_BELOW, d3LogLevel::EMPTY_AND_BELOW);

        return $log->getLogTypeListByRange($range);
    }

    /**
     * @return string
     */
    public function getLogTypeListString()
    {
        return implode(
            "', '",     // don't use translation item for separator, because possible syntactical incorrect one
            $this->getLogTypeList()
        );
    }

    /**
     * @return array
     */
    public function getTimeUnitList()
    {
        return [ DateIntervalUnit::HOUR, DateIntervalUnit::DAY, DateIntervalUnit::MONTH, DateIntervalUnit::YEAR];
    }
}

// @codeCoverageIgnoreStart
$cli = new d3maintenance();
if (false === defined('OXID_PHP_UNIT')) {
    try {
        $cli->run();
    } catch ( ExceptionAlias $e) {
        $cli->error($e->getMessage());
    }
}
// @codeCoverageIgnoreEnd