<?php
/**
 * This file is part of the ForkBB <https://github.com/forkbb>.
 *
 * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
 * @license   The MIT License (MIT)
 */

declare(strict_types=1);

namespace ForkBB\Models\Subscription;

use ForkBB\Models\Model;
use ForkBB\Models\DataModel;
use ForkBB\Models\Forum\Forum;
use ForkBB\Models\Topic\Topic;
use ForkBB\Models\User\User;
use PDO;
use InvalidArgumentException;

class Subscription extends Model
{
    /**
     * Ключ модели для контейнера
     * @var string
     */
    protected $cKey = 'Subscription';

    /**
     * @var array
     */
    protected $forums;

    /**
     * @var array
     */
    protected $topics;

    /**
     * @var array
     */
    protected $users;

    /**
     * Проверяет список моделей на форумы/темы
     * Заполняет forums, topics и users
     */
    protected function check(array $models, bool $mayBeUsers = false): void
    {
        $this->forums = [];
        $this->topics = [];
        $this->users  = [];

        if (empty($models)) {
            if ($mayBeUsers) {
                throw new InvalidArgumentException('Expected at least one Forum, Topic or User');
            } else {
                throw new InvalidArgumentException('Expected at least one Forum or Topic');
            }
        }

        foreach ($models as $model) {
            if (
                $mayBeUsers
                && $model instanceof User
            ) {
                $this->users[$model->id] = $model->id;
            } elseif ($model instanceof Forum) {
                $this->forums[$model->id] = $model->id;
                $mayBeUsers               = false;
            } elseif ($model instanceof Topic) {
                $this->topics[$model->id] = $model->id;
                $mayBeUsers               = false;
            } else {
                throw new InvalidArgumentException('Expected only Forum or Topic');
            }
        }
    }

    /**
     * Подписывает юзера на форум(ы)/тему(ы)
     */
    public function subscribe(User $user, DataModel ...$models): bool
    {
        if (
            $user->isGuest
            || $user->isUnverified
        ) {
            return false;
        }

        $this->check($models);

        $vars = [
            ':uid' => $user->id,
        ];

        if (! empty($this->forums)) {
            $query = 'INSERT INTO ::forum_subscriptions (user_id, forum_id)
                SELECT ?i:uid, ?i:id
                FROM ::groups
                WHERE NOT EXISTS (
                    SELECT 1
                    FROM ::forum_subscriptions
                    WHERE user_id=?i:uid AND forum_id=?i:id
                )
                LIMIT 1';

            foreach ($this->forums as $id) {
                $vars[':id'] = $id;

                $this->c->DB->exec($query, $vars);
            }
        }

        if (! empty($this->topics)) {
            $query = 'INSERT INTO ::topic_subscriptions (user_id, topic_id)
                SELECT ?i:uid, ?i:id
                FROM ::groups
                WHERE NOT EXISTS (
                    SELECT 1
                    FROM ::topic_subscriptions
                    WHERE user_id=?i:uid AND topic_id=?i:id
                )
                LIMIT 1';

            foreach ($this->topics as $id) {
                $vars[':id'] = $id;

                $this->c->DB->exec($query, $vars);
            }
        }

        return true;
    }

    /**
     * Отписывает юзеров от форумов/топиков
     * Убирает подписки с удаляемых форумов/топиков
     * Убирает подписки с удаляемых юзеров
     */
    public function unsubscribe(DataModel ...$models): bool
    {
        $where = [];
        $vars  = [];

        $this->check($models, true);

        if (! empty($this->users)) {
            if (1 === \count($this->users)) {
                $where[':uid'] = 'user_id=?i:uid';
                $vars[':uid']  = \reset($this->users);
            } else {
                $where[':uid'] = 'user_id IN(?ai:uid)';
                $vars[':uid']  = $this->users;
            }
        }

        $all = empty($this->forums) && empty($this->topics);

        if ($all || ! empty($this->forums)) {
            if (! empty($this->forums)) {
                if (1 === \count($this->forums)) {
                    $where[':id'] = 'forum_id=?i:id';
                    $vars[':id']  = \reset($this->forums);
                } else {
                    $where[':id'] = 'forum_id IN(?ai:id)';
                    $vars[':id']  = $this->forums;
                }
            }

            $query = 'DELETE
                FROM ::forum_subscriptions
                WHERE ' . \implode(' AND ', $where);

            $this->c->DB->exec($query, $vars);
        }

        unset($where[':id'], $vars[':id']);

        if ($all || ! empty($this->topics)) {
            if (! empty($this->topics)) {
                if (1 === \count($this->topics)) {
                    $where[':id'] = 'topic_id=?i:id';
                    $vars[':id']  = \reset($this->topics);
                } else {
                    $where[':id'] = 'topic_id IN(?ai:id)';
                    $vars[':id']  = $this->topics;
                }
            }

            $query = 'DELETE
                FROM ::topic_subscriptions
                WHERE ' . \implode(' AND ', $where);

            $this->c->DB->exec($query, $vars);
        }

        return true;
    }

    const FORUMS_DATA = 1;
    const TOPICS_DATA = 2;
    const ALL_DATA    = 3;

    /**
     * Возвращает информацию по подпискам
     */
    public function info(DataModel $model, int $type = self::ALL_DATA): array
    {
        $result = [];

        if ($model instanceof User) {
            $vars = [
                ':uid' => $model->id,
            ];

            if (self::FORUMS_DATA & $type) {
                if (
                    '1' != $this->c->config->o_forum_subscriptions
                    || $model->isGuest
                ) {
                    $result[self::FORUMS_DATA] = null;
                } else {
                    $query = 'SELECT forum_id
                        FROM ::forum_subscriptions
                        WHERE user_id=?i:uid';

                    $result[self::FORUMS_DATA] = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
                }
            }

            if (self::TOPICS_DATA & $type) {
                if (
                    '1' != $this->c->config->o_topic_subscriptions
                    || $model->isGuest
                ) {
                    $result[self::TOPICS_DATA] = null;
                } else {
                    $query = 'SELECT topic_id
                        FROM ::topic_subscriptions
                        WHERE user_id=?i:uid';

                    $result[self::TOPICS_DATA] = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
                }
            }
        } else {
            throw new InvalidArgumentException('Expected only User');
        }

        return $result;
    }
}
