<?php

namespace Shm\ShmChat\SchemaChat;

use Shm\Collection\Collection;
use Shm\Shm;
use Shm\ShmDB\mDB;
use Shm\ShmTypes\StructureType;

class ShmChannels extends Collection
{

    private static $joinablePipelineHandler = null;

    private static bool $indexesEnsured = false;

    public static function setJoinablePipelineHandler(callable $pipelineHandler)
    {
        self::$joinablePipelineHandler = $pipelineHandler;
    }

    public static function hasJoinablePipelineHandler(): bool
    {
        return self::$joinablePipelineHandler !== null;
    }

    public static function getJoinablePipelineHandler(): array
    {
        if (self::$joinablePipelineHandler) {
            return (self::$joinablePipelineHandler)();
        }

        return [];
    }

    public static function ensureIndexes(): void
    {
        if (self::$indexesEnsured) {
            return;
        }
        self::$indexesEnsured = true;

        $collection = self::_collection();

        self::createIndexIfMissing($collection, [
            'members.owner' => 1,
            'members.ownerCollection' => 1,
            'closed' => 1,
            'updated_at' => -1,
        ]);

        self::createIndexIfMissing($collection, [
            'space' => 1,
            'type' => 1,
            'members.owner' => 1,
        ]);

        self::createIndexIfMissing($collection, [
            'reference.documentCollection' => 1,
            'reference.documentId' => 1,
        ]);
    }

    private static function createIndexIfMissing(\MongoDB\Collection $collection, array $keys, array $options = []): void
    {
        foreach ($collection->listIndexes() as $index) {
            if (($index['key'] ?? []) === $keys) {
                return;
            }
        }

        $collection->createIndex($keys, $options);
    }

    public function schema(): ?StructureType
    {
        $schema = Shm::structure([
            "space" => Shm::string(),
            "name" => Shm::string(),
            'lastMessage' => ShmChannelMessages::ID(),
            "payload" =>  Shm::structure([
                "*" => Shm::mixed()
            ]),
            'reference' => Shm::arrayOf(Shm::structure([
                'documentId' => Shm::ID(),
                'documentCollection' => Shm::string(),
            ])),
            'members' => Shm::arrayOf(Shm::structure([
                "owner" => Shm::ID(),
                "ownerCollection" => Shm::string(),
                "role" => Shm::enum([
                    "member" => "Участник",
                    "owner" => "Владелец",
                    "admin" => "Администратор",
                    "support" => "Поддержка",
                ])->default("member"),
            ])),
            'closed' => Shm::boolean()->default(false),

            'unreadCounts' => Shm::structure([
                '*' => Shm::int()
            ]),

            'type' => Shm::enum([
                'default' => 'Обычный чат',
                'support' => 'Поддержка',
                'supportSystem' => 'Поддержка системы',
            ])->default('default'),

            'pinnedMessageId' => ShmChannelMessages::ID(),
        ]);

        return $schema;
    }

    public static function touchLastMessage($channelId, $messageId): void
    {
        ShmChannels::structure()->updateOne(
            ['_id' => mDB::id($channelId)],
            ['$set' => ['lastMessage' => mDB::id($messageId)]]
        );
    }

    public static function setPinnedMessageId($channelId, $messageId = null): void
    {
        ShmChannels::structure()->updateOne(
            ['_id' => mDB::id($channelId)],
            ['$set' => ['pinnedMessageId' => $messageId !== null ? mDB::id($messageId) : null]]
        );
    }

    public static function zeroUnreadCounts($channelId, $memberId): void
    {
        $memberKey = 'unreadCounts.' . (string) $memberId;

        ShmChannels::structure()->updateOne(
            [
                '_id' => mDB::id($channelId),
                $memberKey => ['$gt' => 0],
            ],
            ['$set' => [$memberKey => 0]]
        );
    }

    public static function getTotalUnreadByOwner($ownerId): int
    {
        $ownerId = mDB::id($ownerId);
        $ownerKey = (string) $ownerId;

        $result = ShmChannels::structure()->aggregate([
            [
                '$match' => [
                    'members.owner' => $ownerId,
                    'closed' => ['$ne' => true],
                ],
            ],
            [
                '$addFields' => [
                    'myUnread' => ['$ifNull' => ['$unreadCounts.' . $ownerKey, 0]],
                ],
            ],
            [
                '$group' => [
                    '_id' => null,
                    'total' => ['$sum' => '$myUnread'],
                ],
            ],
        ])->toArray();

        return (int) ($result[0]['total'] ?? 0);
    }

    /**
     * @param mixed $senderOwnerId
     * @param string|null $senderOwnerCollection
     */
    public static function updateUnreadCounts($channelId, $senderOwnerId = null, $senderOwnerCollection = null): void
    {
        $channel = ShmChannels::structure()->findOne(
            ['_id' => mDB::id($channelId)],
            ['projection' => ['members' => 1]]
        );

        if (!$channel) {
            return;
        }

        $inc = [];
        $senderOwnerId = $senderOwnerId !== null ? (string) $senderOwnerId : null;

        foreach ($channel->members ?? [] as $member) {
            $memberId = (string) $member->owner;

            if (
                $senderOwnerId !== null
                && $memberId === $senderOwnerId
                && ($member->ownerCollection ?? null) === $senderOwnerCollection
            ) {
                continue;
            }

            $inc['unreadCounts.' . $memberId] = 1;
        }

        if ($inc === []) {
            return;
        }

        ShmChannels::structure()->updateOne(
            ['_id' => mDB::id($channelId)],
            ['$inc' => $inc]
        );
    }
}