// Copyright 2019 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_URL_LOADING_MODEL_URL_LOADING_BROWSER_AGENT_H_
#define IOS_CHROME_BROWSER_URL_LOADING_MODEL_URL_LOADING_BROWSER_AGENT_H_

#import <Foundation/Foundation.h>

#import <memory>
#import <string>
#import <unordered_map>

#import "base/memory/raw_ptr.h"
#import "base/memory/weak_ptr.h"
#import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
#import "url/gurl.h"

class SceneUrlLoadingService;
class Browser;
class UrlLoadingNotifierBrowserAgent;
struct UrlLoadParams;
namespace web {
class WebState;
class WebStateID;
}  // namespace web

class URLInterceptor;

// A delegate for URL loading that can handle UI animations that are needed at
// specific points in the loading cycle.
@protocol URLLoadingDelegate

// Implementing delegate can do an animation using information in `params` when
// opening a background tab, then call `completion`.
- (void)animateOpenBackgroundTabFromParams:(const UrlLoadParams&)params
                                completion:(void (^)())completion;

@end

// Service used to load url in current or new tab.
class UrlLoadingBrowserAgent : public BrowserUserData<UrlLoadingBrowserAgent> {
 public:
  // Not copyable or moveable.
  UrlLoadingBrowserAgent(const UrlLoadingBrowserAgent&) = delete;
  UrlLoadingBrowserAgent& operator=(const UrlLoadingBrowserAgent&) = delete;
  ~UrlLoadingBrowserAgent() override;

  void SetSceneService(SceneUrlLoadingService* app_service);
  void SetIncognitoLoader(UrlLoadingBrowserAgent* loader);
  void SetDelegate(id<URLLoadingDelegate> delegate);

  // Adds an interceptor for the given URL.
  // Returns false if an interceptor overlapping with `url` is already
  // registered.
  [[nodiscard]] bool AddInterceptor(
      const GURL& url,
      std::unique_ptr<URLInterceptor> interceptor);

  // Removes the interceptor for the given URL.
  void RemoveInterceptor(const GURL& url);

  // Applies load strategy then calls `Dispatch`.
  void Load(const UrlLoadParams& params);

  // Loads URL representing the `query`.
  void LoadURLForQuery(NSString* query);

  // Loads a url based on `params` in `target_web_state`.
  //
  // `target_web_state` is not necessarily the active web state and calling
  // this method will not make it active. `target_web_state` must also already
  // be realized and belong to the same Browser as this agent.
  void LoadUrlInTab(const UrlLoadParams& params,
                    web::WebState* target_web_state);

  // Provides a weak pointer to this instance.
  base::WeakPtr<UrlLoadingBrowserAgent> AsWeakPtr();

 private:
  friend class BrowserUserData<UrlLoadingBrowserAgent>;
  friend class FakeUrlLoadingBrowserAgent;

  explicit UrlLoadingBrowserAgent(Browser* browser);

  // Dispatches to one action method below, depending on `params.disposition`.
  void Dispatch(const UrlLoadParams& params);

  // Action methods.
  // Switches to a tab that matches `params.web_params` or loads in a new tab.
  virtual void SwitchToTab(const UrlLoadParams& params);

  // Loads a url based on `params` in current tab.
  virtual void LoadUrlInCurrentTab(const UrlLoadParams& params);

  // Loads a url based on `params` in a new tab.
  virtual void LoadUrlInNewTab(const UrlLoadParams& params);

  // Helper function implementing the creation and insertion of the new tab
  // for LoadUrlInNewTab(). It is split to a separate function as it can be
  // called asynchronously if the tab is opened in a background (and moving
  // it to a separate function makes it safer not to capture state that can
  // become invalid when creating the asynchronous task).
  void LoadUrlInNewTabImpl(const UrlLoadParams& params,
                           web::WebStateID active_tab_id);

  __weak id<URLLoadingDelegate> delegate_;
  raw_ptr<UrlLoadingNotifierBrowserAgent, DanglingUntriaged> notifier_ =
      nullptr;
  raw_ptr<UrlLoadingBrowserAgent, DanglingUntriaged> incognito_loader_ =
      nullptr;
  raw_ptr<SceneUrlLoadingService, DanglingUntriaged> scene_service_ = nullptr;

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

#endif  // IOS_CHROME_BROWSER_URL_LOADING_MODEL_URL_LOADING_BROWSER_AGENT_H_
