// Copyright 2015 The Weave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef LIBWEAVE_SRC_PRIVET_PRIVET_HANDLER_H_
#define LIBWEAVE_SRC_PRIVET_PRIVET_HANDLER_H_

#include <map>
#include <string>
#include <utility>

#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#include <base/scoped_observer.h>
#include <base/time/default_clock.h>

#include "src/privet/cloud_delegate.h"

namespace base {
class Value;
class DictionaryValue;
}  // namespace base

namespace weave {
namespace privet {

class DeviceDelegate;
class IdentityDelegate;
class SecurityDelegate;
class WifiDelegate;

// Privet V3 HTTP/HTTPS requests handler.
// API details at https://developers.google.com/cloud-devices/
class PrivetHandler : public CloudDelegate::Observer {
 public:
  // Callback to handle requests asynchronously.
  // |status| is HTTP status code.
  // |output| is result returned in HTTP response. Contains result of
  // successfully request of information about error.
  using RequestCallback =
      base::Callback<void(int status, const base::DictionaryValue& output)>;

  PrivetHandler(CloudDelegate* cloud,
                DeviceDelegate* device,
                SecurityDelegate* pairing,
                WifiDelegate* wifi,
                base::Clock* clock = nullptr);
  ~PrivetHandler() override;

  void OnTraitDefsChanged() override;
  void OnStateChanged() override;
  void OnComponentTreeChanged() override;

  std::vector<std::string> GetHttpPaths() const;
  std::vector<std::string> GetHttpsPaths() const;

  // Handles HTTP/HTTPS Privet request.
  // |api| is the path from the HTTP request, e.g /privet/info.
  // |auth_header| is the Authentication header from HTTP request.
  // |input| is the POST data from HTTP request. If nullptr, data format is
  // not valid JSON.
  // |callback| will be called exactly once during or after |HandleRequest|
  // call.
  void HandleRequest(const std::string& api,
                     const std::string& auth_header,
                     const base::DictionaryValue* input,
                     const RequestCallback& callback);

 private:
  using ApiHandler = void (PrivetHandler::*)(const base::DictionaryValue&,
                                             const UserInfo&,
                                             const RequestCallback&);

  // Adds a handler for both HTTP and HTTPS interfaces.
  void AddHandler(const std::string& path, ApiHandler handler, AuthScope scope);

  // Adds a handler for both HTTPS interface only.
  void AddSecureHandler(const std::string& path,
                        ApiHandler handler,
                        AuthScope scope);

  void HandleInfo(const base::DictionaryValue&,
                  const UserInfo& user_info,
                  const RequestCallback& callback);
  void HandlePairingStart(const base::DictionaryValue& input,
                          const UserInfo& user_info,
                          const RequestCallback& callback);
  void HandlePairingConfirm(const base::DictionaryValue& input,
                            const UserInfo& user_info,
                            const RequestCallback& callback);
  void HandlePairingCancel(const base::DictionaryValue& input,
                           const UserInfo& user_info,
                           const RequestCallback& callback);
  void HandleAuth(const base::DictionaryValue& input,
                  const UserInfo& user_info,
                  const RequestCallback& callback);
  void HandleAccessControlClaim(const base::DictionaryValue& input,
                                const UserInfo& user_info,
                                const RequestCallback& callback);
  void HandleAccessControlConfirm(const base::DictionaryValue& input,
                                  const UserInfo& user_info,
                                  const RequestCallback& callback);
  void HandleSetupStart(const base::DictionaryValue& input,
                        const UserInfo& user_info,
                        const RequestCallback& callback);
  void HandleSetupStatus(const base::DictionaryValue&,
                         const UserInfo& user_info,
                         const RequestCallback& callback);
  void HandleState(const base::DictionaryValue& input,
                   const UserInfo& user_info,
                   const RequestCallback& callback);
  void HandleCommandDefs(const base::DictionaryValue& input,
                         const UserInfo& user_info,
                         const RequestCallback& callback);
  void HandleCommandsExecute(const base::DictionaryValue& input,
                             const UserInfo& user_info,
                             const RequestCallback& callback);
  void HandleCommandsStatus(const base::DictionaryValue& input,
                            const UserInfo& user_info,
                            const RequestCallback& callback);
  void HandleCommandsList(const base::DictionaryValue& input,
                          const UserInfo& user_info,
                          const RequestCallback& callback);
  void HandleCommandsCancel(const base::DictionaryValue& input,
                            const UserInfo& user_info,
                            const RequestCallback& callback);
  void HandleCheckForUpdates(const base::DictionaryValue& input,
                             const UserInfo& user_info,
                             const RequestCallback& callback);
  void HandleTraits(const base::DictionaryValue& input,
                    const UserInfo& user_info,
                    const RequestCallback& callback);
  void HandleComponents(const base::DictionaryValue& input,
                        const UserInfo& user_info,
                        const RequestCallback& callback);

  void ReplyWithSetupStatus(const RequestCallback& callback) const;
  void ReplyToUpdateRequest(const RequestCallback& callback) const;
  void OnUpdateRequestTimeout(int update_request_id);

  CloudDelegate* cloud_{nullptr};
  DeviceDelegate* device_{nullptr};
  SecurityDelegate* security_{nullptr};
  WifiDelegate* wifi_{nullptr};
  base::DefaultClock default_clock_;
  base::Clock* clock_{nullptr};

  struct HandlerParameters {
    ApiHandler handler;
    AuthScope scope;
    bool https_only = true;
  };
  std::map<std::string, HandlerParameters> handlers_;

  struct UpdateRequestParameters {
    RequestCallback callback;
    int request_id{0};
    uint64_t state_fingerprint{0};
    uint64_t traits_fingerprint{0};
    uint64_t components_fingerprint{0};
  };
  std::vector<UpdateRequestParameters> update_requests_;
  int last_update_request_id_{0};

  uint64_t state_fingerprint_{1};
  uint64_t traits_fingerprint_{1};
  uint64_t components_fingerprint_{1};
  ScopedObserver<CloudDelegate, CloudDelegate::Observer> cloud_observer_{this};

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

  DISALLOW_COPY_AND_ASSIGN(PrivetHandler);
};

}  // namespace privet
}  // namespace weave

#endif  // LIBWEAVE_SRC_PRIVET_PRIVET_HANDLER_H_