<?php
/**
 * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
 */

namespace IMATHUZH\Qfq\Controller;

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

use IMATHUZH\Qfq\Core\Exception\RedirectResponse;
use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Helper\Path;
use IMATHUZH\Qfq\Core\QuickFormQuery;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Typo3\T3Handler;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\PropagateResponseException;


/**
 * Class QfqController
 * @package IMATHUZH\Qfq\Controller
 */
class QfqController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {

    /**
     * @return string
     * @throws \CodeException
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     * @throws \UserFormException
     */
    public function showAction() {

        $origErrorReporting = '';
        $flagOk = false;

        try {
            $contentObject = $this->configurationManager->getContentObject();

            // By T3 default 'E_NOTICE' is unset. E.g. 'Undefined Index' will throw an exception.
            // QFQ like to see those 'E_NOTICE'
            $origErrorReporting = error_reporting();
            error_reporting($origErrorReporting | E_NOTICE);

            $qfq = new QuickFormQuery($contentObject->data);
            $html = $qfq->process();
            $flagOk = true;

            $this->websocketStart();

        } catch (\UserFormException $e) {
            $html = $e->formatMessage();

        } catch (\UserReportException $e) {
            $html = $e->formatMessage();

        } catch (\CodeException $e) {
            $html = $e->formatMessage();

        } catch (\DbException $e) {
            $html = $e->formatMessage();

        } catch (\ShellException $e) {
            $html = $e->formatMessage();

        } catch (\DownloadException $e) {
            $html = $e->formatMessage();

        } catch (RedirectResponse $e) {
            $this->triggerRedirection($e->getLocationUrl(), $e->getCode());

        } catch (\Exception $e) {
            $ee = new \UserReportException(json_encode([
                ERROR_MESSAGE_TO_USER => "Generic Exception: " . $e->getMessage(),
                ERROR_MESSAGE_TO_DEVELOPER => $e->getTraceAsString()]), E_ERROR);
            $html = $ee->formatMessage();
        } catch (\Throwable $e) {
            $ee = new \UserReportException(json_encode([
                ERROR_MESSAGE_TO_USER => "Generic Error: " . $e->getMessage(),
                ERROR_MESSAGE_TO_DEVELOPER => $e->getTraceAsString()]), E_ERROR);
            $html = $ee->formatMessage();
        }

        if (isset($e) && $e->getCode() == ERROR_QUIT_QFQ_REGULAR) {
            $flagOk = true;
        }

        if (!$flagOk) {
            $html = "<div class='alert alert-warning'>$html</div>";
        }

        // Restore has to be outside of try/catch - E_NOTICE needs to unset for further T3 handling after an QFQ Exception.
        error_reporting($origErrorReporting);

        $this->view->assign('qfqOutput', $html);
        $content = $this->view->render();

        // Compatibility with T3 v11 and higher.
        if (T3Handler::typo3VersionGreaterEqual11()) {
            $content = new HtmlResponse($content);
        }

        return $content;
    }

    private function websocketStart() {
        // Websocket preparations for non docker environment
        $scriptName = Path::absoluteApp(Path::appToApi('websocket.php'));
        $command = 'nohup php ' . $scriptName;
        $logFile = Path::absoluteWebsocketLogFile();
        $websocketKillCmd = 'pkill -f ' . $scriptName;
        $timestamp = date("Y-m-d H:i:s");

        $websocketPort = Store::getVar(SYSTEM_WEBSOCKET_PORT, STORE_SYSTEM);

        // Use grep to check if the WebSocket server is already running
        $running = shell_exec("ps aux | grep '$scriptName' | grep -v grep");
        if (empty($running)) {
            $running = false;
        }

        if (!empty($websocketPort) && !$running) {
            // Execute command and redirect its output to the log file
            shell_exec($command . ' >> ' . $logFile . ' 2>&1 &');
        } elseif (empty($websocketPort) && $running) {
            shell_exec($websocketKillCmd . ' >> ' . $logFile . ' 2>&1');

            $message = "WebSocket server shut down.";
            Logger::logMessage($timestamp . ' - ' . $message, $logFile);
        }
    }

    /**
     * Triggers an HTTP redirection
     *
     * @param string $targetUrl
     * @param int $statusCode
     * @return mixed
     * @throws PropagateResponseException
     */
    private function triggerRedirection(string $targetUrl, int $statusCode) {
        $response = $this->responseFactory->createResponse($statusCode)
            ->withHeader('Location', $targetUrl);
        throw new PropagateResponseException($response);
    }

}