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

#ifndef IOS_CHROME_BROWSER_SUPERVISED_USER_MODEL_SUPERVISED_USER_ERROR_CONTAINER_H_
#define IOS_CHROME_BROWSER_SUPERVISED_USER_MODEL_SUPERVISED_USER_ERROR_CONTAINER_H_

#include <set>

#import "base/memory/raw_ref.h"
#import "base/memory/weak_ptr.h"
#import "base/scoped_observation.h"
#import "components/supervised_user/core/browser/supervised_user_interstitial.h"
#import "components/supervised_user/core/browser/supervised_user_url_filtering_service.h"
#import "components/supervised_user/core/browser/supervised_user_utils.h"
#import "ios/components/security_interstitials/ios_blocking_page_controller_client.h"
#import "ios/components/security_interstitials/ios_security_interstitial_page.h"
#import "ios/web/public/web_state_observer.h"
#import "ios/web/public/web_state_user_data.h"
#import "url/gurl.h"

@protocol ParentAccessCommands;

namespace supervised_user {
class FamilyLinkSettingsService;
class SupervisedUserService;
}  // namespace supervised_user

namespace web {
class WebState;
}

// Exposed for testing. It allows us to confirm that a Supervised User
// interstitial is created for a web state.
extern const char kSupervisedUserInterstitialType[];

using RequestUrlAccessRemoteCallback = base::OnceCallback<void(bool)>;

// Helper object that holds information needed for the supervised user
// interstitial functionality and error page.
class SupervisedUserErrorContainer
    : public web::WebStateUserData<SupervisedUserErrorContainer>,
      public supervised_user::SupervisedUserUrlFilteringService::Observer {
 public:
  SupervisedUserErrorContainer(SupervisedUserErrorContainer& other);
  SupervisedUserErrorContainer& operator=(SupervisedUserErrorContainer& other);
  ~SupervisedUserErrorContainer() override;

  // Structure that contains information for the supervised user interstitial
  // error page UI.
  class SupervisedUserErrorInfo {
   public:
    SupervisedUserErrorInfo(
        supervised_user::WebFilteringResult filtering_result,
        bool is_main_frame);
    SupervisedUserErrorInfo() = delete;
    SupervisedUserErrorInfo(const SupervisedUserErrorInfo& other) = delete;
    SupervisedUserErrorInfo& operator=(const SupervisedUserErrorInfo& other) =
        delete;

    supervised_user::WebFilteringResult filtering_result() const {
      return filtering_result_;
    }
    bool is_main_frame() const { return is_main_frame_; }

   private:
    supervised_user::WebFilteringResult filtering_result_;
    bool is_main_frame_;
  };

  // Stores info associated with a supervised user interstitial error page.
  void SetSupervisedUserErrorInfo(
      std::unique_ptr<SupervisedUserErrorInfo> error_info);

  // Returns and moves ownership of currently stored info associated with an
  // error page.
  std::unique_ptr<SupervisedUserErrorInfo> ReleaseSupervisedUserErrorInfo() {
    return std::move(supervised_user_error_info_);
  }

  // Creates an instance of the SupervisedUserInterstitial from the
  // provided `error_info`.
  std::unique_ptr<supervised_user::SupervisedUserInterstitial>
  CreateSupervisedUserInterstitial(SupervisedUserErrorInfo& error_info);

  // Dispatches a supervised user interstitial command to the given
  // `interstitial` for execution.
  void HandleCommand(
      supervised_user::SupervisedUserInterstitial& interstitial,
      security_interstitials::SecurityInterstitialCommand command);

  // Checks if the `url` host has been already requested for approval.
  bool IsRemoteApprovalPendingForUrl(const GURL& url);

  void OnUrlFilteringServiceChanged() override;

  // Sets the parent access bottom sheet CommandDispatcher.
  void SetParentAccessBottomSheetHandler(
      id<ParentAccessCommands> commands_handler);

 private:
  friend class web::WebStateUserData<SupervisedUserErrorContainer>;

  explicit SupervisedUserErrorContainer(web::WebState* web_state);

  void OnRequestCreated(RequestUrlAccessRemoteCallback callback,
                        const GURL& url,
                        bool successfully_created_request);
  void MaybeUpdatePendingApprovals();
  void URLFilterCheckCallback(supervised_user::WebFilteringResult result);

  // Handler used to request showing the parent access bottom sheet.
  __weak id<ParentAccessCommands> commands_handler_;
  std::unique_ptr<SupervisedUserErrorInfo> supervised_user_error_info_;
  raw_ref<supervised_user::FamilyLinkSettingsService>
      family_link_settings_service_;
  raw_ref<supervised_user::SupervisedUserService> supervised_user_service_;
  raw_ref<supervised_user::SupervisedUserUrlFilteringService>
      supervised_user_url_filtering_service_;
  base::ScopedObservation<
      supervised_user::SupervisedUserUrlFilteringService,
      supervised_user::SupervisedUserUrlFilteringService::Observer>
      url_filtering_service_observation_{this};
  raw_ptr<web::WebState> web_state_;
  std::set<std::string> requested_hosts_;
  base::WeakPtrFactory<SupervisedUserErrorContainer> weak_ptr_factory_{this};
};

// Wrapper class for the supervised user interstitial instance. It allows use to
// manage the interstitial's lifecysle through `IOSBlockingPageTabHelper`.
class SupervisedUserInterstitialBlockingPage
    : public security_interstitials::IOSSecurityInterstitialPage,
      public web::WebStateObserver {
 public:
  SupervisedUserInterstitialBlockingPage(
      std::unique_ptr<supervised_user::SupervisedUserInterstitial> interstitial,
      std::unique_ptr<security_interstitials::IOSBlockingPageControllerClient>
          controller_client,
      SupervisedUserErrorContainer* error_container,
      web::WebState* web_state);
  ~SupervisedUserInterstitialBlockingPage() override;
  supervised_user::SupervisedUserInterstitial& interstitial() {
    return *interstitial_;
  }

 private:
  // security_interstitials::IOSSecurityInterstitialPage implementation:
  void HandleCommand(
      security_interstitials::SecurityInterstitialCommand command) override;
  bool ShouldCreateNewNavigation() const override;
  void PopulateInterstitialStrings(
      base::DictValue& load_time_data) const override;
  std::string_view GetInterstitialType() const override;

  // web::WebStateObserver implementation:
  void WebStateDestroyed(web::WebState* web_state) override;

  const std::unique_ptr<supervised_user::SupervisedUserInterstitial>
      interstitial_;
  std::unique_ptr<security_interstitials::IOSBlockingPageControllerClient>
      controller_client_;
  raw_ptr<web::WebState> web_state_;
  raw_ptr<SupervisedUserErrorContainer> error_container_;
  base::ScopedObservation<web::WebState, web::WebStateObserver>
      scoped_observation_{this};
};

#endif  // IOS_CHROME_BROWSER_SUPERVISED_USER_MODEL_SUPERVISED_USER_ERROR_CONTAINER_H_
