<?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 Store Controller
 * @author dev@maarch.org
 * @ingroup core
 */

namespace Resource\controllers;

use Attachment\models\AttachmentModel;
use ContentManagement\controllers\MergeController;
use CustomField\models\CustomFieldModel;
use Docserver\controllers\DocserverController;
use Entity\models\EntityModel;
use IndexingModel\models\IndexingModelModel;
use Resource\models\ChronoModel;
use SrcCore\models\DatabaseModel;
use SrcCore\models\ValidatorModel;
use Resource\models\ResModel;
use SrcCore\models\CoreConfigModel;

class StoreController
{
    public static function storeResource(array $args)
    {
        try {
            if (empty($args['resId'])) {
                $resId = DatabaseModel::getNextSequenceValue(['sequenceId' => 'res_id_mlb_seq']);

                $data = ['resId' => $resId];
                $data = array_merge($args, $data);
                $data = StoreController::prepareResourceStorage($data);
            } else {
                $resId = $args['resId'];
                $data = StoreController::prepareUpdateResourceStorage($args);
            }

            if (!empty($args['encodedFile'])) {
                $fileContent = base64_decode(str_replace(['-', '_'], ['+', '/'], $args['encodedFile']));

                if (empty($args['resId']) && in_array($args['format'], MergeController::OFFICE_EXTENSIONS) && empty($args['integrations']['inMailing'])) {
                    $tmpPath = CoreConfigModel::getTmpPath();
                    $uniqueId = CoreConfigModel::uniqueId();
                    $tmpFilename = "storeTmp_{$GLOBALS['id']}_{$uniqueId}.{$args['format']}";
                    file_put_contents($tmpPath . $tmpFilename, $fileContent);
                    $fileContent = MergeController::mergeChronoDocument(['chrono' => $data['alt_identifier'], 'path' => $tmpPath . $tmpFilename, 'type' => 'resource']);
                    $fileContent = base64_decode($fileContent['encodedDocument']);
                    unlink($tmpPath . $tmpFilename);
                }

                $storeResult = DocserverController::storeResourceOnDocServer([
                    'collId'            => 'letterbox_coll',
                    'docserverTypeId'   => 'DOC',
                    'encodedResource'   => base64_encode($fileContent),
                    'format'            => $args['format']
                ]);
                if (!empty($storeResult['errors'])) {
                    return ['errors' => '[storeResource] ' . $storeResult['errors']];
                }

                $data['docserver_id']   = $storeResult['docserver_id'];
                $data['filename']       = $storeResult['file_destination_name'];
                $data['filesize']       = $storeResult['fileSize'];
                $data['path']           = $storeResult['directory'];
                $data['fingerprint']    = $storeResult['fingerPrint'];
                $data['format']         = $args['format'];
            }

            if (empty($args['resId'])) {
                ResModel::create($data);
            } else {
                ResModel::update(['set' => $data, 'where' => ['res_id = ?'], 'data' => [$args['resId']]]);
            }

            return $resId;
        } catch (\Exception $e) {
            return ['errors' => '[storeResource] ' . $e->getMessage()];
        }
    }

    public static function storeAttachment(array $args)
    {
        try {
            if (empty($args['id'])) {
                $data = StoreController::prepareAttachmentStorage($args);
            } else {
                $data = StoreController::prepareUpdateAttachmentStorage($args);
            }

            if (!empty($args['encodedFile'])) {
                $fileContent    = base64_decode(str_replace(['-', '_'], ['+', '/'], $args['encodedFile']));

                if (empty($args['id']) && in_array($args['format'], MergeController::OFFICE_EXTENSIONS) && $data['status'] != 'SEND_MASS') {
                    $tmpPath = CoreConfigModel::getTmpPath();
                    $uniqueId = CoreConfigModel::uniqueId();
                    $tmpFilename = "storeTmp_{$GLOBALS['id']}_{$uniqueId}.{$args['format']}";
                    file_put_contents($tmpPath . $tmpFilename, $fileContent);
                    $fileContent = MergeController::mergeChronoDocument(['chrono' => $data['identifier'], 'path' => $tmpPath . $tmpFilename, 'type' => 'attachment']);
                    $fileContent = base64_decode($fileContent['encodedDocument']);
                    unlink($tmpPath . $tmpFilename);
                }

                $storeResult = DocserverController::storeResourceOnDocServer([
                    'collId'            => 'attachments_coll',
                    'docserverTypeId'   => 'DOC',
                    'encodedResource'   => base64_encode($fileContent),
                    'format'            => $args['format']
                ]);
                if (!empty($storeResult['errors'])) {
                    return ['errors' => '[storeAttachment] ' . $storeResult['errors']];
                }

                $data['docserver_id']   = $storeResult['docserver_id'];
                $data['filename']       = $storeResult['file_destination_name'];
                $data['filesize']       = $storeResult['fileSize'];
                $data['path']           = $storeResult['directory'];
                $data['fingerprint']    = $storeResult['fingerPrint'];
                $data['format']         = $args['format'];
            }

            if (empty($args['id'])) {
                $id = AttachmentModel::create($data);
            } else {
                AttachmentModel::update(['set' => $data, 'where' => ['res_id = ?'], 'data' => [$args['id']]]);
                $id = $args['id'];
            }

            return $id;
        } catch (\Exception $e) {
            return ['errors' => '[storeAttachment] ' . $e->getMessage()];
        }
    }

    public static function prepareResourceStorage(array $args)
    {
        ValidatorModel::notEmpty($args, ['resId', 'modelId']);
        ValidatorModel::intVal($args, ['resId', 'modelId']);

        $indexingModel = IndexingModelModel::getById(['id' => $args['modelId'], 'select' => ['category']]);

        if (empty($args['typist'])) {
            $args['typist'] = $GLOBALS['id'];
        }

        if (!empty($args['initiator'])) {
            $entity = EntityModel::getById(['id' => $args['initiator'], 'select' => ['entity_id']]);
            $args['initiator'] = $entity['entity_id'];
        }
        if (!empty($args['destination'])) {
            $entity = EntityModel::getById(['id' => $args['destination'], 'select' => ['entity_id']]);
            $args['destination'] = $entity['entity_id'];
        }
        $chrono = null;
        if (!empty($args['chrono'])) {
            $chrono = ChronoModel::getChrono(['id' => $indexingModel['category'], 'entityId' => $args['destination'], 'typeId' => $args['doctype'], 'resId' => $args['resId']]);
        }

        if (!empty($args['processLimitDate']) && !empty($args['priority'])) {
            $args['priority'] = IndexingController::calculatePriorityWithProcessLimitDate(['processLimitDate' => $args['processLimitDate']]);
        }

        $externalId = '{}';
        if (!empty($args['externalId']) && is_array($args['externalId'])) {
            $externalId = json_encode($args['externalId']);
        }

        $integrations = ['inSignatureBook' => false, 'inShipping' => false];
        if (!empty($args['integrations'])) {
            $integrations['inSignatureBook'] = !empty($args['integrations']['inSignatureBook']);
            $integrations['inShipping'] = !empty($args['integrations']['inShipping']);
        }

        if (!empty($args['customFields'])) {
            foreach ($args['customFields'] as $key => $value) {
                $customField = CustomFieldModel::getById(['id' => $key, 'select' => ['type']]);
                if ($customField['type'] == 'date' && !empty($value)) {
                    $date = new \DateTime($value);
                    $value = $date->format('Y-m-d');
                    $args['customFields'][$key] = $value;
                }
            }
        }

        $preparedData = [
            'res_id'                => $args['resId'],
            'model_id'              => $args['modelId'],
            'category_id'           => $indexingModel['category'],
            'type_id'               => $args['doctype'],
            'subject'               => $args['subject'] ?? null,
            'alt_identifier'        => $chrono,
            'typist'                => $args['typist'],
            'status'                => $args['status'] ?? null,
            'destination'           => $args['destination'] ?? null,
            'initiator'             => $args['initiator'] ?? null,
            'confidentiality'       => empty($args['confidentiality']) ? 'N' : 'Y',
            'doc_date'              => $args['documentDate'] ?? null,
            'admission_date'        => $args['arrivalDate'] ?? null,
            'departure_date'        => $args['departureDate'] ?? null,
            'process_limit_date'    => $args['processLimitDate'] ?? null,
            'priority'              => $args['priority'] ?? null,
            'version'               => 1,
            'barcode'               => $args['barcode'] ?? null,
            'origin'                => $args['origin'] ?? null,
            'custom_fields'         => !empty($args['customFields']) ? json_encode($args['customFields']) : null,
            'integrations'          => json_encode($integrations),
            'linked_resources'      => !empty($args['linkedResources']) ? json_encode($args['linkedResources']) : '[]',
            'external_id'           => $externalId,
            'creation_date'         => 'CURRENT_TIMESTAMP'
        ];

        return $preparedData;
    }

    public static function prepareUpdateResourceStorage(array $args)
    {
        $definedVars = get_defined_vars();

        $preparedData = [
            'modification_date' => 'CURRENT_TIMESTAMP'
        ];

        $resource = ResModel::getById(['resId' => $args['resId'], 'select' => ['version', 'alt_identifier', 'external_id', 'category_id', 'type_id', 'destination']]);

        if (empty($resource['alt_identifier'])) {
            $chrono = ChronoModel::getChrono(['id' => $resource['category_id'], 'entityId' => $resource['destination'], 'typeId' => $resource['type_id'], 'resId' => $args['resId']]);
            $preparedData['alt_identifier'] = $chrono;
        }
        if (!empty($args['doctype'])) {
            $preparedData['type_id'] = $args['doctype'];
        }
        if (isset($args['subject'])) {
            $preparedData['subject'] = $args['subject'];
        }
        if (isset($args['confidentiality'])) {
            $preparedData['confidentiality'] = empty($args['confidentiality']) ? 'N' : 'Y';
        }
        if (!empty($args['initiator'])) {
            $entity = EntityModel::getById(['id' => $args['initiator'], 'select' => ['entity_id']]);
            $preparedData['initiator'] = $entity['entity_id'];
        } else if (array_key_exists('initiator', $definedVars['args'])) {
            $preparedData['initiator'] = null;
        }
        if (isset($args['documentDate'])) {
            $preparedData['doc_date'] = $args['documentDate'];
        } else if (array_key_exists('documentDate', $definedVars['args'])) {
            $preparedData['doc_date'] = null;
        }
        if (isset($args['arrivalDate'])) {
            $preparedData['admission_date'] = $args['arrivalDate'];
        } else if (array_key_exists('arrivalDate', $definedVars['args'])) {
            $preparedData['admission_date'] = null;
        }
        if (isset($args['departureDate'])) {
            $preparedData['departure_date'] = $args['departureDate'];
        } else if (array_key_exists('departureDate', $definedVars['args'])) {
            $preparedData['departure_date'] = null;
        }
        if (isset($args['processLimitDate'])) {
            $preparedData['process_limit_date'] = $args['processLimitDate'];
        } else if (array_key_exists('processLimitDate', $definedVars['args'])) {
            $preparedData['process_limit_date'] = null;
        }
        if (isset($args['priority'])) {
            $preparedData['priority'] = $args['priority'];
        } else if (array_key_exists('priority', $definedVars['args'])) {
            $preparedData['priority'] = null;
        }
        if (!empty($args['processLimitDate']) && !empty($args['priority'])) {
            $preparedData['priority'] = IndexingController::calculatePriorityWithProcessLimitDate(['processLimitDate' => $args['processLimitDate']]);
        }
        if (!empty($args['encodedFile'])) {
            $preparedData['version'] = $resource['version'] + 1;
        }
        if (!empty($args['externalId']) && is_array($args['externalId'])) {
            $externalId = array_merge(json_decode($resource['external_id'], true), $args['externalId']);
            $externalId = json_encode($externalId);
            $preparedData['external_id'] = $externalId;
        }

        if (!empty($args['customFields'])) {
            foreach ($args['customFields'] as $key => $value) {
                $customField = CustomFieldModel::getById(['id' => $key, 'select' => ['type']]);
                if ($customField['type'] == 'date' && !empty($value)) {
                    $date = new \DateTime($value);
                    $value = $date->format('Y-m-d');
                    $args['customFields'][$key] = $value;
                }
            }
            $preparedData['custom_fields'] = json_encode($args['customFields']);
        }

        return $preparedData;
    }

    public static function prepareAttachmentStorage(array $args)
    {
        $attachmentsTypes = AttachmentModel::getAttachmentsTypesByXML();
        if ($attachmentsTypes[$args['type']]['chrono'] && empty($args['chrono'])) {
            $resource = ResModel::getById(['select' => ['destination', 'type_id'], 'resId' => $args['resIdMaster']]);
            $args['chrono'] = ChronoModel::getChrono(['id' => 'outgoing', 'entityId' => $resource['destination'], 'typeId' => $resource['type_id'], 'resId' => $args['resIdMaster']]);
        }
        $shouldBeInSignatureBook = $attachmentsTypes[$args['type']]['sign'];

        if ($args['type'] == 'signed_response') {
            $linkSign = "{$args['originId']},res_attachments";
            unset($args['originId']);
        }

        $relation = 1;
        if (!empty($args['originId'])) {
            $relations = AttachmentModel::get(['select' => ['relation', 'in_signature_book'], 'where' => ['(origin_id = ? or res_id = ?)'], 'data' => [$args['originId'], $args['originId']], 'orderBy' => ['relation DESC'], 'limit' => 1]);
            $relation = $relations[0]['relation'] + 1;
            AttachmentModel::update(['set' => ['status' => 'OBS'], 'where' => ['(origin_id = ? OR res_id = ?)'], 'data' => [$args['originId'], $args['originId']]]);
            $shouldBeInSignatureBook = $relations[0]['in_signature_book'];
        }
        $typist = $GLOBALS['userId'];
        if (!empty($args['typist']) && !is_numeric($args['typist'])) {
            $typist = $args['typist'];
        }

        $externalId = '{}';
        if (!empty($args['externalId']) && is_array($args['externalId'])) {
            $externalId = json_encode($args['externalId']);
        }

        $inSignatureBook = isset($args['inSignatureBook']) ? $args['inSignatureBook'] : $shouldBeInSignatureBook;
        $preparedData = [
            'title'                 => $args['title'] ?? null,
            'identifier'            => $args['chrono'] ?? null,
            'typist'                => $typist,
            'status'                => $args['status'] ?? 'A_TRA',
            'relation'              => $relation,
            'origin_id'             => $args['originId'] ?? null,
            'origin'                => $linkSign ?? null,
            'res_id_master'         => $args['resIdMaster'],
            'attachment_type'       => $args['type'],
            'recipient_id'          => $args['recipientId'] ?? null,
            'recipient_type'        => !empty($args['recipientId']) ? $args['recipientType'] : null,
            'validation_date'       => $args['validationDate'] ?? null,
            'effective_date'        => $args['effectiveDate'] ?? null,
            'in_signature_book'     => $inSignatureBook ? 'true' : 'false',
            'external_id'           => $externalId,
            'creation_date'         => 'CURRENT_TIMESTAMP'
        ];

        return $preparedData;
    }

    public static function prepareUpdateAttachmentStorage(array $args)
    {
        $attachment = AttachmentModel::getById(['id' => $args['id'], 'select' => ['identifier', 'res_id_master']]);
        $attachmentsTypes = AttachmentModel::getAttachmentsTypesByXML();
        if ($attachmentsTypes[$args['type']]['chrono'] && empty($attachment['identifier'])) {
            $resource = ResModel::getById(['select' => ['destination', 'type_id'], 'resId' => $attachment['res_id_master']]);
            $chrono = ChronoModel::getChrono(['id' => 'outgoing', 'entityId' => $resource['destination'], 'typeId' => $resource['type_id'], 'resId' => $attachment['res_id_master']]);
        }

        $preparedData = [
            'title'                 => $args['title'] ?? null,
            'recipient_id'          => $args['recipientId'] ?? null,
            'recipient_type'        => $args['recipientType'] ?? null,
            'attachment_type'       => $args['type'],
            'validation_date'       => $args['validationDate'] ?? null,
            'effective_date'        => $args['effectiveDate'] ?? null,
            'modified_by'           => $GLOBALS['id'],
            'modification_date'     => 'CURRENT_TIMESTAMP'
        ];

        if (!empty($chrono)) {
            $preparedData['identifier'] = $chrono;
        }

        return $preparedData;
    }

    public static function getFingerPrint(array $aArgs)
    {
        ValidatorModel::notEmpty($aArgs, ['filePath']);
        ValidatorModel::stringType($aArgs, ['filePath', 'mode']);

        if (empty($aArgs['mode']) || $aArgs['mode'] == 'NONE') {
            $aArgs['mode'] = 'sha512';
        }

        return hash_file(strtolower($aArgs['mode']), $aArgs['filePath']);
    }

    public static function isFileAllowed(array $args)
    {
        ValidatorModel::notEmpty($args, ['extension', 'type']);
        ValidatorModel::stringType($args, ['extension', 'type']);

        $loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/extensions.xml']);
        if ($loadedXml) {
            foreach ($loadedXml->FORMAT as $value) {
                if (strtolower((string)$value->name) == strtolower($args['extension']) && strtolower((string)$value->mime) == strtolower($args['type'])) {
                    return true;
                }
            }
        }

        return false;
    }

    public static function getAllowedFiles()
    {
        $allowedFiles = [];

        $loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/extensions.xml']);
        if ($loadedXml) {
            foreach ($loadedXml->FORMAT as $value) {
                $allowedFiles[] = [
                    'extension'     => (string)$value->name,
                    'mimeType'      => (string)$value->mime,
                    'canConvert'    => filter_var((string)$value->canConvert, FILTER_VALIDATE_BOOLEAN)
                ];
            }
        }

        return $allowedFiles;
    }

    public static function getBytesSizeFromPhpIni(array $args)
    {
        if (strpos($args['size'], 'K') !== false) {
            return (int)$args['size'] * 1024;
        } elseif (strpos($args['size'], 'M') !== false) {
            return (int)$args['size'] * 1048576;
        } elseif (strpos($args['size'], 'G') !== false) {
            return (int)$args['size'] * 1073741824;
        }

        return (int)$args['size'];
    }

    public static function getFormattedSizeFromBytes(array $args)
    {
        if ($args['size'] / 1073741824 > 1) {
            return round($args['size'] / 1073741824) . ' Go';
        } elseif ($args['size'] / 1048576 > 1) {
            return round($args['size'] / 1048576) . ' Mo';
        } elseif ($args['size'] / 1024 > 1) {
            return round($args['size'] / 1024) . ' Ko';
        }

        return $args['size'] . ' o';
    }
}