// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SHARING_MESSAGE_SHARING_CHANNEL_SENDER_H_
#define COMPONENTS_SHARING_MESSAGE_SHARING_CHANNEL_SENDER_H_

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "components/sharing_message/proto/sharing_message.pb.h"
#include "components/sharing_message/sharing_constants.h"
#include "components/sharing_message/sharing_send_message_result.h"
#include "components/sharing_message/sharing_target_device_info.h"
#include "components/sync/model/syncable_service.h"
#include "components/sync/protocol/sharing_message_specifics.pb.h"
#include "components/sync/protocol/unencrypted_sharing_message.pb.h"
#include "components/sync/service/sync_service_observer.h"
#include "components/sync_device_info/device_info.h"

namespace gcm {
class GCMDriver;
enum class GCMEncryptionResult;
}  // namespace gcm

namespace syncer {
class DeviceInfoTracker;
class LocalDeviceInfoProvider;
class SyncService;
}  // namespace syncer

namespace sync_pb {
class SharingMessageCommitError;
}

enum class SharingChannelType;
class SharingMessageBridge;
class SharingSyncPreference;

// Responsible for sending Sharing messages over the available delivery
// channels (FCM and iOS/Chime push).
class SharingChannelSender : public syncer::SyncServiceObserver {
 public:
  using SharingMessage = components_sharing_message::SharingMessage;
  using SendMessageCallback =
      base::OnceCallback<void(SharingSendMessageResult result,
                              std::optional<std::string> message_id,
                              SharingChannelType channel_type)>;

  SharingChannelSender(
      SharingMessageBridge* sharing_message_bridge,
      SharingSyncPreference* sync_preference,
      gcm::GCMDriver* gcm_driver,
      const syncer::DeviceInfoTracker* device_info_tracker,
      const syncer::LocalDeviceInfoProvider* local_device_info_provider,
      syncer::SyncService* sync_service,
      syncer::SyncableService::StartSyncFlare start_sync_flare);
  SharingChannelSender(const SharingChannelSender&) = delete;
  SharingChannelSender& operator=(const SharingChannelSender&) = delete;
  ~SharingChannelSender() override;

  // Sends an encrypted |message| to |device|. |callback| will be invoked with
  // the send result.
  virtual void SendFcmMessageToDevice(const SharingTargetDeviceInfo& device,
                                      base::TimeDelta time_to_live,
                                      SharingMessage message,
                                      SendMessageCallback callback);

  // Sends an unencrypted |message| to |device| via iOS/Chime push. |callback|
  // will be invoked with the send result.
  virtual void SendIosPushMessageToDevice(
      const SharingTargetDeviceInfo& device,
      sync_pb::UnencryptedSharingMessage message,
      SendMessageCallback callback);

  // Sends a |message| to device identified by |fcm_configuration|, which
  // expires after |time_to_live| seconds. |callback| will be invoked with
  // message_id if asynchronous operation succeeded, or std::nullopt if
  // operation failed.
  virtual void SendMessageToFcmTarget(
      const components_sharing_message::FCMChannelConfiguration&
          fcm_configuration,
      base::TimeDelta time_to_live,
      SharingMessage message,
      SendMessageCallback callback);

  // Sends a |message| to device identified by |server_channel|, |callback| will
  // be invoked with message_id if asynchronous operation succeeded, or
  // std::nullopt if operation failed.
  virtual void SendMessageToServerTarget(
      const components_sharing_message::ServerChannelConfiguration&
          server_channel,
      SharingMessage message,
      SendMessageCallback callback);

  // Removes any pending messages that are waiting for the sync service to
  // initialize. This must be called if the device is unregistered.
  void ClearPendingMessages();

  // syncer::SyncServiceObserver overrides.
  void OnStateChanged(syncer::SyncService* sync_service) override;
  void OnSyncShutdown(syncer::SyncService* sync_service) override;

  // Used to inject a fake SharingMessageBridge in integration tests.
  void SetSharingMessageBridgeForTesting(
      SharingMessageBridge* sharing_message_bridge);

 private:
  using MessageSender = base::OnceCallback<void(std::string message,
                                                SendMessageCallback callback)>;

  void EncryptMessage(const std::string& authorized_entity,
                      const std::string& p256dh,
                      const std::string& auth_secret,
                      const SharingMessage& message,
                      SharingChannelType channel_type,
                      SendMessageCallback callback,
                      MessageSender message_sender);

  void OnMessageEncrypted(SharingChannelType channel_type,
                          SendMessageCallback callback,
                          MessageSender message_sender,
                          gcm::GCMEncryptionResult result,
                          std::string message);

  void SendMessageViaSync(sync_pb::SharingMessageSpecifics::ChannelConfiguration
                              channel_configuration,
                          SharingChannelType channel_type,
                          std::string message_id,
                          std::string payload,
                          SendMessageCallback callback);

  void OnMessageSentViaSync(SendMessageCallback callback,
                            const std::string& message_id,
                            SharingChannelType channel_type,
                            const sync_pb::SharingMessageCommitError& error);

  bool SetMessageSenderInfo(SharingMessage* message);

  // Returns whether an unencrypted SendTab push message can be delivered to
  // |target_device_info| via iOS push.
  bool CanSendSendTabPushMessage(const syncer::DeviceInfo& target_device_info);

  raw_ptr<SharingMessageBridge> sharing_message_bridge_;
  const raw_ptr<SharingSyncPreference> sync_preference_;
  const raw_ptr<gcm::GCMDriver, AcrossTasksDanglingUntriaged> gcm_driver_;
  const raw_ptr<const syncer::DeviceInfoTracker> device_info_tracker_;
  const raw_ptr<const syncer::LocalDeviceInfoProvider>
      local_device_info_provider_;
  const raw_ptr<syncer::SyncService> sync_service_;
  syncer::SyncableService::StartSyncFlare start_sync_flare_;
  base::ScopedObservation<syncer::SyncService, SharingChannelSender>
      sync_service_observation_{this};

  // Pending messages that are waiting for the sync service to initialize.
  struct PendingMessage {
    PendingMessage(sync_pb::SharingMessageSpecifics::ChannelConfiguration
                       channel_configuration,
                   SharingChannelType channel_type,
                   std::string message_id,
                   std::string payload,
                   SendMessageCallback callback);
    PendingMessage(const PendingMessage& other) = delete;
    PendingMessage& operator=(const PendingMessage& other) = delete;
    PendingMessage(PendingMessage&& other);
    PendingMessage& operator=(PendingMessage&& other);
    ~PendingMessage();

    sync_pb::SharingMessageSpecifics::ChannelConfiguration
        channel_configuration;
    SharingChannelType channel_type;
    std::string message_id;
    std::string payload;
    SendMessageCallback callback;
  };
  std::vector<PendingMessage> pending_messages_;

  base::WeakPtrFactory<SharingChannelSender> weak_ptr_factory_{this};
};

#endif  // COMPONENTS_SHARING_MESSAGE_SHARING_CHANNEL_SENDER_H_
