// 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 REMOTING_HOST_PEER_SESSION_IMPL_H_
#define REMOTING_HOST_PEER_SESSION_IMPL_H_

#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "remoting/base/constants.h"
#include "remoting/base/errors.h"
#include "remoting/base/session_policies.h"
#include "remoting/host/audio_injector.h"
#include "remoting/host/base/desktop_environment_options.h"
#include "remoting/host/client_session_control.h"
#include "remoting/host/client_session_events.h"
#include "remoting/host/cursor_visibility_notifier.h"
#include "remoting/host/desktop_display_info.h"
#include "remoting/host/input_pipeline.h"
#include "remoting/host/mojom/chromoting_host_services.mojom.h"
#include "remoting/host/mojom/remote_url_opener.mojom.h"
#include "remoting/host/mojom/webauthn_proxy.mojom.h"
#include "remoting/host/peer_session.h"
#include "remoting/proto/action.pb.h"
#include "remoting/proto/control.pb.h"
#include "remoting/protocol/audio_sample_info.h"
#include "remoting/protocol/clipboard_echo_filter.h"
#include "remoting/protocol/clipboard_filter.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/connection_to_client.h"
#include "remoting/protocol/coordinate_converter.h"
#include "remoting/protocol/data_channel_manager.h"
#include "remoting/protocol/errors.h"
#include "remoting/protocol/host_stub.h"
#include "remoting/protocol/input_event_timestamps.h"
#include "remoting/protocol/mouse_cursor_monitor.h"
#include "remoting/protocol/transport.h"
#include "remoting/protocol/video_stream.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
#include "ui/events/types/event_type.h"

namespace remoting {

class ActiveDisplayMonitor;
class DesktopEnvironment;
class DesktopEnvironmentFactory;
class InputInjector;
class KeyboardLayoutMonitor;
class MouseShapePump;
class RemoteOpenUrlMessageHandler;
class RemoteWebAuthnMessageHandler;
class ScreenControls;
class SecurityKeyAuthHandler;
class TerminalSessionManager;

namespace protocol {
class AudioStream;
class IceConfigFetcher;
class VideoLayout;
}  // namespace protocol

// A PeerSessionImpl keeps a reference to a connection to a client, and
// maintains per-client state.
class PeerSessionImpl : public PeerSession,
                        public protocol::HostStub,
                        public protocol::ConnectionToClient::EventHandler,
                        public ClientSessionControl,
                        public ClientSessionEvents,
                        public CursorVisibilityNotifier::EventHandler,
                        public AudioInjector::Delegate,
                        public protocol::MouseCursorMonitor::Callback,
                        public mojom::ChromotingSessionServices {
 public:
  // Maximum allowed length in bytes for a client pairing name.
  static constexpr size_t kMaxClientNameLength = 1024;

  using RequestPairingResponseCallback =
      PeerSessionFactory::RequestPairingResponseCallback;
  using RequestPairingCallback = PeerSessionFactory::RequestPairingCallback;
  using RequestPairingOnceCallback =
      PeerSessionFactory::RequestPairingOnceCallback;

  // `desktop_environment_factory` must outlive `this`.
  PeerSessionImpl(std::unique_ptr<protocol::ConnectionToClient> connection,
                  DesktopEnvironmentFactory* desktop_environment_factory,
                  RequestPairingOnceCallback request_pairing_cb);

  PeerSessionImpl(const PeerSessionImpl&) = delete;
  PeerSessionImpl& operator=(const PeerSessionImpl&) = delete;

  ~PeerSessionImpl() override;

  // PeerSession interface.
  void Start(PeerSession::EventHandler* event_handler,
             std::string_view client_jid,
             const DesktopEnvironmentOptions& desktop_environment_options,
             const SessionPolicies& session_policies,
             const SessionOptions& session_options) override;

  TerminalSessionManager* terminal_session_manager_for_tests() const {
    return terminal_session_manager_.get();
  }

  // Returns the set of capabilities negotiated between client and host.
  const std::string& capabilities() const { return capabilities_; }

  // protocol::HostStub interface.
  void NotifyClientResolution(
      const protocol::ClientResolution& resolution) override;
  void ControlVideo(const protocol::VideoControl& video_control) override;
  void ControlAudio(const protocol::AudioControl& audio_control) override;
  void SetCapabilities(const protocol::Capabilities& capabilities) override;
  void RequestPairing(
      const remoting::protocol::PairingRequest& pairing_request) override;
  void DeliverClientMessage(const protocol::ExtensionMessage& message) override;
  void SelectDesktopDisplay(
      const protocol::SelectDesktopDisplayRequest& select_display) override;
  void ControlPeerConnection(
      const protocol::PeerConnectionParameters& parameters) override;
  void SetVideoLayout(const protocol::VideoLayout& video_layout) override;
  void ControlTerminal(
      const protocol::TerminalControl& terminal_control) override;

  // protocol::ConnectionToClient::EventHandler interface.
  void CreateMediaStreams() override;
  void OnConnectionChannelsConnected() override;
  void OnTransportProtocolChange(const std::string& protocol) override;
  void OnRouteChange(const std::string& channel_name,
                     const protocol::TransportRoute& route) override;
  void OnIncomingDataChannel(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe) override;
  void OnIncomingAudioFormatChanged(
      const protocol::AudioSampleInfo& info,
      base::OnceCallback<void(bool)> done) override;
  void OnConnectionClosed(protocol::ErrorCode error,
                          std::string_view error_details,
                          const SourceLocation& error_location) override;

  // ClientSessionControl interface.
  const std::string& client_jid() const override;
  void DisconnectSession(ErrorCode error,
                         std::string_view error_details,
                         const SourceLocation& error_location) override;
  void OnLocalKeyPressed(std::uint32_t usb_keycode) override;
  void OnLocalPointerMoved(const webrtc::DesktopVector& position,
                           ui::EventType type) override;
  void SetDisableInputs(bool disable_inputs) override;
  void OnDesktopDisplayChanged(
      std::unique_ptr<protocol::VideoLayout> layout) override;
  void OnMicrophoneControl(const protocol::MicrophoneControl& control) override;

  // ClientSessionEvents interface.
  void OnDesktopAttached() override;
  void OnDesktopDetached() override;
  void OnSecurityKeyConnection(
      mojo::PendingReceiver<mojom::SecurityKeyForwarder> receiver) override;
  void OnSessionServicesClientConnected(
      mojo::PendingReceiver<mojom::ChromotingSessionServices> receiver)
      override;

  // CursorVisibilityNotifier::EventHandler interface
  void OnCursorVisibilityChanged(bool visible) override;

  // MouseCursorMonitor::Callback implementation.
  void OnMouseCursor(
      std::unique_ptr<webrtc::MouseCursor> mouse_cursor) override;
  void OnMouseCursorPosition(const webrtc::DesktopVector& position) override;

  // mojom::ChromotingSessionServices implementation.
  void BindWebAuthnProxy(
      mojo::PendingReceiver<mojom::WebAuthnProxy> receiver) override;
  void BindRemoteUrlOpener(
      mojo::PendingReceiver<mojom::RemoteUrlOpener> receiver) override;
#if BUILDFLAG(IS_WIN)
  void BindSecurityKeyForwarder(
      mojo::PendingReceiver<mojom::SecurityKeyForwarder> receiver) override;
#endif

  protocol::Transport* transport() override;

  bool channels_connected() const { return channels_connected_; }

  const std::string* client_capabilities() const {
    return client_capabilities_.get();
  }

  // Registers a DataChannelManager callback for testing.
  void RegisterCreateHandlerCallbackForTesting(
      const std::string& prefix,
      protocol::DataChannelManager::CreateHandlerCallback constructor);

  void SetEventTimestampsSourceForTests(
      scoped_refptr<protocol::InputEventTimestampsSource>
          event_timestamp_source);

  const SessionPolicies& effective_policies_for_tests() const {
    return effective_policies_;
  }

  void SetRequestPairingCallbackForTesting(RequestPairingOnceCallback cb) {
    request_pairing_cb_ = std::move(cb);
  }

 private:
  friend class ClientSessionTest;
  friend class ChromotingHostTest;

  void OnDesktopEnvironmentCreated(
      std::unique_ptr<DesktopEnvironment> desktop_environment);

  void CreateAudioInjectorAndBuffer();

  // Creates a proxy for sending clipboard events to the client.
  std::unique_ptr<protocol::ClipboardStub> CreateClipboardProxy();

  // AudioInjector::Delegate interface.
  void OnAudioInjectorConsumersChanged(bool has_consumers) override;

  void CreateActionMessageHandler(
      std::vector<protocol::ActionRequest::Action> capabilities,
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateFileTransferMessageHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateRtcLogTransferMessageHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateRemoteOpenUrlMessageHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateUrlForwarderControlMessageHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateRemoteWebAuthnMessageHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreateSecurityKeyDataChannelHandler(
      const std::string& channel_name,
      std::unique_ptr<protocol::MessagePipe> pipe);

  void CreatePerMonitorVideoStreams();

  // Boosts the framerate using `capture_interval` for `boost_duration` based on
  // the type of input `event` received.
  void BoostFramerateOnInput(base::TimeDelta capture_interval,
                             base::TimeDelta boost_duration,
                             bool& mouse_button_down,
                             protocol::ObservingInputFilter::Event event);

  // Sends the new active display to the client. Called by ActiveDisplayMonitor
  // whenever the screen id associated with the active window changes.
  void OnActiveDisplayChanged(webrtc::ScreenId display);

  // Calls SetComposeEnabled() on all video streams. This controls whether the
  // host's cursor should be composed onto the desktop frame.
  // TODO: crbug.com/455622961 - Remove this method once the
  // clientRenderedHostCursor capability is fully rolled out.
  void SetComposeEnabledOnVideoStreams(bool enabled);

  void SendTerminalOutput(int32_t terminal_id, const std::string& data);

  void OnTerminalExited(int32_t terminal_id);

  void SendTerminalProcessInfo(int32_t terminal_id,
                               bool is_active,
                               std::string_view process_name);

  void OnPairingResponse(
      std::optional<protocol::PairingResponse> pairing_response);

  raw_ptr<PeerSession::EventHandler> event_handler_;

  // Used to create a DesktopEnvironment instance for this session.
  raw_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;

  // The DesktopEnvironmentOptions used to initialize DesktopEnvironment.
  DesktopEnvironmentOptions desktop_environment_options_;

  // The DesktopEnvironment instance for this session.
  std::unique_ptr<DesktopEnvironment> desktop_environment_;

  // Pending actions to run once the desktop environment has been created.
  std::vector<base::OnceClosure> desktop_environment_ready_callbacks_;

  // Used to convert fractional coordinates to absolute coordinates.
  protocol::CoordinateConverter coordinate_converter_;

  // Filter to used to stop clipboard items sent from the client being echoed
  // back to it.  It is the final element in the clipboard (client -> host)
  // pipeline.
  protocol::ClipboardEchoFilter clipboard_echo_filter_;

  // Injects microphone input received from the client.
  std::unique_ptr<AudioInjector> audio_injector_;

  // Used to enable/disable clipboard sync and to restrict payload size.
  protocol::ClipboardFilter host_clipboard_filter_;
  protocol::ClipboardFilter client_clipboard_filter_;
  // Factory for weak pointers to the client clipboard stub.
  // This must appear after `clipboard_echo_filter_`, so that it won't outlive
  // it.
  base::WeakPtrFactory<protocol::ClipboardStub> client_clipboard_factory_;

  // Objects responsible for sending video, audio.
  std::map<webrtc::ScreenId, std::unique_ptr<protocol::VideoStream>>
      video_streams_;
  std::unique_ptr<protocol::AudioStream> audio_stream_;
  std::unique_ptr<FifoBufferWriter> pending_audio_writer_
      GUARDED_BY_CONTEXT(sequence_checker_);
  std::optional<protocol::AudioSampleInfo> pending_audio_sample_info_
      GUARDED_BY_CONTEXT(sequence_checker_);
  base::OnceCallback<void(bool)> pending_audio_format_ack_callback_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // The set of all capabilities supported by the client.
  std::unique_ptr<std::string> client_capabilities_;

  // The set of all capabilities supported by the host.
  std::string host_capabilities_;

  // The set of all capabilities negotiated between client and host.
  std::string capabilities_;

  // Used to inject mouse and keyboard input and handle clipboard events.
  std::unique_ptr<InputInjector> input_injector_;

  // Input pipeline encapsulating the event filters.
  // Declared after `input_injector_` because it holds a reference to it (via
  // target), ensuring the pipeline is destroyed before the injector.
  InputPipeline input_pipeline_;

  // Used to apply client-requested changes in screen resolution.
  std::unique_ptr<ScreenControls> screen_controls_;

  // Contains the most recently gathered info about the desktop displays;
  DesktopDisplayInfo desktop_display_info_;

  // Default DPI values to use if a display reports 0 for DPI.
  int default_x_dpi_ = kDefaultDpi;
  int default_y_dpi_ = kDefaultDpi;

  // Callback for PIN-less authentication pairing request.
  RequestPairingOnceCallback request_pairing_cb_;

  // Used to dispatch new data channels to factory methods.
  protocol::DataChannelManager data_channel_manager_;

  // Set to true after all data channels have been connected.
  bool channels_connected_ = false;

  // Used to store the video channel pause parameter.
  bool pause_video_ = false;

  // Used to store the target framerate control parameter.
  int target_framerate_ = kTargetFrameRate;

  // VideoLayout is sent only after the control channel is connected. Until
  // then it's stored in `pending_video_layout_message_`.
  std::unique_ptr<protocol::VideoLayout> pending_video_layout_message_;

  scoped_refptr<protocol::InputEventTimestampsSource>
      event_timestamp_source_for_tests_;

  // The connection to the client.
  std::unique_ptr<protocol::ConnectionToClient> connection_;

  // True if PeerSessionImpl teardown has already begun. Prevents re-entrant
  // execution of OnConnectionClosed() when callbacks fire during teardown.
  bool is_closing_ = false;

  std::string client_jid_;

  // Objects to monitor and send updates for mouse shape and keyboard layout.
  std::unique_ptr<MouseShapePump> mouse_shape_pump_;
  std::unique_ptr<KeyboardLayoutMonitor> keyboard_layout_monitor_;

  std::unique_ptr<SecurityKeyAuthHandler> security_key_auth_handler_;

  base::WeakPtr<RemoteWebAuthnMessageHandler> remote_webauthn_message_handler_;
  base::WeakPtr<RemoteOpenUrlMessageHandler> remote_open_url_message_handler_;

  mojo::ReceiverSet<mojom::ChromotingSessionServices>
      session_services_receivers_;

  std::unique_ptr<ActiveDisplayMonitor> active_display_monitor_;

  SessionPolicies effective_policies_;

  bool host_cursor_rendered_by_client_ = false;
  bool cursor_visible_ = false;

  std::unique_ptr<TerminalSessionManager> terminal_session_manager_;

  bool pairing_request_pending_ = false;
  std::optional<protocol::PairingResponse> pending_pairing_response_;

  SEQUENCE_CHECKER(sequence_checker_);

  // Used to disable callbacks to `this` once DisconnectSession() has been
  // called.
  base::WeakPtrFactory<PeerSessionImpl> weak_factory_{this};
};

// Factory for creating `PeerSessionImpl` instances.
class PeerSessionImplFactory : public PeerSessionFactory {
 public:
  using GetIceConfigFetcherCallback =
      base::RepeatingCallback<std::unique_ptr<protocol::IceConfigFetcher>()>;
  using RequestPairingCallback = PeerSessionImpl::RequestPairingCallback;

  PeerSessionImplFactory(
      DesktopEnvironmentFactory* desktop_environment_factory,
      GetIceConfigFetcherCallback get_ice_config_fetcher_cb,
      RequestPairingCallback request_pairing_cb = base::NullCallback());
  PeerSessionImplFactory(const PeerSessionImplFactory&) = delete;
  PeerSessionImplFactory& operator=(const PeerSessionImplFactory&) = delete;
  ~PeerSessionImplFactory() override;

  std::unique_ptr<PeerSession> Create() override;

  void set_request_pairing_callback(
      const RequestPairingCallback& request_pairing_cb) override;

 private:
  SEQUENCE_CHECKER(sequence_checker_);
  raw_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
  GetIceConfigFetcherCallback get_ice_config_fetcher_cb_;
  RequestPairingCallback request_pairing_cb_;
};

}  // namespace remoting

#endif  // REMOTING_HOST_PEER_SESSION_IMPL_H_
