<?php

/**
 * Copyright Maarch since 2008 under licence GPLv3.
 * See LICENCE.txt file at the root folder for more details.
 * This file is part of Maarch software.
 *
 */

/**
 * @brief Template Controller
 * @author dev@maarch.org
 */

namespace Template\controllers;

use ContentManagement\controllers\MergeController;
use Docserver\controllers\DocserverController;
use Docserver\models\DocserverModel;
use Group\controllers\PrivilegeController;
use History\controllers\HistoryController;
use Resource\controllers\ResController;
use Resource\models\ResModel;
use Respect\Validation\Validator;
use Slim\Http\Request;
use Slim\Http\Response;
use SrcCore\models\CoreConfigModel;
use SrcCore\models\ValidatorModel;
use Template\models\TemplateAssociationModel;
use Template\models\TemplateModel;
use Attachment\models\AttachmentModel;
use Entity\models\EntityModel;
use User\models\UserModel;

class TemplateController
{
    const AUTHORIZED_MIMETYPES = [
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'application/vnd.ms-excel',
        'application/vnd.ms-powerpoint',
        'application/vnd.openxmlformats-officedocument.presentationml‌.slideshow',
        'application/vnd.oasis.opendocument.text',
        'application/vnd.oasis.opendocument.presentation',
        'application/vnd.oasis.opendocument.spreadsheet'
    ];

    public function get(Request $request, Response $response)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $templates = TemplateModel::get();

        return $response->withJson(['templates' => $templates]);
    }

    public function getDetailledById(Request $request, Response $response, array $aArgs)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $template = TemplateModel::getById(['id' => $aArgs['id']]);
        if (empty($template)) {
            return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
        }

        $rawLinkedEntities = TemplateAssociationModel::get(['select' => ['value_field'], 'where' => ['template_id = ?'], 'data' => [$template['template_id']]]);
        $linkedEntities = [];
        foreach ($rawLinkedEntities as $rawLinkedEntity) {
            $linkedEntities[] = $rawLinkedEntity['value_field'];
        }
        $entities = EntityModel::getAllowedEntitiesByUserId(['userId' => 'superadmin']);
        foreach ($entities as $key => $entity) {
            $entities[$key]['state']['selected'] = false;
            if (in_array($entity['id'], $linkedEntities)) {
                $entities[$key]['state']['selected'] = true;
            }
        }

        $attachmentModelsTmp = AttachmentModel::getAttachmentsTypesByXML();
        $attachmentTypes = [];
        foreach ($attachmentModelsTmp as $key => $value) {
            if ($value['show']) {
                $attachmentTypes[] = [
                    'label' => $value['label'],
                    'id'    => $key
                ];
            }
        }

        return $response->withJson([
            'template'          => $template,
            'templatesModels'   => TemplateModel::getModels(),
            'attachmentTypes'   => $attachmentTypes,
            'datasources'       => TemplateModel::getDatasources(),
            'entities'          => $entities
        ]);
    }

    public function create(Request $request, Response $response)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $data = $request->getParams();
        if (!TemplateController::checkData(['data' => $data])) {
            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
        }

        if ($data['template_type'] == 'OFFICE_HTML' && !$data['jnlpUniqueId'] && !$data['uploadedFile'] && !$data['template_content']) {
            return $response->withStatus(400)->withJson(['errors' => 'You must complete at least one of the two templates']);
        }

        if ($data['template_target'] == 'acknowledgementReceipt' && !empty($data['entities'])) {
            $checkEntities = TemplateModel::checkEntities(['data' => $data]);
            
            if (!empty($checkEntities)) {
                return $response->withJson(['checkEntities' => $checkEntities]);
            }
        }

        if ($data['template_type'] == 'OFFICE' || ($data['template_type'] == 'OFFICE_HTML' && ($data['jnlpUniqueId'] || $data['uploadedFile']))) {
            if (empty($data['jnlpUniqueId']) && empty($data['uploadedFile'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Template file is missing']);
            }
            if (!empty($data['jnlpUniqueId'])) {
                if (!Validator::stringType()->notEmpty()->validate($data['template_style'])) {
                    return $response->withStatus(400)->withJson(['errors' => 'Template style is missing']);
                }
                $explodeStyle = explode(':', $data['template_style']);
                $fileOnTmp = "tmp_file_{$GLOBALS['id']}_{$data['jnlpUniqueId']}." . strtolower($explodeStyle[0]);
            } else {
                if (empty($data['uploadedFile']['base64']) || empty($data['uploadedFile']['name'])) {
                    return $response->withStatus(400)->withJson(['errors' => 'Uploaded file is missing']);
                }
                $fileContent = base64_decode($data['uploadedFile']['base64']);
                $finfo    = new \finfo(FILEINFO_MIME_TYPE);
                $mimeType = $finfo->buffer($fileContent);
                if (!in_array($mimeType, self::AUTHORIZED_MIMETYPES)) {
                    return $response->withStatus(400)->withJson(['errors' => _WRONG_FILE_TYPE]);
                }

                $fileOnTmp = rand() . $data['uploadedFile']['name'];
                $file = fopen(CoreConfigModel::getTmpPath() . $fileOnTmp, 'w');
                fwrite($file, $fileContent);
                fclose($file);
            }

            $resource = file_get_contents(CoreConfigModel::getTmpPath() . $fileOnTmp);
            $pathInfo = pathinfo(CoreConfigModel::getTmpPath() . $fileOnTmp);
            $storeResult = DocserverController::storeResourceOnDocServer([
                'collId'            => 'templates',
                'docserverTypeId'   => 'TEMPLATES',
                'encodedResource'   => base64_encode($resource),
                'format'            => $pathInfo['extension']
            ]);
            if (!empty($storeResult['errors'])) {
                return $response->withStatus(500)->withJson(['errors' => '[storeResource] ' . $storeResult['errors']]);
            }

            $data['template_path'] = $storeResult['destination_dir'];
            $data['template_file_name'] = $storeResult['file_destination_name'];
        }

        $id = TemplateModel::create($data);
        if (!empty($data['entities']) && is_array($data['entities'])) {
            foreach ($data['entities'] as $entity) {
                TemplateAssociationModel::create(['templateId' => $id, 'entityId' => $entity]);
            }
        }

        HistoryController::add([
            'tableName' => 'templates',
            'recordId'  => $id,
            'eventType' => 'ADD',
            'info'      => _TEMPLATE_ADDED . " : {$data['template_label']}",
            'moduleId'  => 'template',
            'eventId'   => 'templateCreation',
        ]);

        return $response->withJson(['template' => $id]);
    }

    public function update(Request $request, Response $response, array $aArgs)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $template = TemplateModel::getById(['select' => ['template_style', 'template_file_name', 'template_type', 'template_target'], 'id' => $aArgs['id']]);
        if (empty($template)) {
            return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
        }

        $data = $request->getParams();
        $data['template_type'] = $template['template_type'];
        $data['template_target'] = $template['template_target'];
        $data['template_id'] = $aArgs['id'];

        if (!TemplateController::checkData(['data' => $data])) {
            return $response->withStatus(400)->withJson(['errors' => 'Bad Request']);
        }

        if ($data['template_type'] == 'OFFICE_HTML' && empty($data['jnlpUniqueId']) && empty($data['uploadedFile']) && empty($data['template_content']) && empty($template['template_file_name'])) {
            return $response->withStatus(400)->withJson(['errors' => 'You must complete at least one of the two templates']);
        }

        if ($data['template_target'] == 'acknowledgementReceipt' && !empty($data['entities'])) {
            $checkEntities = TemplateModel::checkEntities(['data' => $data]);
            
            if (!empty($checkEntities)) {
                return $response->withJson(['checkEntities' => $checkEntities]);
            }
        }

        if (($data['template_type'] == 'OFFICE' || $data['template_type'] == 'OFFICE_HTML') && (!empty($data['jnlpUniqueId']) || !empty($data['uploadedFile']))) {
            if (!empty($data['jnlpUniqueId'])) {
                if (!empty($data['template_style'])) {
                    $explodeStyle = explode(':', $data['template_style']);
                    $fileOnTmp = "tmp_file_{$GLOBALS['id']}_{$data['jnlpUniqueId']}." . strtolower($explodeStyle[0]);
                } elseif (!empty($data['template_file_name'])) {
                    $explodeStyle = explode('.', $data['template_file_name']);
                    $fileOnTmp = "tmp_file_{$GLOBALS['id']}_{$data['jnlpUniqueId']}." . strtolower($explodeStyle[count($explodeStyle) - 1]);
                }
            } else {
                if (empty($data['uploadedFile']['base64']) || empty($data['uploadedFile']['name'])) {
                    return $response->withStatus(400)->withJson(['errors' => 'Uploaded file is missing']);
                }
                $fileContent = base64_decode($data['uploadedFile']['base64']);
                $finfo    = new \finfo(FILEINFO_MIME_TYPE);
                $mimeType = $finfo->buffer($fileContent);
                if (!in_array($mimeType, self::AUTHORIZED_MIMETYPES)) {
                    return $response->withStatus(400)->withJson(['errors' => _WRONG_FILE_TYPE]);
                }

                $fileOnTmp = rand() . $data['uploadedFile']['name'];
                $file = fopen(CoreConfigModel::getTmpPath() . $fileOnTmp, 'w');
                fwrite($file, $fileContent);
                fclose($file);
            }

            $resource = file_get_contents(CoreConfigModel::getTmpPath() . $fileOnTmp);
            $pathInfo = pathinfo(CoreConfigModel::getTmpPath() . $fileOnTmp);
            $storeResult = DocserverController::storeResourceOnDocServer([
                'collId'            => 'templates',
                'docserverTypeId'   => 'TEMPLATES',
                'encodedResource'   => base64_encode($resource),
                'format'            => $pathInfo['extension']
            ]);
            if (!empty($storeResult['errors'])) {
                return $response->withStatus(500)->withJson(['errors' => '[storeResource] ' . $storeResult['errors']]);
            }

            $data['template_path'] = $storeResult['destination_dir'];
            $data['template_file_name'] = $storeResult['file_destination_name'];
        }

        TemplateAssociationModel::delete(['where' => ['template_id = ?'], 'data' => [$aArgs['id']]]);
        if (!empty($data['entities']) && is_array($data['entities'])) {
            foreach ($data['entities'] as $entity) {
                TemplateAssociationModel::create(['templateId' => $aArgs['id'], 'entityId' => $entity]);
            }
        }
        unset($data['uploadedFile']);
        unset($data['jnlpUniqueId']);
        unset($data['entities']);
        TemplateModel::update(['set' => $data, 'where' => ['template_id = ?'], 'data' => [$aArgs['id']]]);

        HistoryController::add([
            'tableName' => 'templates',
            'recordId'  => $aArgs['id'],
            'eventType' => 'UP',
            'info'      => _TEMPLATE_UPDATED . " : {$data['template_label']}",
            'moduleId'  => 'template',
            'eventId'   => 'templateModification',
        ]);

        return $response->withJson(['success' => 'success']);
    }

    public function delete(Request $request, Response $response, array $aArgs)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $template = TemplateModel::getById(['select' => ['template_label'], 'id' => $aArgs['id']]);
        if (empty($template)) {
            return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
        }

        TemplateModel::delete(['where' => ['template_id = ?'], 'data' => [$aArgs['id']]]);
        TemplateAssociationModel::delete(['where' => ['template_id = ?'], 'data' => [$aArgs['id']]]);

        HistoryController::add([
            'tableName' => 'templates',
            'recordId'  => $aArgs['id'],
            'eventType' => 'DEL',
            'info'      => _TEMPLATE_DELETED . " : {$template['template_label']}",
            'moduleId'  => 'template',
            'eventId'   => 'templateSuppression',
        ]);

        return $response->withJson(['success' => 'success']);
    }

    public function duplicate(Request $request, Response $response, array $aArgs)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $template = TemplateModel::getById(['id' => $aArgs['id']]);

        if (empty($template)) {
            return $response->withStatus(400)->withJson(['errors' => 'Template not found']);
        }

        if ($template['template_target'] == 'acknowledgementReceipt') {
            return $response->withStatus(400)->withJson(['errors' => 'Forbidden duplication']);
        }

        if ($template['template_type'] == 'OFFICE') {
            $docserver = DocserverModel::getCurrentDocserver(['typeId' => 'TEMPLATES', 'collId' => 'templates', 'select' => ['path_template']]);

            $pathOnDocserver = DocserverController::createPathOnDocServer(['path' => $docserver['path_template']]);
            $docinfo = DocserverController::getNextFileNameInDocServer(['pathOnDocserver' => $pathOnDocserver['pathToDocServer']]);
            $docinfo['fileDestinationName'] .=  '.' . explode('.', $template['template_file_name'])[1];

            $pathToDocumentToCopy = $docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $template['template_path']) . $template['template_file_name'];
            $resource = file_get_contents($pathToDocumentToCopy);

            $copyResult = DocserverController::copyOnDocServer([
                'encodedResource'       => base64_encode($resource),
                'destinationDir'        => $docinfo['destinationDir'],
                'fileDestinationName'   => $docinfo['fileDestinationName']
            ]);
            if (!empty($copyResult['errors'])) {
                return $response->withStatus(500)->withJson(['errors' => 'Template duplication failed : ' . $copyResult['errors']]);
            }
            $template['template_path'] = str_replace($docserver['path_template'], '', $docinfo['destinationDir']);
            $template['template_file_name'] = $docinfo['fileDestinationName'];
        }

        $template['template_label'] = 'Copie de ' . $template['template_label'];

        $templateId = TemplateModel::create($template);

        return $response->withJson(['id' => $templateId]);
    }

    public function initTemplates(Request $request, Response $response)
    {
        if (!PrivilegeController::hasPrivilege(['privilegeId' => 'admin_templates', 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
        }

        $attachmentModelsTmp = AttachmentModel::getAttachmentsTypesByXML();
        $attachmentTypes = [];
        foreach ($attachmentModelsTmp as $key => $value) {
            if ($value['show']) {
                $attachmentTypes[] = [
                    'label' => $value['label'],
                    'id'    => $key
                ];
            }
        }

        $entities = EntityModel::getAllowedEntitiesByUserId(['userId' => 'superadmin']);
        foreach ($entities as $key => $entity) {
            $entities[$key]['state']['selected'] = false;
        }

        return $response->withJson([
            'templatesModels' => TemplateModel::getModels(),
            'attachmentTypes' => $attachmentTypes,
            'datasources'     => TemplateModel::getDatasources(),
            'entities'        => $entities,
        ]);
    }

    public function getByResId(Request $request, Response $response, array $args)
    {
        if (!Validator::intVal()->validate($args['resId'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Route resId is not an integer']);
        }
        if (!ResController::hasRightByResId(['resId' => [$args['resId']], 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
        }

        $resource = ResModel::getById(['resId' => $args['resId'], 'select' => ['destination']]);
        if (!empty($resource['destination'])) {
            $entities = [$resource['destination']];
        } else {
            $entities = UserModel::getEntitiesByLogin(['login' => $GLOBALS['userId']]);
            $entities = array_column($entities, 'entity_id');
            if (empty($entities)) {
                $entities = [0];
            }
        }
        $where = ['(templates_association.value_field in (?) OR templates_association.template_id IS NULL)', 'templates.template_type = ?', 'templates.template_target = ?'];
        $data = [$entities, 'OFFICE', 'attachments'];

        $queryParams = $request->getQueryParams();

        if (!empty($queryParams['attachmentType'])) {
            $where[] = 'templates.template_attachment_type in (?)';
            $data[] = explode(',', $queryParams['attachmentType']);
        }
        
        $templates = TemplateModel::getWithAssociation([
            'select'    => ['DISTINCT(templates.template_id)', 'templates.template_label', 'templates.template_file_name', 'templates.template_path', 'templates.template_attachment_type'],
            'where'     => $where,
            'data'      => $data,
            'orderBy'   => ['templates.template_label']
        ]);

        $docserver = DocserverModel::getCurrentDocserver(['typeId' => 'TEMPLATES', 'collId' => 'templates', 'select' => ['path_template']]);
        foreach ($templates as $key => $template) {
            $explodeFile = explode('.', $template['template_file_name']);
            $ext = $explodeFile[count($explodeFile) - 1];
            $exists = is_file($docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $template['template_path']) . $template['template_file_name']);

            $templates[$key] = [
                'id'                => $template['template_id'],
                'label'             => $template['template_label'],
                'extension'         => $ext,
                'exists'            => $exists,
                'attachmentType'    => $template['template_attachment_type']
            ];
        }

        return $response->withJson(['templates' => $templates]);
    }

    public function getEmailTemplatesByResId(Request $request, Response $response, array $args)
    {
        if (!Validator::intVal()->validate($args['resId']) || !ResController::hasRightByResId(['resId' => [$args['resId']], 'userId' => $GLOBALS['id']])) {
            return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
        }

        $resource = ResModel::getById(['resId' => $args['resId'], 'select' => ['destination']]);
        if (!empty($resource['destination'])) {
            $entities = [$resource['destination']];
        } else {
            $entities = UserModel::getEntitiesByLogin(['login' => $GLOBALS['userId']]);
            $entities = array_column($entities, 'entity_id');
            if (empty($entities)) {
                $entities = [0];
            }
        }
        $where = ['(templates_association.value_field in (?) OR templates_association.template_id IS NULL)', 'templates.template_type = ?', 'templates.template_target = ?'];
        $data = [$entities, 'HTML', 'sendmail'];

        $templates = TemplateModel::getWithAssociation([
            'select'    => ['DISTINCT(templates.template_id)', 'templates.template_label'],
            'where'     => $where,
            'data'      => $data,
            'orderBy'   => ['templates.template_label']
        ]);

        foreach ($templates as $key => $template) {
            $templates[$key] = [
                'id'                => $template['template_id'],
                'label'             => $template['template_label']
            ];
        }

        return $response->withJson(['templates' => $templates]);
    }

    public static function mergeEmailTemplate(Request $request, Response $response, array $args)
    {
        if (!Validator::intVal()->validate($args['id'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Route param id is not an integer']);
        }

        $entities = UserModel::getEntitiesByLogin(['login' => $GLOBALS['userId']]);
        $entities = array_column($entities, 'entity_id');
        if (empty($entities)) {
            $entities = [0];
        }

        $templates = TemplateModel::getWithAssociation([
            'select'  => ['DISTINCT(templates.template_id)', 'templates.template_content'],
            'where'   => ['(templates_association.value_field in (?) OR templates_association.template_id IS NULL)', 'templates.template_type = ?', 'templates.template_target = ?', 'templates.template_id = ?'],
            'data'    => [$entities, 'HTML', 'sendmail', $args['id']],
            'orderBy' => ['templates.template_id']
        ]);

        if (empty($templates[0])) {
            return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
        }
        $template = $templates[0];
        if (empty($template['template_content'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Template has no content']);
        }

        $body = $request->getParsedBody();

        if (!Validator::intVal()->validate($body['data']['resId'])) {
            return $response->withStatus(400)->withJson(['errors' => 'Body param resId is missing']);
        }

        $dataToMerge = ['userId' => $GLOBALS['id']];
        if (!empty($body['data']) && is_array($body['data'])) {
            $dataToMerge = array_merge($dataToMerge, $body['data']);
        }
        $mergedDocument = MergeController::mergeDocument([
            'content' => $template['template_content'],
            'data'    => $dataToMerge
        ]);
        $fileContent = base64_decode($mergedDocument['encodedDocument']);

        return $response->withJson(['mergedDocument' => $fileContent]);
    }

    private static function checkData(array $aArgs)
    {
        ValidatorModel::notEmpty($aArgs, ['data']);
        ValidatorModel::arrayType($aArgs, ['data']);

        $availableTypes = ['HTML', 'TXT', 'OFFICE', 'OFFICE_HTML'];
        $data = $aArgs['data'];

        $check = Validator::stringType()->notEmpty()->validate($data['template_label']);
        $check = $check && Validator::stringType()->notEmpty()->validate($data['template_comment']);
        $check = $check && Validator::stringType()->notEmpty()->validate($data['template_type']) && in_array($data['template_type'], $availableTypes);

        if ($data['template_type'] == 'HTML' || $data['template_type'] == 'TXT') {
            $check = $check && Validator::stringType()->notEmpty()->validate($data['template_content']);
        }

        if ($data['template_type'] == 'OFFICE_HTML') {
            $check = $check && Validator::stringType()->validate($data['template_content']);
            $check = $check && Validator::stringType()->notEmpty()->validate($data['template_attachment_type']);
        }

        if (!empty($data['entities'])) {
            $check = $check && Validator::arrayType()->validate($data['entities']);
        }

        return $check;
    }
}