<?php
namespace Teknisa\Libs\Service;

use Teknisa\Libs\Util\Utilities;
use Zeedhi\Framework\Session\Session;
use Teknisa\Libs\Util\Login\UserData;
use Teknisa\Libs\Util\InstanceProvider;
use Teknisa\Libs\Exception\CustomException;
use Teknisa\Libs\Exception\Login as LoginException;

class Api {
    /** @var Session $sessionManager */
	protected $sessionManager;
    /** @var UserData $userData */
    private $userData;
    /** @var array $userData */
    protected $config;

    const DEFAULT_TRANSLATE_URL = 'https://translate.teknisa.com/backend/index.php';
    const DEFAULT_FILE_NAME     = 'translation';
    const DEFAULT_WORDS_PATH    = 'mobile/assets/json/words';

    /**
     * @param Session $sessionManager
     * @param UserData $userData
     */
    public function __construct(Session $sessionManager, UserData $userData) {
        $this->sessionManager = $sessionManager;
        $this->userData = $userData;
        $this->config = json_decode(file_get_contents(__DIR__ . '/../../../../config/config.json'), true);
    }

    /**
     * @param $nrOrg
     * @param $cdOperator
     */
    public function setSession($nrOrg, $cdOperator)
    {
        $this->sessionManager->set('NRORG', $nrOrg);
        $this->sessionManager->set('CDOPERADOR', $cdOperator);
    }

    /**
     * @param $user
     * @param $password
     * @return array
     */
    public function getUserData($user, $password) {
        return $this->userData->getUserData($user, $password);
    }

    /**
     * @param $email
     * @param $password
     * @param $identification
     * @return array
     */
    public function updatePassword($email, $password, $identification) {
        $this->userData->updatePassword($email, $password, $identification);
    }

    /**
     * @param $user
     * @param $password
     * @return array
     */
    public function validateUser($user, $password) {
        return $this->userData->validateUser($user, $password);
    }

    /**
     * @param bool $ignoreBowerNotFound
     * @return array
     */
    public function getModulesVersion($ignoreBowerNotFound = false) {
        $productBasePath = Utilities::getProductBasePath();
        $modules = @file_get_contents($productBasePath . $this->config['modulesPath']);
        if (!$modules) {
            LoginException::modulesConfigNotFound();
        }

        $modules = json_decode($modules, true);
        $modulesVersion = $bowersNotFound = $modulesParametersNotFound = [];
        foreach($modules as $key => $module) {
            if(!isset($module['onlyControl'])) {
                if($key == Utilities::PRODUCT_MENU_NAME) {
                    $bowerFile = @file_get_contents($productBasePath . 'mobile/bower.json');
                } else {
                    $bowerFile = @file_get_contents($productBasePath . 'modules/' . $key . '/mobile/bower.json');
                    if(!$bowerFile) {
                        $bowerFile = @file_get_contents($productBasePath . 'modules/' . $key . '/productInfo.json');
                    }
                }
                if(!$bowerFile && !$ignoreBowerNotFound) {
                    $bowersNotFound[] = $key;
                } else if((!isset($module['maxVersion']) && $key != Utilities::PRODUCT_MENU_NAME) || !isset($module['devOpsId'])) {
                    $modulesParametersNotFound[] = $key;
                } else {
                    $bowerFile = json_decode($bowerFile, true);
                    if($key != Utilities::PRODUCT_MENU_NAME && isset($bowerFile['product'])) {
                        $bowerFile = $bowerFile['product'];
                    }
                    $modulesVersion[] = array(
                        'KEY'         => $key,
                        'NAME'        => isset($bowerFile['name']) ? $bowerFile['name'] : '',
                        'VERSION'     => $ignoreBowerNotFound && !$bowerFile ? '-' : $bowerFile['version'],
                        'MAX_VERSION' => $key != Utilities::PRODUCT_MENU_NAME ? $module['maxVersion'] : '*',
                        'DEVOPS_ID'   => $module['devOpsId']
                    );
                }
            }
        }

        if (!empty($bowersNotFound)) {
            LoginException::bowerConfigModuleNotFound(join(', ', $bowersNotFound));
        }
        if (!empty($modulesParametersNotFound)) {
            LoginException::moduleParametersNotFound(join('",<br/>"', $modulesParametersNotFound));
        }
        return $modulesVersion;
    }

    /**
     * @return string
     */
    public function generateModulesVersionFile() {
        $modulesVersion = $this->getModulesVersion();
        $modulesVersion = json_encode($modulesVersion);
        $generatedFile  = Utilities::getGeneratedFile();
        file_put_contents($generatedFile, $modulesVersion);
        $generatedFile = explode('/', $generatedFile);
        return array_pop($generatedFile);
    }

    public function downloadLoginAccessFile($fileName) {
        Utilities::downloadAndRemoveFile(Utilities::getGeneratedFileDir() . '/' . $fileName, $fileName);
    }

    public function setLastRequestTime($route) {
        if($this->sessionManager->isStarted() && $route != '/lib_validateExpiredSession') {
            $date = new \DateTime();
            $this->sessionManager->set('LAST_REQUEST_TIME', $date->getTimestamp());
        }
    }

    public function updateTranslation() {
        $ds = DIRECTORY_SEPARATOR;
        $productBasePath = Utilities::getProductBasePath();
        $products = $this->getProductsTranslation($productBasePath);
        $response = $this->callWordsWebService($products);

        foreach($products as $module) {
            $productId = $module['id'];
            if(!empty($response[$productId])) {
                $moduleResponse = $response[$productId];
                $this->buildWordFiles($moduleResponse['json'], $productBasePath, $ds, $module);
                if(!empty($moduleResponse['repository']) && $moduleResponse['repository']['NEW_STRUCTURE'] == 'Y') {
                    $this->buildBirtFiles($moduleResponse['birt'], $moduleResponse['repository'], $productBasePath, $ds, $module);
                }
            }
        }
    }

    protected function buildWordFiles($moduleJson, $productBasePath, $ds, $module) {
        $wordsPath = !empty($module['wordsPath']) ? $module['wordsPath'] : self::DEFAULT_WORDS_PATH;
        $path = $this->getWordsBasePath($productBasePath, $wordsPath, $ds, $module);
        if($path) {
            $fileData = array();
            foreach($moduleJson as $word) {
                $wordValue = $word['WORDS_VALUE'];
                if(!is_numeric($wordValue) && !empty(trim($wordValue))) {
                    $fileData[strtolower($word['LANGUAGE_ISO'])][$wordValue] = $word['VALUE'];
                }
            }
            
            foreach($fileData as $fileName => $fileTranslation) {
                $filePath = $path . $ds . $fileName . '.json';
                file_put_contents($filePath, json_encode($fileTranslation, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
            }
        }
    }

    protected function buildBirtFiles($moduleBirt, $repository, $productBasePath, $ds, $module) {
        $wordsPath = $repository['WORDS_PATH'];
        $messagesJson = $repository['MESSAGES_FILE'];
        if(!empty($module['birtBasePath'])) {
            $wordsPath  = !empty($module['birtWordsPath']) ? $module['birtWordsPath'] : $module['birtBasePath'] . $ds . $repository['WORDS_PATH'];
            if(!empty($module['birtMessagesJson']) || !empty($repository['MESSAGES_FILE'])) {
                $messagesJson = !empty($module['birtMessagesJson']) ? $module['birtMessagesJson'] : $module['birtBasePath'] . $ds . $repository['MESSAGES_FILE'];
            }
        }

        $path = $this->getWordsBasePath($productBasePath, $wordsPath, $ds, $module);
        if($path) {
            $defaultLangKeys = $this->getDefaultLangToUseKeys($moduleBirt);
            $fileData = array();
            $fileTranslationData = '';
            foreach($moduleBirt as $word) {
                $wordValue = $word['WORDS_VALUE'];
                if(!is_numeric($wordValue) && !empty(trim($wordValue))) {
                    $fileData[$word['LANGUAGE_ISO']]  = empty($fileData[$word['LANGUAGE_ISO']]) ? '' : $fileData[$word['LANGUAGE_ISO']];
                    $wordValueEsc = $this->unicodeEscape($wordValue);
                    $fileData[$word['LANGUAGE_ISO']] .= $wordValueEsc . '=' . $this->unicodeEscape($word['VALUE']) . PHP_EOL;
                    if($word['LANGUAGE_ISO'] == $defaultLangKeys) {
                        $fileTranslationData .= $wordValueEsc . '=' . $wordValueEsc . PHP_EOL;
                    }
                }
            }

            foreach($fileData as $langIso => $fileTranslation) {
                $filePath = $path . $ds . self::DEFAULT_FILE_NAME . '_' . $langIso . '.properties';
                file_put_contents($filePath, $fileTranslation);
            }
            $fileTranslationData .= $this->getMessagesJson($productBasePath, $messagesJson, $ds, $repository, $module);
            $fileTranslationPath = $path . $ds . self::DEFAULT_FILE_NAME . '.properties';
            file_put_contents($fileTranslationPath, $fileTranslationData);
        }
    }

    protected function getMessagesJson($productBasePath, $messagesJson, $ds, $repository, $module) {
        $data = '';
        $path = $this->getModuleBasePath($productBasePath, $module, $ds);
        $fileContents = @file_get_contents($path . $ds . $messagesJson);
        if(!empty($fileContents)) {
            $messages = @json_decode($fileContents, true);
            if(!empty($messages)) {
                foreach($messages[$repository['MESSAGES_STRUCTURE']] as $key => $value) {
                    $data .= $this->unicodeEscape($key) . '=' . $this->unicodeEscape($value) . PHP_EOL;
                }
            }
        }
        return $data;
    }

    protected function getWordsBasePath($productBasePath, $wordsPath, $ds, $module) {
        $path = $this->getModuleBasePath($productBasePath, $module, $ds);
        if(!is_dir($path)) {
            return false;
        }
        $path .= $ds . $wordsPath;
        if(!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        return $path;
    }

    protected function getModuleBasePath($productBasePath, $module, $ds) {
        return $productBasePath . ($module['path'] == 'product' ? '' : $ds . 'modules' . $ds . $module['path']);
    }

    protected function unicodeEscape($string) {
        $newStr = '';
        $regex = '/[^0-9|^a-z]/i';
        $chars = $this->strSplitUnicode($string);
        foreach($chars as $char) {
            if(preg_match($regex, $char)) {
                $enconding = mb_convert_encoding($char, 'UTF-32BE', 'UTF-8');
                $newStr .= "\u" . substr(bin2hex($enconding), -4);
            } else {
                $newStr .= $char;
            }
        }
        return $newStr;
    }

    protected function strSplitUnicode($str, $l = 1) {
        if ($l > 0) {
            $ret = array();
            $len = mb_strlen($str, "UTF-8");
            for ($i = 0; $i < $len; $i += $l) {
                $ret[] = mb_substr($str, $i, $l, "UTF-8");
            }
            return $ret;
        }
        return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
    }

    protected function getDefaultLangToUseKeys($moduleBirt) {
        foreach($moduleBirt as $word) {
            if($word['LANGUAGE_ISO'] == 'es_CL' || $word['LANGUAGE_ISO'] == 'en_US') {
                return $word['LANGUAGE_ISO'];
            }
        }
        return 'en_US';
    }

    protected function getProductsTranslation($productBasePath) {
        $products = array();
        $modules = @file_get_contents($productBasePath . $this->config['modulesPath']);
        if (!$modules) {
            LoginException::modulesConfigNotFound();
        }
        $modules = json_decode($modules, true);
        foreach($modules as $key => $module) {            
            if(empty($module['onlyControl']) && empty($module['nextProduct']) && empty($module['nextModule'])) {
                $currentModule = $module;
                $currentModule['id'] = $module['devOpsId'];
                $currentModule['path'] = $key;
                $currentModule['repositoryId'] = !empty($module['repositoryId']) ? $module['repositoryId'] : null;
                $products[] = $currentModule;
            }
        }
        return $products;
    }

    protected function callWordsWebService($products) {
        $url = strpos($this->config['translateUrl'], '[TRANSLATE_URL]') !== false ? self::DEFAULT_TRANSLATE_URL : $this->config['translateUrl'];
        $params = array(
            "requestType" => "Row",
            "row" => array(
                'products' => $products,
                'organizationId' => 20,
            )
        );
        return Utilities::callWebService(null, '/getWordsByModules', $params, 'getWordsByModules', $this->config['loginClientSecret'], $url);
    }

    public function openReport($key)
    {
        $filter = array(
            'KEY' => $key
        );
        $reportData = InstanceProvider::getMongoDB()->find('ZhReportData', $filter);

        if(empty($reportData)) {
            echo '<h3>Relatório não encontrado. <br> Utilize o sistema para gerar novamente.<h3>';
            die;
        } else {
            $format = !empty($reportData[0]['FORMAT']) ? $reportData[0]['FORMAT'] : '';
            $url    = $reportData[0]['URL'];
            
            if(strtolower($format) == 'html') {
                echo file_get_contents($url);
            } else {
                $name = $reportData[0]['NAME'] . '.pdf';
                header('Accept-Ranges: none');
                header("Content-Type: application/pdf");
                header("Content-Disposition: inline; filename=" . $name);
                readfile($url);
            }
            InstanceProvider::getMongoDB()->remove('ZhReportData', $filter);
            exit;
        }
    }

    public function downloadPHPErrorLog($token, $filename) {
        $this->checkAccessToken($token);
        $file = Utilities::getPHPErrorLogPath() . DIRECTORY_SEPARATOR . $filename;
        if (!is_file($file)) {
            throw new \Exception('Arquivo de log não encontrado.', 500);
        }
        header('Accept-Ranges: none');
        header("Content-Type: text/plain");
        header('Content-Length: ' . filesize($file));
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        readfile($file);
        exit;
    }

    private function checkAccessToken($token) {
        $access  = InstanceProvider::getOAuthCustom()->checkAccess($token, '', array(), 'Yes');
        $session = InstanceProvider::getSession();
        if($session->getId() != $access['sessionId']) {
            $session->start();
            $session->destroy();
            $session->setId($access['sessionId']);
        }
        $session->start();

        $isSupOperator = $session->has('IS_SUPPORT_OPERATOR') ? $session->get('IS_SUPPORT_OPERATOR') : false;
        $isTeknisaUser = $session->has('IS_TEKNISA_USER')     ? $session->get('IS_TEKNISA_USER')     : false;
        if(!$isSupOperator && !$isTeknisaUser) {
            throw new \Exception('Rotina disponível apenas para operadores de suporte da Teknisa', 500);
        }
    }

    public function getPHPErrorLogFiles() {
        $data = array();
        $path = Utilities::getPHPErrorLogPath();
        if (!is_dir($path)) {
            throw new CustomException('PHP_ERROR_LOG_PATH_NOT_FOUND', 500);
        }
        $files = array_diff(scandir($path), array('.', '..'));
        if(empty($files)) {
            throw new CustomException('PHP_ERROR_LOG_FILES_NOT_FOUND', 500);
        }
        foreach($files as $file) {
            $filePath = $path . DIRECTORY_SEPARATOR . $file;
            if(is_file($filePath)) {
                $data[] = array(
                    'NAME' => $file,
                    'SIZE' => Utilities::formatSizeUnits(filesize($filePath)),
                    'LAST_MODIFIED' => date("d/m/Y H:i:s", filemtime($filePath))
                );
            }
        }
        return $data;
    }

    public function chatBotQuery($sql, $sqlParams) {
        return InstanceProvider::getEntityManager()->getConnection()->fetchAllAssociative($sql, $sqlParams);
    }
}