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

#ifndef GOOGLE_APIS_GCM_ENGINE_CONNECTION_FACTORY_IMPL_H_
#define GOOGLE_APIS_GCM_ENGINE_CONNECTION_FACTORY_IMPL_H_

#include "base/memory/raw_ptr.h"
#include "google_apis/gcm/engine/connection_factory.h"

#include <stddef.h>

#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "google_apis/gcm/engine/connection_event_tracker.h"
#include "google_apis/gcm/engine/connection_handler.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/backoff_entry.h"
#include "net/log/net_log_with_source.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "services/network/public/mojom/proxy_resolving_socket.mojom.h"
#include "url/gurl.h"

namespace gcm {

class GCMStatsRecorder;

class GCM_EXPORT ConnectionFactoryImpl
    : public ConnectionFactory,
      public network::NetworkConnectionTracker::NetworkConnectionObserver {
 public:
  ConnectionFactoryImpl(
      const std::vector<GURL>& mcs_endpoints,
      const net::BackoffEntry::Policy& backoff_policy,
      GetProxyResolvingFactoryCallback get_socket_factory_callback,
      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
      GCMStatsRecorder* recorder,
      network::NetworkConnectionTracker* network_connection_tracker);

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

  ~ConnectionFactoryImpl() override;

  // ConnectionFactory implementation.
  void Initialize(
      const BuildLoginRequestCallback& request_builder,
      const ConnectionHandler::ProtoReceivedCallback& read_callback,
      const ConnectionHandler::ProtoSentCallback& write_callback) override;
  ConnectionHandler* GetConnectionHandler() const override;
  void Connect() override;
  bool IsEndpointReachable() const override;
  std::string GetConnectionStateString() const override;
  base::TimeTicks NextRetryAttempt() const override;
  void SignalConnectionReset(ConnectionResetReason reason) override;
  void SetConnectionListener(ConnectionListener* listener) override;

  // NetworkConnectionTracker implementation.
  void OnConnectionChanged(
      net::NetworkChangeNotifier::ConnectionType type) override;

  // Returns the server to which the factory is currently connected, or if
  // a connection is currently pending, the server to which the next connection
  // attempt will be made.
  GURL GetCurrentEndpoint() const;

 protected:
  // Initiate the connection to the GCM server. When `ignore_connection_failure`
  // is true, backoff delay won't be changed in case of failure.
  // Virtual for testing.
  virtual void StartConnection(bool ignore_connection_failure);

  // Helper method for initalizing the connection hander.
  // Virtual for testing.
  virtual void InitHandler(mojo::ScopedDataPipeConsumerHandle receive_stream,
                           mojo::ScopedDataPipeProducerHandle send_stream);

  // Helper method for creating a backoff entry.
  // Virtual for testing.
  virtual std::unique_ptr<net::BackoffEntry> CreateBackoffEntry(
      const net::BackoffEntry::Policy* const policy);

  // Helper method for creating the connection handler.
  // Virtual for testing.
  virtual std::unique_ptr<ConnectionHandler> CreateConnectionHandler(
      base::TimeDelta read_timeout,
      const ConnectionHandler::ProtoReceivedCallback& read_callback,
      const ConnectionHandler::ProtoSentCallback& write_callback,
      const ConnectionHandler::ConnectionChangedCallback& connection_callback);

  // Returns the current time in Ticks.
  // Virtual for testing.
  virtual base::TimeTicks NowTicks();

  // Callback for Socket connection completion. When `ignore_connection_failure`
  // is true, backoff delay won't be changed in case of failure.
  // This is public for testing.
  void OnConnectDone(bool ignore_connection_failure,
                     int result,
                     const std::optional<net::IPEndPoint>& local_addr,
                     const std::optional<net::IPEndPoint>& peer_addr,
                     mojo::ScopedDataPipeConsumerHandle receive_stream,
                     mojo::ScopedDataPipeProducerHandle send_stream);

  // ConnectionHandler callback for connection issues.
  void ConnectionHandlerCallback(int result);

 private:
  friend class ConnectionFactoryImplTest;

  ConnectionEventTracker* GetEventTrackerForTesting();

  // Helper method for checking backoff and triggering a connection as
  // necessary.
  void ConnectWithBackoff();

  // Implementation of Connect(..). If not in backoff attempts a connection and
  // handshake. On connection/handshake failure, goes into backoff. When
  // `ignore_connection_failure` is true, backoff delay won't be changed in case
  // of failure.
  void ConnectImpl(bool ignore_connection_failure);

  // Closes the local socket if one is present, and resets connection handler.
  void CloseSocket();

  // Updates the GCM Network Session's with current data from HTTP Network
  // Session's, if available.
  // Specifically, HttpAuthCache and IsQuicEnabled are updated.
  void UpdateFromHttpNetworkSession();

  // The tracker will maintain a list of all connection attempts with GCM,
  // whether they succeeded, and their duration.
  ConnectionEventTracker event_tracker_;

  // The MCS endpoints to make connections to, sorted in order of priority.
  const std::vector<GURL> mcs_endpoints_;
  // Index to the endpoint for which a connection should be attempted next.
  size_t next_endpoint_;
  // Index to the endpoint that was last successfully connected.
  size_t last_successful_endpoint_;

  // The backoff policy to use.
  const net::BackoffEntry::Policy backoff_policy_;

  // ---- network:: components for establishing connections. ----
  // Socket factory for creating new GCM connections.
  GetProxyResolvingFactoryCallback get_socket_factory_callback_;
  mojo::Remote<network::mojom::ProxyResolvingSocketFactory> socket_factory_;
  // The handle to the socket for the current connection, if one exists.
  mojo::Remote<network::mojom::ProxyResolvingSocket> socket_;
  // Peer address of |socket_|.
  net::IPEndPoint peer_addr_;

  // Current backoff entry.
  std::unique_ptr<net::BackoffEntry> backoff_entry_;
  // Backoff entry from previous connection attempt. Updated on each login
  // completion.
  std::unique_ptr<net::BackoffEntry> previous_backoff_;

  // Whether a connection attempt is currently actively in progress.
  bool connecting_;

  // Whether the client is waiting for backoff to finish before attempting to
  // connect. Canary jobs are able to preempt connections pending backoff
  // expiration.
  bool waiting_for_backoff_;

  // Whether the NetworkConnectionTracker has informed the client that there is
  // no current connection. No connection attempts will be made until the
  // client is informed of a valid connection type.
  bool waiting_for_network_online_;

  // Whether handshake is in progress after the connection was established. If
  // a connection reset happens while attempting to complete the handshake, the
  // current backoff entry is reused (after incrementing with a new failure).
  bool handshake_in_progress_;

  // The time of the last login completion. Used for calculating whether to
  // restore a previous backoff entry and for measuring uptime.
  base::TimeTicks last_login_time_;

  // Cached callbacks. Set at |Initialize| time, consumed at first |Connect|
  // time.
  ConnectionHandler::ProtoReceivedCallback read_callback_;
  ConnectionHandler::ProtoSentCallback write_callback_;

  // The current connection handler, if one exists.
  std::unique_ptr<ConnectionHandler> connection_handler_;

  // Builder for generating new login requests.
  BuildLoginRequestCallback request_builder_;

  // Task runner.
  const scoped_refptr<base::SequencedTaskRunner> io_task_runner_;

  // Recorder that records GCM activities for debugging purpose. Not owned.
  raw_ptr<GCMStatsRecorder> recorder_;

  // Notifies this class of network connection changes.
  // Must outlive the ConnectionFactoryImpl.
  raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;

  // The currently registered listener to notify of connection changes.
  raw_ptr<ConnectionListener> listener_;

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

}  // namespace gcm

#endif  // GOOGLE_APIS_GCM_ENGINE_CONNECTION_FACTORY_IMPL_H_
