// Copyright 2021 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_WEB_APPLICATIONS_SUB_APPS_SUB_APPS_SERVICE_IMPL_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_SUB_APPS_SUB_APPS_SERVICE_IMPL_H_

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/types/expected.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_metrics_helper.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "components/webapps/browser/install_result_code.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/document_service.h"
#include "third_party/blink/public/mojom/subapps/sub_apps_service.mojom.h"
#include "url/origin.h"

namespace content {
class RenderFrameHost;
}

namespace web_app {

// Total limit of sub apps per parent app.
// Number of sub apps per parent app cannot be more than that,
// regardless of how much sub apps they install per single API call.
BASE_DECLARE_FEATURE(kSubAppsInstallLimit);
extern const base::FeatureParam<int> kSubAppsInstallLimitParam;

// Max number of sub apps that can be installed
// via single call to add, excluding the enterprose policy case.
BASE_DECLARE_FEATURE(kSubAppsPerPromptLimit);
extern const base::FeatureParam<int> kSubAppsPerPromptLimitParam;

namespace {

struct SubAppInstallResult {
  SubAppInstallResult(GURL install_url,
                      webapps::ManifestId manifest_id,
                      webapps::InstallResultCode install_result_code);
  ~SubAppInstallResult();
  SubAppInstallResult(const SubAppInstallResult&);
  SubAppInstallResult& operator=(const SubAppInstallResult&);
  SubAppInstallResult(SubAppInstallResult&&);
  SubAppInstallResult& operator=(SubAppInstallResult&&);

  GURL install_url;
  webapps::ManifestId manifest_id;
  webapps::InstallResultCode install_result_code;
};

}  // namespace

// Internal enum to represent error codes of add function.
// It is remapped to ukm and mojo corresponding enums.
enum class AddCallErrorCode {
  kUserDeclined,
  kUserDeclinedEmbargo,
  kTotalLimitExceeded,
  kPerPromptLimitExceeded,
  kWebAppsNotUserInstallable,
};

class SubAppsServiceImpl
    : public content::DocumentService<blink::mojom::SubAppsService> {
 public:
  using AddResultsMojo = std::vector<blink::mojom::SubAppsServiceAddResultPtr>;

  static constexpr char kSubAppsUninstallNotificationId[] =
      "sub_apps_uninstall_notification";

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

  // We only want to create this object when the Browser* associated with the
  // WebContents is an Isolated Web App and when the RFH is the main frame.
  static void CreateIfAllowed(
      content::RenderFrameHost* render_frame_host,
      mojo::PendingReceiver<blink::mojom::SubAppsService> receiver);

  // blink::mojom::SubAppsService
  void Add(const std::vector<std::string>& install_paths,
           AddCallback result_callback) override;
  void List(ListCallback result_callback) override;
  void Remove(const std::vector<std::string>& manifest_ids,
              RemoveCallback result_callback) override;

 private:
  using AddResult =
      base::expected<std::vector<blink::mojom::SubAppsServiceAddResultPtr>,
                     AddCallErrorCode>;

  struct AddCallInfo {
    AddCallInfo();
    ~AddCallInfo();

    // The callback to run when the API call is complete.
    base::OnceCallback<void(AddResult)> mojo_callback;

    // The list of results for each requested install.
    std::vector<blink::mojom::SubAppsServiceAddResultPtr> results;

    // The list of install infos collected from the install URLs.
    std::vector<std::unique_ptr<WebAppInstallInfo>> install_infos;

    bool install_bypassed_prompt = false;
  };

  void CollectInstallData(int add_call_id,
                          std::vector<GURL> requested_installs,
                          webapps::ManifestId parent_manifest_id);
  void ProcessInstallData(
      int add_call_id,
      std::vector<std::pair<GURL, std::unique_ptr<WebAppInstallInfo>>>
          install_data);
  void ScheduleSubAppInstalls(int add_call_id);
  void ProcessDialogResponse(int add_call_id, bool dialog_accepted);
  void FinishAddCallOrShowInstallDialog(int add_call_id);
  void FinishAddCall(int add_call_id,
                     std::vector<SubAppInstallResult> install_results);
  void ReportAddMetricsAndRunCallback(const url::Origin& parent_origin,
                                      int add_call_id,
                                      AddCallback original_callback,
                                      AddResult result);

  void RemoveSubApp(
      const std::string& manifest_id,
      base::OnceCallback<void(blink::mojom::SubAppsServiceRemoveResultPtr)>
          remove_barrier_callback,
      const webapps::AppId* calling_app_id);
  void NotifyUninstall(
      RemoveCallback result_callback,
      std::vector<blink::mojom::SubAppsServiceRemoveResultPtr> remove_results);

  WebAppProvider& provider() const;

  SubAppsServiceImpl(
      content::RenderFrameHost& render_frame_host,
      mojo::PendingReceiver<blink::mojom::SubAppsService> receiver);

  int next_add_call_id_ = 0;
  std::map<int, AddCallInfo> add_call_info_;

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

}  // namespace web_app

#endif  // CHROME_BROWSER_WEB_APPLICATIONS_SUB_APPS_SUB_APPS_SERVICE_IMPL_H_
