<?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 bool $indexesEnsured = false;

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


        return;

        $collection = self::_collection();

        self::createIndexIfMissing($collection, [
            ShmChannelsField::MEMBERS__IDX__OWNER => 1,
            ShmChannelsField::MEMBERS__IDX__OWNER_COLLECTION => 1,
            ShmChannelsField::CLOSED => 1,
            ShmChannelsField::UPDATED_AT => -1,
        ]);

        self::createIndexIfMissing($collection, [
            ShmChannelsField::SPACE => 1,
            ShmChannelsField::MEMBERS__IDX__OWNER => 1,
        ]);

        self::createIndexIfMissing($collection, [
            ShmChannelsField::JOINABLE_MEMBERS__IDX__OWNER => 1,
            ShmChannelsField::JOINABLE_MEMBERS__IDX__OWNER_COLLECTION => 1,
        ]);

        self::createIndexIfMissing($collection, [
            ShmChannelsField::REFERENCE__IDX__DOCUMENT_ID => 1,
            ShmChannelsField::REFERENCE__IDX__DOCUMENT_COLLECTION => 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(),
                "isAdmin" => Shm::bool()->default(false),
            ])),
            'joinableMembers' => Shm::arrayOf(Shm::structure([
                "owner" => Shm::ID(),
                "ownerCollection" => Shm::string(),
                "isAdmin" => Shm::bool()->default(false),
            ])),
            'closed' => Shm::boolean()->default(false),

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

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

        return $schema;
    }

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

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

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

        ShmChannels::structure()->updateOne(
            [
                ShmChannelsField::_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' => [
                    ShmChannelsField::MEMBERS__IDX__OWNER => $ownerId,
                    ShmChannelsField::CLOSED => ['$ne' => true],
                ],
            ],
            [
                '$addFields' => [
                    'myUnread' => ['$ifNull' => ['$' . ShmChannelsField::UNREAD_COUNTS . '.' . $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(
            [ShmChannelsField::_ID => mDB::id($channelId)],
            ['projection' => [ShmChannelsField::MEMBERS => 1]]
        );

        if (!$channel) {
            return;
        }

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

        foreach (ShmChannelsField::MEMBERS_GET($channel, []) as $member) {
            $memberId = (string) ShmChannelsField::EL_MEMBERS__OWNER_GET($member);

            if (
                $senderOwnerId !== null
                && $memberId === $senderOwnerId
                && (ShmChannelsField::EL_MEMBERS__OWNER_COLLECTION_GET($member) ?? null) === $senderOwnerCollection
            ) {
                continue;
            }

            $inc[ShmChannelsField::UNREAD_COUNTS . '.' . $memberId] = 1;
        }

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

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