<?php
/**
 * Mirasvit
 *
 * This source file is subject to the Mirasvit Software License, which is available at https://mirasvit.com/license/.
 * Do not edit or add to this file if you wish to upgrade the to newer versions in the future.
 * If you wish to customize this module for your needs.
 * Please refer to http://www.magentocommerce.com for more information.
 *
 * @category  Mirasvit
 * @package   mirasvit/module-rma
 * @version   2.2.27
 * @copyright Copyright (C) 2023 Mirasvit (https://mirasvit.com/)
 */



namespace Mirasvit\Rma\Model;

use Mirasvit\Rma\Helper\Ruleevent;
use Magento\Framework\Filesystem;
use Psr\Log\LoggerInterface;

class Cron
{
    private $rmaRuleevent;

    private $filesystem;

    private $logger;

    private $output;

    /**
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        Ruleevent $rmaRuleevent,
        Filesystem $filesystem,
        LoggerInterface $logger
    ) {
        $this->rmaRuleevent = $rmaRuleevent;
        $this->filesystem   = $filesystem;
        $this->logger       = $logger;
    }

    /**
     * @var null
     */
    private $lockFilePath = null;

    /**
     * @var null
     */
    protected $_lockFile = null;

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

    public function magentoCronEveryHourRun(): void
    {
        $this->rmaRuleevent->newEventCheck('cron_every_hour');
    }

    public function shellCronRun($output = null): void
    {
        $this->output = $output;
        $this->run();
    }

    public function setFast($fast): void
    {
        $this->_fast = $fast;
    }

    public function run(): void
    {
        set_time_limit(60 * 30); //30 min. we need this. otherwise script can hang out.
        if (!$this->isLocked() || $this->_fast) {
            $this->lock();

            $this->magentoCronEveryHourRun();

            $this->unlock();
        } else {
            if ($this->output) {
                $this->output->writeln('Process is locked');
            } else {
                $this->logger->info(__('Process is locked'));
            }
        }
    }


    /**
     * @return bool|resource|null
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    protected function _getLockFile()
    {
        if ($this->_lockFile === null) {
            $file = $this->getFilePath();
            if (is_file($file)) {
                $this->_lockFile = fopen($file, 'w');
            } else {
                $this->_lockFile = fopen($file, 'x');
            }
            fwrite($this->_lockFile, date('r'));
        }

        return $this->_lockFile;
    }

    private function getFilePath(): string
    {
        if ($this->lockFilePath === null) {
            $varDir = $this->filesystem
                ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::TMP)
                ->getAbsolutePath();
            if (!file_exists($varDir)) {
                mkdir($varDir, 0777, true);
            }
            $this->lockFilePath = $varDir . '/rma.lock';
        }

        return (string)$this->lockFilePath;
    }

    /**
     * Lock file. File will unlock if process was terminated
     * @return $this
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function lock(): object
    {
        flock($this->_getLockFile(), LOCK_EX | LOCK_NB);

        return $this;
    }

    /**
     * Lock and block process.
     * If new instance of the process will try validate locking state
     * script will wait until process will be unlocked.
     * @return Cron
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function lockAndBlock(): object
    {
        flock($this->_getLockFile(), LOCK_EX);

        return $this;
    }

    public function unlock(): object
    {
        flock($this->_getLockFile(), LOCK_UN);

        return $this;
    }

    public function forceUnlock(): object
    {
        $fp = $this->_getLockFile();
        flock($fp, LOCK_UN);
        fclose($fp);
        $this->_lockFile = null;
        unlink($this->getFilePath());

        return $this;
    }

    public function isLocked(): bool
    {
        $fp = $this->_getLockFile();
        if (flock($fp, LOCK_EX | LOCK_NB)) {
            flock($fp, LOCK_UN);

            return false;
        }

        return true;
    }

    public function __destruct()
    {
        if ($this->_lockFile) {
            fclose($this->_lockFile);
        }
    }
}
