/*
 *
 *    Copyright (c) 2023 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *      This file defines the interface to store subscription information.
 */

#pragma once

#include <app/ReadClient.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CommonIterator.h>

namespace chip {
namespace app {

/**
 * The SubscriptionResumptionStorage interface is used to persist subscriptions when they are established.
 */
class SubscriptionResumptionStorage
{
public:
    // Structs to hold path param values as is_trivial struct
    struct AttributePathParamsValues
    {
        ClusterId mClusterId;
        AttributeId mAttributeId;
        EndpointId mEndpointId;
        void SetValues(const AttributePathParams & params)
        {
            mEndpointId  = params.mEndpointId;
            mClusterId   = params.mClusterId;
            mAttributeId = params.mAttributeId;
        }
        AttributePathParams GetParams() { return AttributePathParams(mEndpointId, mClusterId, mAttributeId); }
    };
    struct EventPathParamsValues
    {
        ClusterId mClusterId;
        EventId mEventId;
        EndpointId mEndpointId;
        bool mIsUrgentEvent;
        void SetValues(const EventPathParams & params)
        {
            mEndpointId    = params.mEndpointId;
            mClusterId     = params.mClusterId;
            mEventId       = params.mEventId;
            mIsUrgentEvent = params.mIsUrgentEvent;
        }
        EventPathParams GetParams() { return EventPathParams(mEndpointId, mClusterId, mEventId, mIsUrgentEvent); }
    };

    /**
     * Struct to hold information about subscriptions
     */
    struct SubscriptionInfo
    {
        NodeId mNodeId;
        FabricIndex mFabricIndex;
        SubscriptionId mSubscriptionId;
#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
        uint32_t mResumptionRetries;
#endif
        uint16_t mMinInterval;
        uint16_t mMaxInterval;
        bool mFabricFiltered;
        Platform::ScopedMemoryBufferWithSize<AttributePathParamsValues> mAttributePaths;
        Platform::ScopedMemoryBufferWithSize<EventPathParamsValues> mEventPaths;
        CHIP_ERROR SetAttributePaths(const SingleLinkedListNode<AttributePathParams> * pAttributePathList)
        {
            mAttributePaths.Free();
            if (!pAttributePathList)
            {
                return CHIP_NO_ERROR;
            }
            const SingleLinkedListNode<AttributePathParams> * attributePath = pAttributePathList;
            size_t attributePathCount                                       = 0;
            while (attributePath)
            {
                attributePathCount++;
                attributePath = attributePath->mpNext;
            }
            VerifyOrReturnError((attributePathCount * sizeof(AttributePathParamsValues)) <= UINT16_MAX, CHIP_ERROR_NO_MEMORY);
            mAttributePaths.Calloc(attributePathCount);
            VerifyOrReturnError(mAttributePaths.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
            attributePath = pAttributePathList;
            for (size_t i = 0; i < attributePathCount; i++)
            {
                mAttributePaths[i].SetValues(attributePath->mValue);
                attributePath = attributePath->mpNext;
            }
            return CHIP_NO_ERROR;
        }
        CHIP_ERROR SetEventPaths(const SingleLinkedListNode<EventPathParams> * pEventPathList)
        {
            mEventPaths.Free();
            if (!pEventPathList)
            {
                return CHIP_NO_ERROR;
            }
            const SingleLinkedListNode<EventPathParams> * eventPath = pEventPathList;
            size_t eventPathCount                                   = 0;
            while (eventPath)
            {
                eventPathCount++;
                eventPath = eventPath->mpNext;
            }
            VerifyOrReturnError((eventPathCount * sizeof(EventPathParamsValues)) <= UINT16_MAX, CHIP_ERROR_NO_MEMORY);
            mEventPaths.Calloc(eventPathCount);
            VerifyOrReturnError(mEventPaths.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
            eventPath = pEventPathList;
            for (size_t i = 0; i < eventPathCount; i++)
            {
                mEventPaths[i].SetValues(eventPath->mValue);
                eventPath = eventPath->mpNext;
            }
            return CHIP_NO_ERROR;
        }
    };

    using SubscriptionInfoIterator = CommonIterator<SubscriptionInfo>;

    virtual ~SubscriptionResumptionStorage(){};

    /**
     * Iterate through persisted subscriptions
     *
     * @return A valid iterator on success. Use CommonIterator accessor to retrieve SubscriptionInfo
     */
    virtual SubscriptionInfoIterator * IterateSubscriptions() = 0;

    /**
     * Save subscription resumption information to storage.
     *
     * @param subscriptionInfo the subscription information to save - caller should expect the passed in value is consumed
     */
    virtual CHIP_ERROR Save(SubscriptionInfo & subscriptionInfo) = 0;

    /**
     * Delete subscription resumption information by node ID, fabric index, and subscription ID.
     */
    virtual CHIP_ERROR Delete(NodeId nodeId, FabricIndex fabricIndex, SubscriptionId subscriptionId) = 0;

    /**
     * Remove all subscription resumption information associated with the specified
     * fabric index.  If no entries for the fabric index exist, this is a no-op
     * and is considered successful.
     *
     * @param fabricIndex the index of the fabric for which to remove subscription resumption information
     */
    virtual CHIP_ERROR DeleteAll(FabricIndex fabricIndex) = 0;
};
} // namespace app
} // namespace chip
