<?php
// Copyright 1999-2017. Plesk International GmbH. All rights reserved.

namespace Plesk\MigrationLibV8\SiteImport\Backend\Files;

use Plesk\MigrationLibV8\Utils\ArrayUtils;
use Plesk\MigrationLibV8\Utils\FileUtils;

class Status
{
    const OPERATION_CONNECT = 'connect';
    const OPERATION_SCAN_CONTENT = 'scan-content';

    const STATUS_NOT_STARTED = 'not-started';
    const STATUS_SUCCESS = 'success';
    const STATUS_FAILURE = 'failure';

    /** @var string */
    private $_statusFilePath;
    /** @var string */
    private $_siteName;

    private $_siteData;

    public function __construct($statusFilePath, $siteName)
    {
        $this->_statusFilePath = $statusFilePath;
        $this->_siteName = $siteName;
        $this->read();
    }

    public function isOperationFinished($operation)
    {
        return $this->getOperationStatus($operation) != self::STATUS_NOT_STARTED;
    }

    public function isOperationSuccessful($operation)
    {
        return $this->getOperationStatus($operation) == self::STATUS_SUCCESS;
    }

    public function getOperationStatus($operation)
    {
        if (isset($this->_siteData['operation'][$operation]['status'])) {
            return $this->_siteData['operation'][$operation]['status'];
        } else {
            return self::STATUS_NOT_STARTED;
        }
    }

    public function getErrorMessages()
    {
        $errorMessages = [];
        foreach ([self::OPERATION_CONNECT, self::OPERATION_SCAN_CONTENT] as $operation) {
            if (isset($this->_siteData['operation'][$operation]['message'])) {
                $errorMessages[] = $this->_siteData['operation'][$operation]['message'];
            }
        }
        return $errorMessages;
    }

    public function getIssueId()
    {
        foreach ([self::OPERATION_CONNECT, self::OPERATION_SCAN_CONTENT] as $operation) {
            if (isset($this->_siteData['operation'][$operation]['issue_id'])) {
                return $this->_siteData['operation'][$operation]['issue_id'];
            }
        }
        return null;
    }

    /**
     * Get whole set of site status data as array.
     *
     * @return array
     */
    public function getSiteData()
    {
        return $this->_siteData;
    }

    public function resetOperationsStatus()
    {
        if (FileUtils::fileExists($this->getFilePath())) {
            $data = json_decode(FileUtils::fileGetContents($this->getFilePath()), true);
            if (isset($data[$this->_siteName]['operation'])) {
                unset($data[$this->_siteName]['operation']);

                // fix for JSON serialization: avoid empty lists where backend expects empty dictionaries
                if (count($data[$this->_siteName]) == 0) {
                    unset($data[$this->_siteName]);
                }
                if (count($data) == 0) {
                    $data = new \stdClass();
                }

                FileUtils::putFileContents(
                    $this->getFilePath(), \Zend_Json::prettyPrint(\Zend_Json::encode($data))
                );
                // re-read contents of status file
                $this->read();
            }
        }
    }

    /**
     * Set backend error for the last unfinished operation, if it was not already set by backend itself.
     *
     * @param $message
     */
    public function setBackendFailure($message)
    {
        if (FileUtils::fileExists($this->getFilePath())) {
            $data = json_decode(FileUtils::fileGetContents($this->getFilePath()), true);
        } else {
            $data = [];
        }

        // find the last unfinished operation, and set failure for it
        $lastOperation = null;

        foreach ([self::OPERATION_CONNECT, self::OPERATION_SCAN_CONTENT] as $operation) {
            $lastOperation = $operation;
            if (isset($data[$this->_siteName]['operation'][$operation]['status'])) {
                $status = $data[$this->_siteName]['operation'][$operation]['status'];
                if ($status == self::STATUS_FAILURE) {
                    // failure is already set in the status file by backend; consider backend has better
                    // message, so don't change anything in the status file
                    return;
                } else if ($status != self::STATUS_FAILURE && $status != self::STATUS_SUCCESS) {
                    // found operation, for which we have unfinished status - it is the last unfinished operation
                    break;
                }
            } else {
                // found operation which has no status set - it is the last unfinished operation
                break;
            }
        }

        if (!isset($data[$this->_siteName])) {
            $data[$this->_siteName] = [];
        }
        if (!isset($data[$this->_siteName]['operation'])) {
            $data[$this->_siteName]['operation'] = [];
        }
        $data[$this->_siteName]['operation'][$lastOperation] = [
            'status' => self::STATUS_FAILURE,
            'message' => $message
        ];

        FileUtils::putFileContents(
            $this->getFilePath(), \Zend_Json::prettyPrint(\Zend_Json::encode($data))
        );
    }

    /**
     * Get full path to file with the site info.
     *
     * @return string
     */
    private function getFilePath()
    {
        return $this->_statusFilePath;
    }

    public function read()
    {
        if (FileUtils::fileExists($this->getFilePath())) {
            $data = json_decode(FileUtils::fileGetContents($this->getFilePath()), true);
        } else {
            $data = [];
        }

        $data = ArrayUtils::getElementDefaultArray($data, $this->_siteName);

        // Leave issue details only for administrator:
        // - for better security (details could accidentally contain sensitive data)
        // - for better user experience (in most cases user won't understood the details, and there is no sense
        // to show that useless info).
        // So, only administrator could see logs, stack traces, etc.
        if (!\pm_Session::getClient()->isAdmin()) {
            $data = $this->_filterOutDetails($data);
        }

        $this->_siteData = $data;
    }

    /**
     * Remove issue details from migration status data structure
     *
     * @param mixed $data
     * @return mixed
     */
    private function _filterOutDetails($data)
    {
        if (is_array($data)) {
            $result = [];

            foreach ($data as $key => $value) {
                if ($key !== 'details_text') {
                    $result[$key] = $this->_filterOutDetails($value);
                }
            }

            return $result;
        } else {
            return $data;
        }
    }
}
