// Copyright 2025 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_SAVE_TO_DRIVE_SAVE_TO_DRIVE_FLOW_H_
#define CHROME_BROWSER_SAVE_TO_DRIVE_SAVE_TO_DRIVE_FLOW_H_

#include <memory>
#include <optional>

#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "chrome/common/extensions/api/pdf_viewer_private.h"
#include "content/public/browser/document_user_data.h"

struct AccountInfo;

namespace content {
class RenderFrameHost;
}  // namespace content

class HatsService;

namespace save_to_drive {

class AccountChooser;
class ContentReader;
class DriveUploader;
class SaveToDriveEventDispatcher;

// This class is responsible for orchastrating the entire save to Drive flow
// on the browser process from showing the account chooser, reading the file
// size, and initiating the appropriate drive uploader to save the file to
// Drive. This flow will be tied to the lifetime of the document. It is
// responsible for cleaning up its resources when the flow is stopped.
// This flow should only be called on the UI thread.
class SaveToDriveFlow : public content::DocumentUserData<SaveToDriveFlow> {
 public:
  using CreateCallback = base::RepeatingCallback<SaveToDriveFlow*(
      content::RenderFrameHost* render_frame_host,
      std::unique_ptr<SaveToDriveEventDispatcher> event_dispatcher,
      std::unique_ptr<ContentReader> content_reader,
      std::unique_ptr<AccountChooser> account_chooser,
      HatsService* hats_service)>;

  // Factory method to create a new instance of `SaveToDriveFlow`. This is
  // used to allow for creating a mock flow in tests through
  // `SetCreateCallbackForTesting`.
  static SaveToDriveFlow* Create(
      content::RenderFrameHost* render_frame_host,
      std::unique_ptr<SaveToDriveEventDispatcher> event_dispatcher,
      std::unique_ptr<ContentReader> content_reader,
      std::unique_ptr<AccountChooser> account_chooser,
      HatsService* hats_service);

  // Sets the callback to create a new instance of `SaveToDriveFlow`. This
  // is used to create a mock flow in tests.
  static void SetCreateCallbackForTesting(CreateCallback* callback);

  // Sets whether or not `HasValidTabId()` will return true for test.
  static void SetSkipValidateTabForTesting(bool skip_check_tab);

  SaveToDriveFlow(const SaveToDriveFlow&) = delete;
  SaveToDriveFlow& operator=(const SaveToDriveFlow&) = delete;
  ~SaveToDriveFlow() override;

  // Starts the save to Drive flow. Marked virtual for testing.
  virtual void Run();

  // Cleans up the flow and its resources. This is called when the flow is
  // aborted or completed. Marked virtual for testing.
  virtual void Stop();

  // Returns weather the stream container associated with the
  // `render_frame_host` has a tab id with valid web content. Also returns true
  // when `SetSkipValidateTabForTesting(true)` has been called."
  static bool HasValidTabId(content::RenderFrameHost* render_frame_host);

  class TestApi {
   public:
    explicit TestApi(SaveToDriveFlow* flow);
    TestApi(const TestApi&) = delete;
    TestApi& operator=(const TestApi&) = delete;
    ~TestApi();

    // It will never return `nullptr' before the `flow_` runs.
    const ContentReader* content_reader() const;
    const DriveUploader* drive_uploader() const;
    // It will never return `nullptr' before the `flow_` runs.
    const SaveToDriveEventDispatcher* event_dispatcher() const;
    // It will never return `nullptr` before the `flow_` runs.
    content::RenderFrameHost* rfh();

   private:
    base::WeakPtr<SaveToDriveFlow> flow_;
  };

 protected:
  SaveToDriveFlow(content::RenderFrameHost* render_frame_host,
                  std::unique_ptr<SaveToDriveEventDispatcher> event_dispatcher,
                  std::unique_ptr<ContentReader> content_reader,
                  std::unique_ptr<AccountChooser> account_chooser,
                  HatsService* hats_service);

 private:
  friend class content::DocumentUserData<SaveToDriveFlow>;
  friend class TestApi;

  struct SaveToDriveAccountInfo {
    // The email of the account that is chosen to save the PDF to Drive. It is
    // used to construct the URL to open the file in Drive or manage the quota.
    std::string email;

    // Whether the account is a managed account. It is used to determine whether
    // the account is a dasher account and show the correct manage storage URL.
    bool is_managed = false;
  };
  void OnAccountChosen(std::optional<AccountInfo> account_info);
  void OnOpenContent(AccountInfo account_info, bool success);
  void OnUploadProgress(
      extensions::api::pdf_viewer_private::SaveToDriveProgress progress);
  void ShowHatsSurveyWithDelay();
  std::string GetHatsSurveyTriggerId();

  std::unique_ptr<SaveToDriveEventDispatcher> event_dispatcher_;
  std::unique_ptr<ContentReader> content_reader_;
  std::unique_ptr<DriveUploader> drive_uploader_;
  std::unique_ptr<AccountChooser> account_chooser_;
  raw_ptr<HatsService> hats_service_ = nullptr;

  // This is set when an account is chosen.
  std::optional<SaveToDriveAccountInfo> save_to_drive_account_info_;
  // This is the title of the document that is being saved to Drive.
  std::string upload_title_;
  // This is set after the upload starts.
  std::optional<extensions::api::pdf_viewer_private::SaveToDriveProgress>
      upload_progress_;

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

  DOCUMENT_USER_DATA_KEY_DECL();
};

}  // namespace save_to_drive

#endif  // CHROME_BROWSER_SAVE_TO_DRIVE_SAVE_TO_DRIVE_FLOW_H_
