// Copyright 2016 The Chromium 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 COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_
#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_

#include <stdint.h>

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

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_decider.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
#include "net/nqe/effective_connection_type.h"

class GURL;

namespace base {
class Clock;
}

namespace blacklist {
class OptOutStore;
}

namespace net {
class URLRequest;
}

namespace previews {
class PreviewsUIService;

typedef base::RepeatingCallback<bool(PreviewsType)> PreviewsIsEnabledCallback;

// A class to manage the IO portion of inter-thread communication between
// previews/ objects. Created on the UI thread, but used only on the IO thread
// after initialization.
class PreviewsDeciderImpl : public PreviewsDecider,
                            public blacklist::OptOutBlacklistDelegate {
 public:
  PreviewsDeciderImpl(
      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
      base::Clock* clock);
  ~PreviewsDeciderImpl() override;

  // blacklist::OptOutBlacklistDelegate:
  void OnNewBlacklistedHost(const std::string& host, base::Time time) override;
  void OnUserBlacklistedStatusChange(bool blacklisted) override;
  void OnBlacklistCleared(base::Time time) override;

  // Stores |previews_ui_service| as |previews_ui_service_| and posts a task to
  // InitializeOnIOThread on the IO thread.
  virtual void Initialize(
      base::WeakPtr<PreviewsUIService> previews_ui_service,
      std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
      std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
      const PreviewsIsEnabledCallback& is_enabled_callback,
      blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews);

  // Adds log message of the navigation asynchronously.
  void LogPreviewNavigation(const GURL& url,
                            bool opt_out,
                            PreviewsType type,
                            base::Time time,
                            uint64_t page_id) const;

  // Adds a log message for the preview decision made (|reason|) asynchronously.
  // |passed_reasons| is a collection of reason codes that correspond to
  // eligibility checks that were satisfied prior to determining |reason| and
  // so the opposite of these |passed_reasons| codes was true. The method
  // takes ownership of |passed_reasons|. |page_id| is generated by
  // PreviewsDeciderImpl, and used to group decisions into groups on the page,
  // messages that don't need to be grouped can pass in 0 as page_id.
  void LogPreviewDecisionMade(
      PreviewsEligibilityReason reason,
      const GURL& url,
      base::Time time,
      PreviewsType type,
      std::vector<PreviewsEligibilityReason>&& passed_reasons,
      uint64_t page_id) const;

  // Adds a navigation to |url| to the black list with result |opt_out|.
  void AddPreviewNavigation(const GURL& url,
                            bool opt_out,
                            PreviewsType type,
                            uint64_t page_id);

  // Clears the history of the black list between |begin_time| and |end_time|,
  // both inclusive.
  void ClearBlackList(base::Time begin_time, base::Time end_time);

  // Change the status of whether to ignore the decisions made by
  // PreviewsBlackList to |ignored|. Virtualized in testing.
  virtual void SetIgnorePreviewsBlacklistDecision(bool ignored);

  // The previews black list that decides whether a navigation can use previews.
  PreviewsBlackList* black_list() const { return previews_black_list_.get(); }

  // PreviewsDecider implementation:
  bool ShouldAllowPreview(const net::URLRequest& request,
                          PreviewsType type) const override;
  bool ShouldAllowPreviewAtECT(
      const net::URLRequest& request,
      PreviewsType type,
      net::EffectiveConnectionType effective_connection_type_threshold,
      const std::vector<std::string>& host_blacklist_from_server,
      bool ignore_long_term_black_list_rules) const override;
  bool IsURLAllowedForPreview(const net::URLRequest& request,
                              PreviewsType type) const override;

  // Generates a page ID that is guaranteed to be unique from any other page ID
  // generated in this browser session. Also, guaranteed to be non-zero.
  uint64_t GeneratePageId();

 protected:
  // Posts a task to SetIOData for |previews_ui_service_| on the UI thread with
  // a weak pointer to |this|. Virtualized for testing.
  virtual void InitializeOnIOThread(
      std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
      blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews);

  // Sets a blacklist for testing.
  void SetPreviewsBlacklistForTesting(
      std::unique_ptr<PreviewsBlackList> previews_back_list);

 private:
  // Whether |request| is allowed for |type| according to server provided
  // optimization hints, if available. Returns ALLOWED if no optimization
  // hints are available.
  PreviewsEligibilityReason IsPreviewAllowedByOptmizationHints(
      const net::URLRequest& request,
      PreviewsType type,
      std::vector<PreviewsEligibilityReason>* passed_reasons) const;

  // The UI thread portion of the inter-thread communication for previews.
  base::WeakPtr<PreviewsUIService> previews_ui_service_;

  std::unique_ptr<PreviewsBlackList> previews_black_list_;

  // Only used when the blacklist has been disabled to allow "Show Original" to
  // function as expected. The time of the most recent opt out event.
  base::Time last_opt_out_time_;

  // Holds optimization guidance from the server.
  std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide_;

  // Whether the decisions made by PreviewsBlackList should be ignored or not.
  // This can be changed by chrome://interventions-internals to test/debug the
  // behavior of Previews decisions.
  bool blacklist_ignored_;

  base::Clock* clock_;

  // The UI and IO thread task runners. |ui_task_runner_| is used to post
  // tasks to |previews_ui_service_|, and |io_task_runner_| is used to post from
  // Initialize to InitializeOnIOThread as well as verify that execution is
  // happening on the IO thread.
  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;

  // Whether the preview is enabled. Valid after Initialize() is called.
  PreviewsIsEnabledCallback is_enabled_callback_;

  uint64_t page_id_;

  base::WeakPtrFactory<PreviewsDeciderImpl> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(PreviewsDeciderImpl);
};

}  // namespace previews

#endif  // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_
