<?php

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

use Nitotm\Eld\LanguageDetector;

/**
 *
 * @OA\Tag(
 *   name="inbox",
 *   description="Inbox module",
 * )
 */

$this->on('restApi.config', function($restApi) {

    $restApi->addEndPoint('/inbox/submit/{token}', [

        /**
         * @OA\Post(
         *     path="/inbox/submit/{token}",
         *     tags={"inbox"},
         *     @OA\Parameter(
         *         description="Form token",
         *         in="path",
         *         name="token",
         *         required=true,
         *         @OA\Schema(type="string")
         *     ),
         *     @OA\RequestBody(
         *         description="Content item data",
         *         required=true,
         *         @OA\JsonContent(
         *             type="object",
         *             @OA\Property(property="data", type="object")
         *         )
         *     ),
         *     @OA\Parameter(
         *         description="Redirect to url after submission",
         *         in="query",
         *         name="success",
         *         required=false,
         *         @OA\Schema(type="string")
         *     ),
         *     @OA\OpenApi(
         *         security={
         *             {"api_key": {}}
         *         }
         *     ),
         *     @OA\Response(response="200", description="Form data submitted", @OA\JsonContent()),
         *     @OA\Response(response="404", description="Form not found"),
         *     @OA\Response(response="412", description="Form data is missing")
         * )
         */

        'POST' => function($params, $app) {

            $form = $app->dataStorage->findOne('inbox/forms', ['token' => $params['token']]);

            if (!$form) {
                $app->response->status = 404;
                return ["error" => "Form not found"];
            }

            $data = $app->param('data');
            $files = $app->request->files['data'] ?? [];
            $fields = $form['security']['fields'] ?? [];
            $domains = $form['security']['domains'] ?? [];
            $honeypot = $form['security']['honeypot'] ?? null;
            $languages = $form['security']['languages'] ?? [];

            if (!$data) {
                $app->response->status = 412;
                return ['error' => 'Item data is missing'];
            }

            if (count($domains)) {

                $referrer = $app->request->server['HTTP_REFERER'] ?? ($app->request->server['HTTP_ORIGIN'] ?? '');
                $host = parse_url($referrer, \PHP_URL_HOST);

                if (!$host || !in_array($host, $domains)) {
                    $app->response->status = 412;
                    return ['error' => 'Not allowed'];
                }
            }

            // check honeypot field
            if ($honeypot) {

                if (isset($data[$honeypot]) && !empty($data[$honeypot])) {
                    return ['success' => true];
                }

                unset($data[$honeypot]);
            }

            $submission = [];
            $attachments = [];

            if (count($fields)) {

                foreach ($fields as $field) {

                    $submission[$field] = null;

                    if (!isset($data[$field]) && !isset($files['name'][$field])) continue;

                    if (isset($data[$field])) {
                        $submission[$field] = $data[$field];
                    }

                    if (isset($files['name'][$field])) {

                        $file = [
                            'name' => $files['name'][$field],
                            'error' => $files['error'][$field],
                            'tmp_name' => $files['tmp_name'][$field],
                        ];

                        $errors = is_countable($file['error']) ? $file['error'] : [$file['error']];

                        foreach ($errors as $err) {

                            if ($err) {
                                $app->response->status = 412;
                                return ['error' => "<{$field}>: upload failed ({$err})"];
                            }
                        }

                        $uid    = uniqid();
                        $finfo   = finfo_open(FILEINFO_MIME_TYPE);
                        $uploads = [];

                        $_tmp = is_countable($file['tmp_name']) ? $file['tmp_name'] : [$file['tmp_name']];
                        $_name = is_countable($file['name']) ? $file['name'] : [$file['name']];

                        $forbiddenExtension = ['bat', 'exe', 'sh', 'php', 'phar', 'phtml', 'phps', 'htm', 'html', 'xhtml', 'htaccess'];
                        $forbiddenMime = [
                            'application/x-httpd-php', 'application/x-php', 'text/x-php',
                            'text/html', 'application/xhtml+xml'
                        ];

                        foreach ($_tmp as $idx => $tmp) {

                            $name   = preg_replace('/[^a-zA-Z0-9-_\.]/','', str_replace(' ', '-', basename($_name[$idx])));
                            $mime   = finfo_file($finfo, $tmp);
                            $ext    = strtolower(pathinfo(parse_url($name, PHP_URL_PATH), PATHINFO_EXTENSION));

                            if (
                                !$ext ||
                                in_array($ext, $forbiddenExtension) ||
                                in_array(strtolower($mime), $forbiddenMime)
                            ) {
                                continue;
                            }

                            $path   = "/inbox/uploads/{$form['_id']}/".date('Y/m/d')."/{$uid}/{$name}";
                            $opts   = ['mimetype' => $mime];
                            $stream = fopen($tmp, 'r+');

                            $app->fileStorage->writeStream("uploads://{$path}", $stream, $opts);

                            if (is_resource($stream)) {
                                fclose($stream);
                            }

                            $uploads[] = [
                                'name' => basename($_name[$idx]),
                                'file' => $path
                            ];
                        }

                        unset($submission[$field]);
                        $attachments[$field] = $uploads;
                    }
                }

            } else {
                $submission = $data;
            }

            if (!count(array_filter(array_values($submission)))) {
                $app->response->status = 412;
                return ['error' => 'Item data is missing'];
            }

            // check allowed languages
            if (count($languages)) {

                $detector = new LanguageDetector();
                $detector->cleanText(true);

                foreach ($submission as $key => $value) {

                    if (!is_string($value) || strlen($value) < 50) continue;

                    $lang = $detector->detect($value);

                    if (!$lang->language || $lang->language === 'und' || !($lang->isReliable() && in_array($lang->language, $languages))) {
                        $app->response->status = 412;
                        return ['error' => 'Precondition failed'];
                    }
                }
            }

            $collection = "inbox/form_{$form['_id']}";
            $record = [
                'data' => $submission,
                'attachments' => $attachments,
                'spam' => false,
                '_created' => time()
            ];

            $app->trigger('inbox.submit.before', [$form, &$record]);
            $app->dataStorage->save($collection, $record);

            $emails = $form['notify']['emails'] ?? [];

            if (is_array($emails) && count($emails)) {

                $template = $app->path('#config:inbox/layouts/email.php');

                if (!$template) {
                    $template = $app->path('inbox:layouts/email.php');
                }

                $subject = $app->param('subject', "New submission: {$form['name']}");
                $body = $app->render($template, ['data' => $submission, 'attachments' => $attachments, 'form' => $form], false);
                $opts = [];

                if (isset($submission['email']) && $submission['email'] && filter_var($submission['email'], FILTER_VALIDATE_EMAIL)) {
                    $opts['reply_to'] = $submission['email'];
                }

                try {
                    $app->mailer->mail($emails, $subject, $body, $opts);
                } catch (\Exception $e) {
                    $app->module('system')->log("Inbox error: {$e->getMessage()}", channel: 'inbox', type: 'error');
                }
            }

            $app->trigger('inbox.submit.after', [$form, $record]);

            if ($app->param('success')) {
                $app->reroute($app->param('success'));
            }

            return ['success' => true, 'record' => $record];

        }
    ]);
});
