/*
 *  Copyright (c) 2020, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file
 *   This file includes definitions for SRP server.
 */

#ifndef NET_SRP_SERVER_HPP_
#define NET_SRP_SERVER_HPP_

#include "openthread-core-config.h"

#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE

#if !OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#error "OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE is required for OPENTHREAD_CONFIG_SRP_SERVER_ENABLE"
#endif

#if !OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
#error "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE is required for OPENTHREAD_CONFIG_SRP_SERVER_ENABLE"
#endif

#if !OPENTHREAD_CONFIG_ECDSA_ENABLE
#error "OPENTHREAD_CONFIG_ECDSA_ENABLE is required for OPENTHREAD_CONFIG_SRP_SERVER_ENABLE"
#endif

#include <openthread/ip6.h>
#include <openthread/srp_server.h>

#include "common/array.hpp"
#include "common/as_core_type.hpp"
#include "common/callback.hpp"
#include "common/clearable.hpp"
#include "common/heap.hpp"
#include "common/heap_allocatable.hpp"
#include "common/heap_array.hpp"
#include "common/heap_data.hpp"
#include "common/heap_string.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/retain_ptr.hpp"
#include "common/timer.hpp"
#include "crypto/ecdsa.hpp"
#include "net/dns_types.hpp"
#include "net/dnssd.hpp"
#include "net/ip6.hpp"
#include "net/ip6_address.hpp"
#include "net/udp6.hpp"
#include "thread/network_data_publisher.hpp"

struct otSrpServerHost
{
};

struct otSrpServerService
{
};

namespace ot {

namespace Dns {
namespace ServiceDiscovery {
class Server;
}
} // namespace Dns

#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
namespace BorderRouter {
class RoutingManager;
}
#endif

namespace Srp {

class AdvertisingProxy;

/**
 * Implements the SRP server.
 */
class Server : public InstanceLocator, private NonCopyable
{
    friend class NetworkData::Publisher;
    friend class UpdateMetadata;
    friend class Service;
    friend class Host;
    friend class Dns::ServiceDiscovery::Server;
    friend class AdvertisingProxy;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
    friend class BorderRouter::RoutingManager;
#endif

    enum RetainName : bool
    {
        kDeleteName = false,
        kRetainName = true,
    };

    enum NotifyMode : bool
    {
        kDoNotNotifyServiceHandler = false,
        kNotifyServiceHandler      = true,
    };

#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
    static constexpr Dnssd::RequestId kInvalidRequestId = 0;
#endif

public:
    static constexpr uint16_t kUdpPortMin = OPENTHREAD_CONFIG_SRP_SERVER_UDP_PORT_MIN; ///< The reserved min port.
    static constexpr uint16_t kUdpPortMax = OPENTHREAD_CONFIG_SRP_SERVER_UDP_PORT_MAX; ///< The reserved max port.

    static_assert(kUdpPortMin <= kUdpPortMax, "invalid port range");

    /**
     * The ID of SRP service update transaction.
     */
    typedef otSrpServerServiceUpdateId ServiceUpdateId;

    /**
     * The SRP server lease information of a host/service.
     */
    typedef otSrpServerLeaseInfo LeaseInfo;

    /**
     * Represents the address mode used by the SRP server.
     *
     * Address mode specifies how the address and port number are determined by the SRP server and how this info ins
     * published in the Thread Network Data.
     */
    enum AddressMode : uint8_t
    {
        kAddressModeUnicast = OT_SRP_SERVER_ADDRESS_MODE_UNICAST, ///< Unicast address mode.
        kAddressModeAnycast = OT_SRP_SERVER_ADDRESS_MODE_ANYCAST, ///< Anycast address mode.
    };

    class Host;

    /**
     * Represents the state of SRP server.
     */
    enum State : uint8_t
    {
        kStateDisabled = OT_SRP_SERVER_STATE_DISABLED, ///< Server is disabled.
        kStateRunning  = OT_SRP_SERVER_STATE_RUNNING,  ///< Server is enabled and running.
        kStateStopped  = OT_SRP_SERVER_STATE_STOPPED,  ///< Server is enabled but stopped.
    };

    /**
     * Implements a server-side SRP service.
     */
    class Service : public otSrpServerService,
                    public LinkedListEntry<Service>,
                    private Heap::Allocatable<Service>,
                    private NonCopyable
    {
        friend class Server;
        friend class LinkedList<Service>;
        friend class LinkedListEntry<Service>;
        friend class Heap::Allocatable<Service>;
        friend class AdvertisingProxy;

    public:
        /**
         * Tells if the SRP service has been deleted.
         *
         * A SRP service can be deleted but retains its name for future uses.
         * In this case, the service instance is not removed from the SRP server/registry.
         * It is guaranteed that all services are deleted if the host is deleted.
         *
         * @returns  TRUE if the service has been deleted, FALSE if not.
         */
        bool IsDeleted(void) const { return mIsDeleted; }

        /**
         * Gets the full service instance name of the service.
         *
         * @returns  A pointer service instance name (as a null-terminated C string).
         */
        const char *GetInstanceName(void) const { return mInstanceName.AsCString(); }

        /**
         * Gets the service instance label of the service.
         *
         * @returns  A pointer service instance label (as a null-terminated C string).
         */
        const char *GetInstanceLabel(void) const { return mInstanceLabel.AsCString(); }

        /**
         * Gets the full service name of the service.
         *
         * @returns  A pointer service name (as a null-terminated C string).
         */
        const char *GetServiceName(void) const { return mServiceName.AsCString(); }

        /**
         * Gets number of sub-types of this service.
         *
         * @returns The number of sub-types.
         */
        uint16_t GetNumberOfSubTypes(void) const { return mSubTypes.GetLength(); }

        /**
         * Gets the sub-type service name (full name) at a given index.
         *
         * The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.".
         *
         * @param[in] aIndex   The index to get.
         *
         * @returns A pointer to sub-type service name at @p aIndex, or `nullptr` if none at this index.
         */
        const char *GetSubTypeServiceNameAt(uint16_t aIndex) const;

        /**
         * Indicates whether or not service has a given sub-type.
         *
         * @param[in] aSubTypeServiceName  The sub-type service name (full name).
         *
         * @retval TRUE   Service contains the sub-type @p aSubTypeServiceName.
         * @retval FALSE  Service does not contain the sub-type @p aSubTypeServiceName.
         */
        bool HasSubTypeServiceName(const char *aSubTypeServiceName) const;

        /**
         * Parses a sub-type service name (full name) and extracts the sub-type label.
         *
         * The full service name for a sub-type service follows "<sub-label>._sub.<service-labels>.<domain>.".
         *
         * @param[in]  aSubTypeServiceName  A sub-type service name (full name).
         * @param[out] aLabel               A pointer to a buffer to copy the extracted sub-type label.
         * @param[in]  aLabelSize           Maximum size of @p aLabel buffer.
         *
         * @retval kErrorNone         Name was successfully parsed and @p aLabel was updated.
         * @retval kErrorNoBufs       The sub-type label could not fit in @p aLabel buffer (number of chars from label
         *                            that could fit are copied in @p aLabel ensuring it is null-terminated).
         * @retval kErrorInvalidArgs  @p aSubTypeServiceName is not a valid sub-type format.
         */
        static Error ParseSubTypeServiceName(const char *aSubTypeServiceName, char *aLabel, uint8_t aLabelSize);

        /**
         * Returns the TTL of the service instance.
         *
         * @returns The TTL of the service instance.
         */
        uint32_t GetTtl(void) const { return mTtl; }

        /**
         * Returns the port of the service instance.
         *
         * @returns  The port of the service.
         */
        uint16_t GetPort(void) const { return mPort; }

        /**
         * Returns the weight of the service instance.
         *
         * @returns  The weight of the service.
         */
        uint16_t GetWeight(void) const { return mWeight; }

        /**
         * Returns the priority of the service instance.
         *
         * @returns  The priority of the service.
         */
        uint16_t GetPriority(void) const { return mPriority; }

        /**
         * Returns the TXT record data of the service instance.
         *
         * @returns A pointer to the buffer containing the TXT record data.
         */
        const uint8_t *GetTxtData(void) const { return mTxtData.GetBytes(); }

        /**
         * Returns the TXT record data length of the service instance.
         *
         * @return The TXT record data length (number of bytes in buffer returned from `GetTxtData()`).
         */
        uint16_t GetTxtDataLength(void) const { return mTxtData.GetLength(); }

        /**
         * Returns the host which the service instance reside on.
         *
         * @returns  A reference to the host instance.
         */
        const Host &GetHost(void) const { return *mHost; }

        /**
         * Returns the LEASE time of the service.
         *
         * @returns  The LEASE time in seconds.
         */
        uint32_t GetLease(void) const { return mLease; }

        /**
         * Returns the KEY-LEASE time of the key of the service.
         *
         * @returns  The KEY-LEASE time in seconds.
         */
        uint32_t GetKeyLease(void) const { return mKeyLease; }

        /**
         * Returns the expire time (in milliseconds) of the service.
         *
         * @returns  The service expire time in milliseconds.
         */
        TimeMilli GetExpireTime(void) const;

        /**
         * Returns the key expire time (in milliseconds) of the service.
         *
         * @returns  The service key expire time in milliseconds.
         */
        TimeMilli GetKeyExpireTime(void) const;

        /**
         * Gets the LEASE and KEY-LEASE information of a given service.
         *
         * @param[out]  aLeaseInfo  A reference to a LeaseInfo instance. It contains the LEASE time, KEY-LEASE time,
         *                          remaining LEASE time and the remaining KEY-LEASE time.
         */
        void GetLeaseInfo(LeaseInfo &aLeaseInfo) const;

        /**
         * Indicates whether this service matches a given service instance name.
         *
         * @param[in]  aInstanceName  The service instance name.
         *
         * @retval  TRUE   If the service matches the service instance name.
         * @retval  FALSE  If the service does not match the service instance name.
         */
        bool MatchesInstanceName(const char *aInstanceName) const;

        /**
         * Tells whether this service matches a given service name.
         *
         * @param[in] aServiceName  The full service name to match.
         *
         * @retval  TRUE   If the service matches the full service name.
         * @retval  FALSE  If the service does not match the full service name.
         */
        bool MatchesServiceName(const char *aServiceName) const;

    private:
        enum Action : uint8_t
        {
            kAddNew,
            kUpdateExisting,
            kKeepUnchanged,
            kRemoveButRetainName,
            kFullyRemove,
            kLeaseExpired,
            kKeyLeaseExpired,
        };

        Error Init(const char *aInstanceName, const char *aInstanceLabel, Host &aHost, TimeMilli aUpdateTime);
        Error SetTxtDataFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
        bool  Matches(const char *aInstanceName) const;
        void  Log(Action aAction) const;

        template <uint16_t kLabelSize>
        static Error ParseSubTypeServiceName(const char *aSubTypeServiceName, char (&aLabel)[kLabelSize])
        {
            return ParseSubTypeServiceName(aSubTypeServiceName, aLabel, kLabelSize);
        }

        Service                  *mNext;
        Heap::String              mInstanceName;
        Heap::String              mInstanceLabel;
        Heap::String              mServiceName;
        Heap::Array<Heap::String> mSubTypes;
        Host                     *mHost;
        Heap::Data                mTxtData;
        uint16_t                  mPriority;
        uint16_t                  mWeight;
        uint16_t                  mPort;
        uint32_t                  mTtl;      // In seconds
        uint32_t                  mLease;    // In seconds
        uint32_t                  mKeyLease; // In seconds
        TimeMilli                 mUpdateTime;
        bool                      mIsDeleted : 1;
        bool                      mIsCommitted : 1;
        bool                      mParsedDeleteAllRrset : 1;
        bool                      mParsedSrv : 1;
        bool                      mParsedTxt : 1;
#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
        bool             mIsRegistered : 1;
        bool             mIsKeyRegistered : 1;
        bool             mIsReplaced : 1;
        bool             mShouldAdvertise : 1;
        bool             mShouldRegisterKey : 1;
        Dnssd::RequestId mAdvId;
        Dnssd::RequestId mKeyAdvId;
#endif
    };

    /**
     * Implements the Host which registers services on the SRP server.
     */
    class Host : public otSrpServerHost,
                 public InstanceLocator,
                 public LinkedListEntry<Host>,
                 private Heap::Allocatable<Host>,
                 private NonCopyable
    {
        friend class Server;
        friend class LinkedListEntry<Host>;
        friend class Heap::Allocatable<Host>;
        friend class AdvertisingProxy;

    public:
        typedef Crypto::Ecdsa::P256::PublicKey Key; ///< Host key (public ECDSA P256 key).

        /**
         * Tells whether the Host object has been deleted.
         *
         * The Host object retains event if the host has been deleted by the SRP client,
         * because the host name may retain.
         *
         * @returns  TRUE if the host is deleted, FALSE if the host is not deleted.
         */
        bool IsDeleted(void) const { return (mLease == 0); }

        /**
         * Returns the full name of the host.
         *
         * @returns  A pointer to the null-terminated full host name.
         */
        const char *GetFullName(void) const { return mFullName.AsCString(); }

        /**
         * Returns addresses of the host.
         *
         * @param[out]  aAddressesNum  The number of the addresses.
         *
         * @returns  A pointer to the addresses array or `nullptr` if no addresses.
         */
        const Ip6::Address *GetAddresses(uint8_t &aAddressesNum) const
        {
            aAddressesNum = ClampToUint8(mAddresses.GetLength());

            return mAddresses.AsCArray();
        }

        /**
         * Returns the TTL of the host.
         *
         * @returns The TTL of the host.
         */
        uint32_t GetTtl(void) const { return mTtl; }

        /**
         * Returns the LEASE time of the host.
         *
         * @returns  The LEASE time in seconds.
         */
        uint32_t GetLease(void) const { return mLease; }

        /**
         * Returns the KEY-LEASE time of the key of the host.
         *
         * @returns  The KEY-LEASE time in seconds.
         */
        uint32_t GetKeyLease(void) const { return mKeyLease; }

        /**
         * Gets the LEASE and KEY-LEASE information of a given host.
         *
         * @param[out]  aLeaseInfo  A reference to a LeaseInfo instance. It contains the LEASE time, KEY-LEASE time,
         *                          remaining LEASE time and the remaining KEY-LEASE time.
         */
        void GetLeaseInfo(LeaseInfo &aLeaseInfo) const;

        /**
         * Returns the key associated with this host.
         *
         * @returns  The host key.
         */
        const Key &GetKey(void) const { return mKey; }

        /**
         * Returns the expire time (in milliseconds) of the host.
         *
         * @returns  The expire time in milliseconds.
         */
        TimeMilli GetExpireTime(void) const;

        /**
         * Returns the expire time (in milliseconds) of the key of the host.
         *
         * @returns  The expire time of the key in milliseconds.
         */
        TimeMilli GetKeyExpireTime(void) const;

        /**
         * Returns the `Service` linked list associated with the host.
         *
         * @returns The `Service` linked list.
         */
        const LinkedList<Service> &GetServices(void) const { return mServices; }

        /*
         * Returns the next service.
         *
         * @param[in] aPrevService   A pointer to the previous service or `nullptr` to start from beginning of the list.
         *
         * @returns  A pointer to the next service or `nullptr` if no more services can be found.
         */
        const Service *GetNextService(const Service *aPrevService) const;

        /**
         * Tells whether the host matches a given full name.
         *
         * @param[in]  aFullName  The full name.
         *
         * @returns  A boolean that indicates whether the host matches the given name.
         */
        bool Matches(const char *aFullName) const;

    private:
        Host(Instance &aInstance, TimeMilli aUpdateTime);
        ~Host(void);

        Error SetFullName(const char *aFullName);
        void  SetTtl(uint32_t aTtl) { mTtl = aTtl; }
        void  SetLease(uint32_t aLease) { mLease = aLease; }
        void  SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; }
        void  SetUseShortLeaseOption(bool aUse) { mUseShortLeaseOption = aUse; }
        bool  ShouldUseShortLeaseOption(void) const { return mUseShortLeaseOption; }
        Error ProcessTtl(uint32_t aTtl);

        Service       *AddNewService(const char *aInstanceName, const char *aInstanceLabel, TimeMilli aUpdateTime);
        void           AddService(Service &aService);
        void           RemoveService(Service *aService, RetainName aRetainName, NotifyMode aNotifyServiceHandler);
        bool           HasService(const char *aInstanceName) const;
        Service       *FindService(const char *aInstanceName);
        const Service *FindService(const char *aInstanceName) const;
        void           FreeAllServices(void);
        void           ClearResources(void);
        Error          AddIp6Address(const Ip6::Address &aIp6Address);

        Host                     *mNext;
        Heap::String              mFullName;
        Heap::Array<Ip6::Address> mAddresses;
        Key                       mKey;
        uint32_t                  mTtl;      // The TTL in seconds.
        uint32_t                  mLease;    // The LEASE time in seconds.
        uint32_t                  mKeyLease; // The KEY-LEASE time in seconds.
        TimeMilli                 mUpdateTime;
        LinkedList<Service>       mServices;
        bool                      mParsedKey : 1;
        bool                      mUseShortLeaseOption : 1; // Use short lease option (lease only 4 bytes).
#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
        bool                  mIsRegistered : 1;
        bool                  mIsKeyRegistered : 1;
        bool                  mIsReplaced : 1;
        bool                  mShouldAdvertise : 1;
        bool                  mShouldRegisterKey : 1;
        Dnssd::RequestId      mAdvId;
        Dnssd::RequestId      mKeyAdvId;
        Dnssd::RequestIdRange mAdvIdRange;
#endif
    };

    /**
     * Handles TTL configuration.
     */
    class TtlConfig : public otSrpServerTtlConfig
    {
        friend class Server;

    public:
        /**
         * Initializes to default TTL configuration.
         */
        TtlConfig(void);

    private:
        bool     IsValid(void) const { return mMinTtl <= mMaxTtl; }
        uint32_t GrantTtl(uint32_t aLease, uint32_t aTtl) const;
    };

    /**
     * Handles LEASE and KEY-LEASE configurations.
     */
    class LeaseConfig : public otSrpServerLeaseConfig
    {
        friend class Server;

    public:
        /**
         * Initialize to default LEASE and KEY-LEASE configurations.
         */
        LeaseConfig(void);

    private:
        bool     IsValid(void) const;
        uint32_t GrantLease(uint32_t aLease) const;
        uint32_t GrantKeyLease(uint32_t aKeyLease) const;
    };

    /**
     * Initializes the SRP server object.
     *
     * @param[in]  aInstance  A reference to the OpenThread instance.
     */
    explicit Server(Instance &aInstance);

    /**
     * Sets the SRP service events handler.
     *
     * @param[in]  aServiceHandler         A service events handler.
     * @param[in]  aServiceHandlerContext  A pointer to arbitrary context information.
     *
     * @note  The handler SHOULD call HandleServiceUpdateResult to report the result of its processing.
     *        Otherwise, a SRP update will be considered failed.
     *
     * @sa  HandleServiceUpdateResult
     */
    void SetServiceHandler(otSrpServerServiceUpdateHandler aServiceHandler, void *aServiceHandlerContext)
    {
        mServiceUpdateHandler.Set(aServiceHandler, aServiceHandlerContext);
    }

    /**
     * Returns the domain authorized to the SRP server.
     *
     * If the domain if not set by SetDomain, "default.service.arpa." will be returned.
     * A trailing dot is always appended even if the domain is set without it.
     *
     * @returns A pointer to the dot-joined domain string.
     */
    const char *GetDomain(void) const { return mDomain.AsCString(); }

    /**
     * Sets the domain on the SRP server.
     *
     * A trailing dot will be appended to @p aDomain if it is not already there.
     * Should only be called before the SRP server is enabled.
     *
     * @param[in]  aDomain  The domain to be set. MUST NOT be `nullptr`.
     *
     * @retval  kErrorNone          Successfully set the domain to @p aDomain.
     * @retval  kErrorInvalidState  The SRP server is already enabled and the Domain cannot be changed.
     * @retval  kErrorInvalidArgs   The argument @p aDomain is not a valid DNS domain name.
     * @retval  kErrorNoBufs        There is no memory to store content of @p aDomain.
     */
    Error SetDomain(const char *aDomain);

    /**
     * Returns the address mode being used by the SRP server.
     *
     * @returns The SRP server's address mode.
     */
    AddressMode GetAddressMode(void) const { return mAddressMode; }

    /**
     * Sets the address mode to be used by the SRP server.
     *
     * @param[in] aMode      The address mode to use.
     *
     * @retval kErrorNone           Successfully set the address mode.
     * @retval kErrorInvalidState   The SRP server is enabled and the address mode cannot be changed.
     */
    Error SetAddressMode(AddressMode aMode);

    /**
     * Gets the sequence number used with anycast address mode.
     *
     * The sequence number is included in "DNS/SRP Service Anycast Address" entry published in the Network Data.
     *
     * @returns The anycast sequence number.
     */
    uint8_t GetAnycastModeSequenceNumber(void) const { return mAnycastSequenceNumber; }

    /**
     * Sets the sequence number used with anycast address mode.
     *
     * @param[in] aSequenceNumber  The sequence number to use.
     *
     * @retval kErrorNone           Successfully set the address mode.
     * @retval kErrorInvalidState   The SRP server is enabled and the sequence number cannot be changed.
     */
    Error SetAnycastModeSequenceNumber(uint8_t aSequenceNumber);

    /**
     * Returns the state of the SRP server.
     *
     * @returns  The state of the server.
     */
    State GetState(void) const { return mState; }

    /**
     * Tells the port the SRP server is listening to.
     *
     * @returns  The port of the server or 0 if the SRP server is not running.
     */
    uint16_t GetPort(void) const { return (mState == kStateRunning) ? mPort : 0; }

    /**
     * Enables/disables the SRP server.
     *
     * @param[in]  aEnabled  A boolean to enable/disable the SRP server.
     */
    void SetEnabled(bool aEnabled);

#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
    /**
     * Enables/disables the auto-enable mode on SRP server.
     *
     * When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server.
     * SRP sever is auto-enabled if/when Border Routing is started it is done with the initial prefix and route
     * configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advert message on
     * infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled when BR is
     * stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached).
     *
     * This mode can be disabled by a `SetAutoEnableMode(false)` call or if the SRP server is explicitly enabled or
     * disabled by a call to `SetEnabled()` method. Disabling auto-enable mode using `SetAutoEnableMode(false` call
     * will not change the current state of SRP sever (e.g., if it is enabled it stays enabled).
     *
     * @param[in] aEnabled    A boolean to enable/disable the auto-enable mode.
     */
    void SetAutoEnableMode(bool aEnabled);

    /**
     * Indicates whether the auto-enable mode is enabled or disabled.
     *
     * @retval TRUE   The auto-enable mode is enabled.
     * @retval FALSE  The auto-enable mode is disabled.
     */
    bool IsAutoEnableMode(void) const { return mAutoEnable; }
#endif

    /**
     * Returns the TTL configuration.
     *
     * @param[out]  aTtlConfig  A reference to the `TtlConfig` instance.
     */
    void GetTtlConfig(TtlConfig &aTtlConfig) const { aTtlConfig = mTtlConfig; }

    /**
     * Sets the TTL configuration.
     *
     * @param[in]  aTtlConfig  A reference to the `TtlConfig` instance.
     *
     * @retval  kErrorNone         Successfully set the TTL configuration
     * @retval  kErrorInvalidArgs  The TTL range is not valid.
     */
    Error SetTtlConfig(const TtlConfig &aTtlConfig);

    /**
     * Returns the LEASE and KEY-LEASE configurations.
     *
     * @param[out]  aLeaseConfig  A reference to the `LeaseConfig` instance.
     */
    void GetLeaseConfig(LeaseConfig &aLeaseConfig) const { aLeaseConfig = mLeaseConfig; }

    /**
     * Sets the LEASE and KEY-LEASE configurations.
     *
     * When a LEASE time is requested from a client, the granted value will be
     * limited in range [aMinLease, aMaxLease]; and a KEY-LEASE will be granted
     * in range [aMinKeyLease, aMaxKeyLease].
     *
     * @param[in]  aLeaseConfig  A reference to the `LeaseConfig` instance.
     *
     * @retval  kErrorNone         Successfully set the LEASE and KEY-LEASE ranges.
     * @retval  kErrorInvalidArgs  The LEASE or KEY-LEASE range is not valid.
     */
    Error SetLeaseConfig(const LeaseConfig &aLeaseConfig);

    /**
     * Returns the `Host` linked list.
     *
     * @returns The `Host` linked list.
     */
    const LinkedList<Host> &GetHosts(void) const { return mHosts; }

    /**
     * Returns the next registered SRP host.
     *
     * @param[in]  aHost  The current SRP host; use `nullptr` to get the first SRP host.
     *
     * @returns  A pointer to the next SRP host or `nullptr` if no more SRP hosts can be found.
     */
    const Host *GetNextHost(const Host *aHost);

    /**
     * Returns the response counters of the SRP server.
     *
     * @returns  A pointer to the response counters of the SRP server.
     */
    const otSrpServerResponseCounters *GetResponseCounters(void) const { return &mResponseCounters; }

    /**
     * Receives the service update result from service handler set by
     * SetServiceHandler.
     *
     * @param[in]  aId     The ID of the service update transaction.
     * @param[in]  aError  The service update result.
     */
    void HandleServiceUpdateResult(ServiceUpdateId aId, Error aError);

private:
    static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header);

    static constexpr uint32_t kDefaultMinLease             = 30;          // 30 seconds.
    static constexpr uint32_t kDefaultMaxLease             = 27u * 3600;  // 27 hours (in seconds).
    static constexpr uint32_t kDefaultMinKeyLease          = 30;          // 30 seconds.
    static constexpr uint32_t kDefaultMaxKeyLease          = 189u * 3600; // 189 hours (in seconds).
    static constexpr uint32_t kDefaultMinTtl               = kDefaultMinLease;
    static constexpr uint32_t kDefaultMaxTtl               = kDefaultMaxLease;
    static constexpr uint32_t kDefaultEventsHandlerTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT;

    static constexpr AddressMode kDefaultAddressMode =
        static_cast<AddressMode>(OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE);

    static constexpr uint16_t kUninitializedPort      = 0;
    static constexpr uint16_t kAnycastAddressModePort = 53;

    // Metadata for a received SRP Update message.
    struct MessageMetadata
    {
        // Indicates whether the `Message` is received directly from a
        // client or from an SRPL partner.
        bool IsDirectRxFromClient(void) const { return (mMessageInfo != nullptr); }

        Dns::UpdateHeader       mDnsHeader;
        Dns::Zone               mDnsZone;
        uint16_t                mOffset;
        TimeMilli               mRxTime;
        TtlConfig               mTtlConfig;
        LeaseConfig             mLeaseConfig;
        const Ip6::MessageInfo *mMessageInfo; // Set to `nullptr` when from SRPL.
    };

    // This class includes metadata for processing a SRP update (register, deregister)
    // and sending DNS response to the client.
    class UpdateMetadata : public InstanceLocator,
                           public LinkedListEntry<UpdateMetadata>,
                           public Heap::Allocatable<UpdateMetadata>
    {
        friend class LinkedListEntry<UpdateMetadata>;
        friend class Heap::Allocatable<UpdateMetadata>;

    public:
        TimeMilli                GetExpireTime(void) const { return mExpireTime; }
        const Dns::UpdateHeader &GetDnsHeader(void) const { return mDnsHeader; }
        ServiceUpdateId          GetId(void) const { return mId; }
        const TtlConfig         &GetTtlConfig(void) const { return mTtlConfig; }
        const LeaseConfig       &GetLeaseConfig(void) const { return mLeaseConfig; }
        Host                    &GetHost(void) { return mHost; }
        const Ip6::MessageInfo  &GetMessageInfo(void) const { return mMessageInfo; }
        Error                    GetError(void) const { return mError; }
        void                     SetError(Error aError) { mError = aError; }
        bool                     IsDirectRxFromClient(void) const { return mIsDirectRxFromClient; }
        bool                     Matches(ServiceUpdateId aId) const { return mId == aId; }

    private:
        UpdateMetadata(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata);

        UpdateMetadata   *mNext;
        TimeMilli         mExpireTime;
        Dns::UpdateHeader mDnsHeader;
        ServiceUpdateId   mId;          // The ID of this service update transaction.
        TtlConfig         mTtlConfig;   // TTL config to use when processing the message.
        LeaseConfig       mLeaseConfig; // Lease config to use when processing the message.
        Host             &mHost;        // The `UpdateMetadata` has no ownership of this host.
        Ip6::MessageInfo  mMessageInfo; // Valid when `mIsDirectRxFromClient` is true.
        Error             mError;
        bool              mIsDirectRxFromClient;
    };

    void              Enable(void);
    void              Disable(void);
    void              Start(void);
    void              Stop(void);
    void              InitPort(void);
    void              SelectPort(void);
    Error             PrepareSocket(void);
    Ip6::Udp::Socket &GetSocket(void);
    LinkedList<Host> &GetHosts(void) { return mHosts; }

#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
    void  HandleDnssdServerStateChange(void);
    Error HandleDnssdServerUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
#endif

    void HandleNetDataPublisherEvent(NetworkData::Publisher::Event aEvent);

    ServiceUpdateId AllocateServiceUpdateId(void) { return mServiceUpdateId++; }

    void  InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata);
    void  CommitSrpUpdate(Error aError, Host &aHost, const MessageMetadata &aMessageMetadata);
    void  CommitSrpUpdate(UpdateMetadata &aUpdateMetadata);
    void  CommitSrpUpdate(Error                    aError,
                          Host                    &aHost,
                          const Dns::UpdateHeader &aDnsHeader,
                          const Ip6::MessageInfo  *aMessageInfo,
                          const TtlConfig         &aTtlConfig,
                          const LeaseConfig       &aLeaseConfig);
    Error ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
    Error ProcessMessage(Message                &aMessage,
                         TimeMilli               aRxTime,
                         const TtlConfig        &aTtlConfig,
                         const LeaseConfig      &aLeaseConfig,
                         const Ip6::MessageInfo *aMessageInfo);
    void  ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata);
    Error ProcessUpdateSection(Host &aHost, const Message &aMessage, MessageMetadata &aMetadata) const;
    Error ProcessAdditionalSection(Host *aHost, const Message &aMessage, MessageMetadata &aMetadata) const;
    Error VerifySignature(const Host::Key  &aKey,
                          const Message    &aMessage,
                          Dns::UpdateHeader aDnsHeader,
                          uint16_t          aSigOffset,
                          uint16_t          aSigRdataOffset,
                          uint16_t          aSigRdataLength,
                          const char       *aSignerName) const;
    Error ProcessZoneSection(const Message &aMessage, MessageMetadata &aMetadata) const;
    Error ProcessHostDescriptionInstruction(Host                  &aHost,
                                            const Message         &aMessage,
                                            const MessageMetadata &aMetadata) const;
    Error ProcessServiceDiscoveryInstructions(Host                  &aHost,
                                              const Message         &aMessage,
                                              const MessageMetadata &aMetadata) const;
    Error ProcessServiceDescriptionInstructions(Host &aHost, const Message &aMessage, MessageMetadata &aMetadata) const;

    static bool IsValidDeleteAllRecord(const Dns::ResourceRecord &aRecord);

    void        HandleUpdate(Host &aHost, const MessageMetadata &aMetadata);
    void        RemoveHost(Host *aHost, RetainName aRetainName);
    bool        HasNameConflictsWith(Host &aHost) const;
    void        SendResponse(const Dns::UpdateHeader    &aHeader,
                             Dns::UpdateHeader::Response aResponseCode,
                             const Ip6::MessageInfo     &aMessageInfo);
    void        SendResponse(const Dns::UpdateHeader &aHeader,
                             uint32_t                 aLease,
                             uint32_t                 aKeyLease,
                             bool                     mUseShortLeaseOption,
                             const Ip6::MessageInfo  &aMessageInfo);
    void        HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
    void        HandleLeaseTimer(void);
    static void HandleOutstandingUpdatesTimer(Timer &aTimer);
    void        HandleOutstandingUpdatesTimer(void);
    void        ProcessCompletedUpdates(void);

    const UpdateMetadata *FindOutstandingUpdate(const MessageMetadata &aMessageMetadata) const;
    static const char    *AddressModeToString(AddressMode aMode);

    void UpdateResponseCounters(Dns::Header::Response aResponseCode);
    void UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, const Host &aHost);

    using LeaseTimer           = TimerMilliIn<Server, &Server::HandleLeaseTimer>;
    using UpdateTimer          = TimerMilliIn<Server, &Server::HandleOutstandingUpdatesTimer>;
    using CompletedUpdatesTask = TaskletIn<Server, &Server::ProcessCompletedUpdates>;
    using ServerSocket         = Ip6::Udp::SocketIn<Server, &Server::HandleUdpReceive>;

    ServerSocket mSocket;

    Callback<otSrpServerServiceUpdateHandler> mServiceUpdateHandler;

    Heap::String mDomain;

    TtlConfig   mTtlConfig;
    LeaseConfig mLeaseConfig;

    LinkedList<Host> mHosts;
    LeaseTimer       mLeaseTimer;

    UpdateTimer                mOutstandingUpdatesTimer;
    LinkedList<UpdateMetadata> mOutstandingUpdates;
    LinkedList<UpdateMetadata> mCompletedUpdates;
    CompletedUpdatesTask       mCompletedUpdateTask;

    ServiceUpdateId mServiceUpdateId;
    uint16_t        mPort;
    State           mState;
    AddressMode     mAddressMode;
    uint8_t         mAnycastSequenceNumber;
    bool            mHasRegisteredAnyService : 1;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
    bool mAutoEnable : 1;
#endif

    otSrpServerResponseCounters mResponseCounters;
};

} // namespace Srp

DefineCoreType(otSrpServerTtlConfig, Srp::Server::TtlConfig);
DefineCoreType(otSrpServerLeaseConfig, Srp::Server::LeaseConfig);
DefineCoreType(otSrpServerHost, Srp::Server::Host);
DefineCoreType(otSrpServerService, Srp::Server::Service);
DefineMapEnum(otSrpServerState, Srp::Server::State);
DefineMapEnum(otSrpServerAddressMode, Srp::Server::AddressMode);

} // namespace ot

#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
#endif // NET_SRP_SERVER_HPP_
