<?php

declare(strict_types=1);

namespace app\controllers;

use app\models\AccountEdgeNotification;
use yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;

use app\models\Notification;

class NotificationController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                        'actions' => ['index']
                    ]
                ]
            ]
        ];
    }

    /**
     * @todo Перенести логику в модель
     */
    public function actionIndex()
    {
        if (yii::$app->request->isPost) {
            // POST-запрос

            // Инициализация
            $model = new Notification(yii::$app->request->post('Notification'));
            $preload = (bool) (int) yii::$app->request->post('preload');
            $account = yii::$app->user->identity;

            yii::$app->response->format = Response::FORMAT_JSON;

            $return = [
                '_csrf' => yii::$app->request->getCsrfToken()
            ];

            if (yii::$app->request->post('last')) {
                // Запрос последнего уведомлений (всплывающее окно)

                // Инициализация
                $limit = 1;
            } else if (yii::$app->request->post('stream')) {
                // Запрос последних уведомлений (панель)
                $limit = 10;
            }

            if (isset($limit)) {
                // Обработка для всплывающего окна или панель

                // Подзапрос для проверки статуса уведомления относительно пользователя
                // Поиск рёбер: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ
                $let = $model::find()
                    ->for(['account', $model::collectionName() . '_edge_account'])
                    ->traversal($model::collectionName(), 'OUTBOUND')
                    ->in('account_edge_' . $model::collectionName())
                    ->where(['account._id == "' . $account->readId() . '" && notification_edge_account._from != account_edge_notification[0]._to'])
                    ->select($model::collectionName() . '_edge_account');

                if (yii::$app->request->post('last')) {
                    // Запрос последнего уведомлений (всплывающее окно)

                    // Уведомление которое не выводилось на мониторе пользователя
                    $type = 'received';
                } else if (yii::$app->request->post('stream')) {
                    // Запрос последних уведомлений (панель)

                    // Уведомление которое не было прочитано в окне уведомний
                    $type = 'checked';
                }

                // Генерация
                $let = $let->createCommand();

                /**
                 * Поиск рёбер: (УВЕДОМЛЕНИЕ)? -> ПОЛЬЗОВАТЕЛЬ
                 *
                 * @param bool $new Активация проверки на то, что уведомление не получено
                 * @param bool $count Посчитать
                 */
                $search = function (bool $new = false, bool $count = false) use ($model, $account, $type, $let, $limit): array|int|null|Notification {
                    if ($count) {
                        // Запрошен подсчёт непрочитанных уведомлений

                        // Реинициализация
                        $type = 'checked';
                        $limit = null;
                    }

                    return $model::searchByEdge(
                        from: 'account',
                        to: 'notification',
                        params: $new ? $let->getBindVars() : [],
                        subquery_where: [
                            [
                                'account._id' => $account->readId()
                            ],
                            [
                                [
                                    'notification.html' => null
                                ],
                                'operator' => '!='
                            ],
                            [
                                'account_edge_notification.type' => $type
                            ]
                        ],
                        let: [
                            'notification_edge_account',
                            '(' . (string) $let . ')'
                        ],
                        foreach: [
                            'edge' => 'notification_edge_account'
                        ],
                        where: $new ? [
                            'edge != null'
                        ] : [],
                        limit: $limit,
                        sort: ['DESC'],
                        direction: 'INBOUND',
                        handle: $count ? function ($request) {
                            // Посчитать рёбра (информация о количестве непрочитанных уведомлений)
                            return $request->count();
                        } : null
                    );
                };

                // Поиск новых (непрочитанных) уведомлений пользователя
                // Если заккоментировать, то вместо только новых отправит все последние уведомления
                $notifications = $search(true);

                // Подсчёт новых (непрочитанных) уведомлений
                $return['button'] = $this->renderPartial('button', ['notifications_new_amount' => $search(new: true, count: true)]);

                if (!yii::$app->request->post('last') && empty($notifications)) {
                    // Запрошены НЕ ВСПЛЫВАЮЩИЕ уведомления и новые уведомления не найдены

                    // Поиск уведомлений пользователя
                    $notifications = $search();
                }

                if (empty($notifications)) {
                    // Уведомления не найдены

                    /**
                     * @todo Определить какой код лучше использовать (404 не подходит)
                     */
                    yii::$app->response->statusCode = 200;

                    goto end;
                }

                foreach (is_array($notifications) ? $notifications : [$notifications] as $notification) {
                    // Перебор найденных уведомлений

                    if ($preload) {
                        // Запрошены уведомления для предзагрузки (их ещё не увидели)

                        break;
                    }

                    // Запись ребра: ПОЛЬЗОВАТЕЛЬ -> УВЕДОМЛЕНИЕ (о том, что уведомление прочитано)
                    AccountEdgeNotification::write($account->readId(), $notification->readId(), $type);
                }

                if (yii::$app->request->post('last')) {
                    // Запрос последнего уведомлений (всплывающее окно)

                    // Реинициализация
                    $notification = $notifications[0];

                    $return['popup'] = [
                        'html' => $this->renderPartial('popup', compact('model', 'notification', 'account')),
                        'id' => 'popup/' . $notification->readId()
                    ];
                } else if (yii::$app->request->post('stream')) {
                    // Запрос последних уведомлений (панель)

                    $return['panel'] = $this->renderPartial('panel', compact('model', 'notifications'));
                }
            } else {
                // Иначе обрабатывается как запрос страницы уведомлений

                $return['main'] = $this->renderPartial('index', compact('model'));
                $return['redirect'] = '/notification';
            }

            /**
             * Конец алгоритма
             */
            end:

            return $return;
        }

        return $this->render('index', ['model' => new Notification(yii::$app->request->get('Notification'))]);
    }
}
