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

#ifndef CHROME_BROWSER_ASH_BOCA_ON_TASK_ON_TASK_LOCKED_SESSION_WINDOW_TRACKER_H_
#define CHROME_BROWSER_ASH_BOCA_ON_TASK_ON_TASK_LOCKED_SESSION_WINDOW_TRACKER_H_

#include <memory>

#include "base/callback_list.h"
#include "base/cancelable_callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/browser_delegate/browser_controller.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chromeos/ash/components/boca/on_task/on_task_blocklist.h"
#include "chromeos/ash/components/boca/on_task/on_task_notifications_manager.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"

class BrowserWindowInterface;

namespace content::webid {
class IdentityCredentialSource;
}

namespace ash {
class BrowserDelegate;
class OnTaskPodController;
}  // namespace ash

namespace ash::boca {
class BocaWindowObserver;
}

// This class is used to track the windows and tabs that are opened in the
// user's OnTask locked session. Only one browser window is allowed at a time to
// be tracked. Attempting to track another browser while there is one already
// tracked will reset the tracker and setup for the new browser. It will be used
// to block the navigation of the tabs that are not allowed to be opened in the
// locked session. Each tab has its set of rules as defined in the
// `OnTaskBlocklist` which determines what types of urls are allowed on a per
// tab basis. See `OnTaskBlocklist` for more details about what the restrictions
// are. All of these calls should be called from the main thread.
class LockedSessionWindowTracker : public KeyedService,
                                   public TabStripModelObserver,
                                   public ash::BrowserController::Observer,
                                   public content::WebContentsObserver {
 public:
  LockedSessionWindowTracker(std::unique_ptr<OnTaskBlocklist> on_task_blocklist,
                             content::BrowserContext* context);
  LockedSessionWindowTracker(const LockedSessionWindowTracker&) = delete;
  LockedSessionWindowTracker& operator=(const LockedSessionWindowTracker&) =
      delete;
  ~LockedSessionWindowTracker() override;

  void AddObserver(ash::boca::BocaWindowObserver* observer);
  void RemoveObserver(ash::boca::BocaWindowObserver* observer);

  // Starts tracking the `browser` for navigation changes.
  void InitializeBrowserInfoForTracking(ash::BrowserDelegate* browser);

  // Displays a toast that indicates the URL was blocked.
  void ShowURLBlockedToast();

  // Updates the current blocklist with its appropriate restriction. This should
  // rarely be explicitly called except for when we start tracking a new browser
  // window. All other calls should come from tab strip model changes (ex:
  // active tab changes).
  // TODO: b/357139784 - Remove RefreshBlockList.
  void RefreshUrlBlocklist();

  // Checks to make sure this is the first time an OAuth popup has occurred.
  // This is to make sure popup retries don't try to reopen windows while older
  // popups are still open.
  bool CanOpenNewPopup();

  // Observe the web contents so that we can close any unintended popup windows
  // or new tabs that are opened when a navigation
  void ObserveWebContents(content::WebContents* web_content);

  // Callback triggered to configure the browsing instance and the OnTask pod
  // when entering or exiting pause mode.
  void OnPauseModeChanged(bool paused);

  bool can_start_navigation_throttle() {
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    return can_start_navigation_throttle_;
  }
  virtual void set_can_start_navigation_throttle(bool is_ready);

  bool oauth_in_progress() { return oauth_in_progress_; }
  void set_oauth_in_progress(bool in_progress, ash::BrowserDelegate* browser);

  ash::OnTaskPodController* on_task_pod_controller();
  OnTaskBlocklist* on_task_blocklist();
  BrowserWindowInterface* browser();

  // Test helpers:
  void SetNotificationManagerForTesting(
      std::unique_ptr<ash::boca::OnTaskNotificationsManager>
          notification_manager);
  void SetIdentityCredentialSourceForTesting(
      content::webid::IdentityCredentialSource* source);
  void TriggerFedCmFederatedLoginCompletionForTesting(bool success);

 private:
  // TabStripModelObserver Impl
  void OnTabChangedAt(tabs::TabInterface* tab,
                      TabChangeType change_type) override;
  void OnTabStripModelChanged(
      TabStripModel* tab_strip_model,
      const TabStripModelChange& change,
      const TabStripSelectionChange& selection) override;
  void OnTabWillBeRemoved(tabs::TabInterface* tab, int index) override;
  void WillCloseAllTabs(TabStripModel* tab_strip_model) override;

  // ash::BrowserController::Observer:
  void OnBrowserCreated(ash::BrowserDelegate* browser) override;
  void OnBrowserActivated(ash::BrowserDelegate* browser) override;
  void OnBrowserClosed(ash::BrowserDelegate* browser) override;

  // content::WebContentsObserver Impl
  void DidFinishNavigation(
      content::NavigationHandle* navigation_handle) override;
  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
                     const GURL& validated_url) override;
  void OnFedCmFederatedLogin(bool success) override;

  // Callback for browser closed events.
  void OnBrowserDidClose(BrowserWindowInterface* browser_window_interface);

  void MaybeCloseWebContents(base::WeakPtr<content::WebContents> weak_tab_ptr);
  void MaybeCloseBrowser(ash::BrowserDelegate* browser);
  void EnsureMaybeCloseBrowserTaskPosted(ash::BrowserDelegate* browser);

  content::webid::IdentityCredentialSource* GetIdentityCredentialSource(
      content::Page& page);

  void CleanupWindowTracker();

  bool can_open_new_popup_ = true;
  bool can_start_navigation_throttle_ = true;
  bool oauth_in_progress_ = false;
  const std::unique_ptr<OnTaskBlocklist> on_task_blocklist_;
  const bool is_consumer_profile_;
  std::unique_ptr<ash::boca::OnTaskNotificationsManager> notifications_manager_;
  std::unique_ptr<ash::OnTaskPodController> on_task_pod_controller_;
  raw_ptr<ash::BrowserDelegate> browser_ = nullptr;
  raw_ptr<ash::BrowserDelegate> authorized_oauth_browser_ = nullptr;
  raw_ptr<content::webid::IdentityCredentialSource>
      identity_credential_source_for_testing_ = nullptr;
  base::ScopedObservation<ash::BrowserController,
                          ash::BrowserController::Observer>
      browser_controller_observation_{this};
  absl::flat_hash_map<ash::BrowserDelegate*,
                      std::unique_ptr<base::CancelableOnceClosure>>
      pending_close_tasks_;
  base::ObserverList<ash::boca::BocaWindowObserver> observers_;
  base::WeakPtrFactory<LockedSessionWindowTracker> weak_pointer_factory_{this};
};

#endif  // CHROME_BROWSER_ASH_BOCA_ON_TASK_ON_TASK_LOCKED_SESSION_WINDOW_TRACKER_H_
