/**
 *
 *    Copyright (c) 2025 Project CHIP Authors
 *    All rights reserved.
 *
 *    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 CommodityTariffServer.h
 * @brief Header for Matter Commodity Tariff Cluster implementation
 *
 * This file contains the implementation of the Matter Commodity Tariff Cluster,
 * including attribute management, data structures, and server-side handling.
 */

#pragma once

#include "CommodityTariffAttrsDataMgmt.h"

#include <app/AttributeAccessInterface.h>
#include <app/CommandHandlerInterface.h>
#include <app/ConcreteAttributePath.h>
#include <app/InteractionModelEngine.h>
#include <app/reporting/reporting.h>
#include <array>
#include <cstdint>
#include <functional>

namespace chip {
namespace app {
namespace Clusters {
namespace CommodityTariff {

/**
 * @class Delegate
 * @brief Core tariff data management and processing class
 *
 * This class provides:
 * - Storage for both primary and current attributes
 * - Tariff update processing pipeline
 * - Attribute access methods
 * - Data validation and change management
 */
class Delegate
{
public:
    virtual ~Delegate() = default;

    void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; }

    /**
     * @brief Set the current feature map for this tariff instance
     * @param aFeature The current feature map value
     */
    void SetFeatures(BitMask<Feature> aFeature) { mFeature = aFeature; }

    bool HasFeature(Feature aFeature) { return mFeature.Has(aFeature); }

    void SetTariffUpdCb(std::function<void(bool, const AttributeId *, size_t)> cb) { mTariffDataUpdatedCb = cb; }

    // Attribute accessors - manually defined for each attribute
    DataModel::Nullable<Globals::TariffUnitEnum> & GetTariffUnit() { return mTariffUnit_MgmtObj.GetValue(); }
    DataModel::Nullable<uint32_t> & GetStartDate() { return mStartDate_MgmtObj.GetValue(); }
    DataModel::Nullable<int16_t> & GetDefaultRandomizationOffset() { return mDefaultRandomizationOffset_MgmtObj.GetValue(); }
    DataModel::Nullable<DayEntryRandomizationTypeEnum> & GetDefaultRandomizationType()
    {
        return mDefaultRandomizationType_MgmtObj.GetValue();
    }
    DataModel::Nullable<Structs::TariffInformationStruct::Type> & GetTariffInfo() { return mTariffInfo_MgmtObj.GetValue(); }
    DataModel::Nullable<DataModel::List<Structs::DayEntryStruct::Type>> & GetDayEntries() { return mDayEntries_MgmtObj.GetValue(); }
    DataModel::Nullable<DataModel::List<Structs::DayPatternStruct::Type>> & GetDayPatterns()
    {
        return mDayPatterns_MgmtObj.GetValue();
    }
    DataModel::Nullable<DataModel::List<Structs::TariffComponentStruct::Type>> & GetTariffComponents()
    {
        return mTariffComponents_MgmtObj.GetValue();
    }
    DataModel::Nullable<DataModel::List<Structs::TariffPeriodStruct::Type>> & GetTariffPeriods()
    {
        return mTariffPeriods_MgmtObj.GetValue();
    }
    DataModel::Nullable<DataModel::List<Structs::DayStruct::Type>> & GetIndividualDays()
    {
        return mIndividualDays_MgmtObj.GetValue();
    }
    DataModel::Nullable<DataModel::List<Structs::CalendarPeriodStruct::Type>> & GetCalendarPeriods()
    {
        return mCalendarPeriods_MgmtObj.GetValue();
    }

    enum CommodityTariffAttrTypeEnum
    {
        kTariffUnit,
        kStartDate,
        kDefaultRandomizationOffset,
        kDefaultRandomizationType,
        kTariffInfo,
        kDayEntries,
        kDayPatterns,
        kTariffComponents,
        kTariffPeriods,
        kIndividualDays,
        kCalendarPeriods,
        kAttrMax
    };

    /**
     * @brief Get the management object for a given attribute type enum
     * @param aType The attribute type enum to retrieve
     * @return Reference to the corresponding management object
     */
    CommodityTariffAttrsDataMgmt::CTC_BaseDataClassBase & GetMgmtObj(CommodityTariffAttrTypeEnum aType);

private:
    // Primary attribute storage and management
    TariffUnitDataClass mTariffUnit_MgmtObj{};
    StartDateDataClass mStartDate_MgmtObj{};
    DefaultRandomizationOffsetDataClass mDefaultRandomizationOffset_MgmtObj{};
    DefaultRandomizationTypeDataClass mDefaultRandomizationType_MgmtObj{};
    TariffInfoDataClass mTariffInfo_MgmtObj{};
    DayEntriesDataClass mDayEntries_MgmtObj{};
    DayPatternsDataClass mDayPatterns_MgmtObj{};
    TariffComponentsDataClass mTariffComponents_MgmtObj{};
    TariffPeriodsDataClass mTariffPeriods_MgmtObj{};
    IndividualDaysDataClass mIndividualDays_MgmtObj{};
    CalendarPeriodsDataClass mCalendarPeriods_MgmtObj{};

protected:
    EndpointId mEndpointId = 0; ///< Associated Matter endpoint ID
    BitMask<Feature> mFeature;
    std::function<void(bool, const AttributeId *, size_t)> mTariffDataUpdatedCb;
    bool DelayedTariffUpdateIsActive = false;
};

/**
 * @class CommodityTariffServer
 * @brief Matter server implementation for the Commodity Tariff cluster
 *
 * This class handles:
 * - Attribute access interface
 * - Command handling
 * - Cluster feature management
 */
class Instance : public AttributeAccessInterface, public CommandHandlerInterface
{
public:
    /**
     * @brief Construct a new Commodity Tariff Server instance
     * @param aEndpointId The Matter endpoint ID
     * @param aProvider The data provider delegate
     * @param aFeature Bitmask of supported features
     */
    Instance(EndpointId aEndpointId, Delegate & aDelegate, Feature aFeature) :
        AttributeAccessInterface(MakeOptional(aEndpointId), Id), CommandHandlerInterface(MakeOptional(aEndpointId), Id),
        mDelegate(aDelegate), mFeature(aFeature)
    {
        /* set the base class delegates endpointId */
        mDelegate.SetEndpointId(aEndpointId);
        mEndpointId = aEndpointId;
        mDelegate.SetTariffUpdCb([this](bool is_erased, const AttributeId * updatedAttrIds, size_t count) {
            this->TariffDataUpdatedCb(is_erased, updatedAttrIds, count);
        });
        mDelegate.SetFeatures(aFeature);
    }

    ~Instance() { Shutdown(); }

    CHIP_ERROR Init();
    void Shutdown();

    bool HasFeature(Feature aFeature) const;

    void AttributeUpdCb(AttributeId aAttrId)
    {
        ChipLogProgress(NotSpecified, "EGW-CTC: The value for attribute (Id %" PRIu32 ") updated", aAttrId);
        MatterReportingAttributeChangeCallback(mEndpointId, CommodityTariff::Id, aAttrId);
    }

    void TariffTimeAttrsSync();

    /**
     * @struct CurrentTariffAttrsCtx
     * @brief Context for current tariff attributes
     */
    struct CurrentTariffAttrsCtx
    {
        Delegate * mTariffProvider;
        EndpointId mEndpointId;
    };

private:
    Delegate & mDelegate;
    BitMask<Feature> mFeature;

    EndpointId mEndpointId;

    // Current attributes storage
    DataModel::Nullable<Structs::DayStruct::Type> mCurrentDay;
    DataModel::Nullable<Structs::DayStruct::Type> mNextDay;
    DataModel::Nullable<Structs::DayEntryStruct::Type> mCurrentDayEntry;
    DataModel::Nullable<Structs::DayEntryStruct::Type> mNextDayEntry;
    DataModel::Nullable<uint32_t> mCurrentDayEntryDate;
    DataModel::Nullable<uint32_t> mNextDayEntryDate;

    TariffComponentsDataClass mCurrentTariffComponents_MgmtObj{};
    TariffComponentsDataClass mNextTariffComponents_MgmtObj{};

    // Attribute accessors
    DataModel::Nullable<Structs::DayStruct::Type> & GetCurrentDay() { return mCurrentDay; }
    DataModel::Nullable<Structs::DayStruct::Type> & GetNextDay() { return mNextDay; }
    DataModel::Nullable<Structs::DayEntryStruct::Type> & GetCurrentDayEntry() { return mCurrentDayEntry; }
    DataModel::Nullable<Structs::DayEntryStruct::Type> & GetNextDayEntry() { return mNextDayEntry; }
    DataModel::Nullable<uint32_t> & GetCurrentDayEntryDate() { return mCurrentDayEntryDate; }
    DataModel::Nullable<uint32_t> & GetNextDayEntryDate() { return mNextDayEntryDate; }

    template <typename T>
    CHIP_ERROR SetValue(T & currValue, T & newValue, uint32_t attrId);

    CHIP_ERROR SetCurrentDay(DataModel::Nullable<Structs::DayStruct::Type> & newValue)
    {
        return SetValue(mCurrentDay, newValue, Attributes::CurrentDay::Id);
    }
    CHIP_ERROR SetNextDay(DataModel::Nullable<Structs::DayStruct::Type> & newValue)
    {
        return SetValue(mNextDay, newValue, Attributes::NextDay::Id);
    }
    CHIP_ERROR SetCurrentDayEntry(DataModel::Nullable<Structs::DayEntryStruct::Type> & newValue)
    {
        return SetValue(mCurrentDayEntry, newValue, Attributes::CurrentDayEntry::Id);
    }
    CHIP_ERROR SetNextDayEntry(DataModel::Nullable<Structs::DayEntryStruct::Type> & newValue)
    {
        return SetValue(mNextDayEntry, newValue, Attributes::NextDayEntry::Id);
    }
    CHIP_ERROR SetCurrentDayEntryDate(DataModel::Nullable<uint32_t> & newValue)
    {
        return SetValue(mCurrentDayEntryDate, newValue, Attributes::CurrentDayEntryDate::Id);
    }
    CHIP_ERROR SetNextDayEntryDate(DataModel::Nullable<uint32_t> & newValue)
    {
        return SetValue(mNextDayEntryDate, newValue, Attributes::NextDayEntryDate::Id);
    }

    DataModel::Nullable<DataModel::List<Structs::TariffComponentStruct::Type>> & GetCurrentTariffComponents()
    {
        return mCurrentTariffComponents_MgmtObj.GetValue();
    }
    DataModel::Nullable<DataModel::List<Structs::TariffComponentStruct::Type>> & GetNextTariffComponents()
    {
        return mNextTariffComponents_MgmtObj.GetValue();
    }

    CurrentTariffAttrsCtx mServerTariffAttrsCtx;

    void TariffDataUpdatedCb(bool is_erased, const AttributeId * aUpdatedAttrIds, size_t aCount);
    void ResetCurrentAttributes();

    // AttributeAccessInterface implementation
    CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;

    // CommandHandlerInterface implementation
    void InvokeCommand(HandlerContext & handlerContext) override;

    // Command handlers
    void HandleGetDayEntry(HandlerContext & ctx, const Commands::GetDayEntry::DecodableType & commandData);
    void HandleGetTariffComponent(HandlerContext & ctx, const Commands::GetTariffComponent::DecodableType & commandData);

    // Current attrs (time depended) update methods
    void InitCurrentAttrs();
    CHIP_ERROR UpdateCurrentAttrs();
    CHIP_ERROR UpdateDayInformation(uint32_t now);
    CHIP_ERROR UpdateDayEntryInformation(uint32_t now);
    void DeinitCurrentAttrs();
};

} // namespace CommodityTariff
} // namespace Clusters
} // namespace app
} // namespace chip
