// 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_ASH_WALLPAPER_HANDLERS_GOOGLE_PHOTOS_WALLPAPER_HANDLERS_H_
#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_GOOGLE_PHOTOS_WALLPAPER_HANDLERS_H_

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

#include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"

class Profile;

namespace network {
class SimpleURLLoader;
}  // namespace network

namespace signin {
class PrimaryAccountAccessTokenFetcher;
struct AccessTokenInfo;
}  // namespace signin

namespace wallpaper_handlers {

// Base class for common logic among fetchers that query the Google Photos API.
// Parametrized by the client callback's argument type.
template <typename T>
class GooglePhotosFetcher : public signin::IdentityManager::Observer {
 public:
  GooglePhotosFetcher(
      Profile* profile,
      const net::NetworkTrafficAnnotationTag& traffic_annotation);

  GooglePhotosFetcher(const GooglePhotosFetcher&) = delete;
  GooglePhotosFetcher& operator=(const GooglePhotosFetcher&) = delete;

  ~GooglePhotosFetcher() override;

 protected:
  // Issues an API request to `service_url` if and only if one is not in
  // progress. Each subclass is expected to write a public overload of this
  // function that prepares `service_url`--with appended query params from the
  // client if applicable--and delegates the rest of the work to this function.
  using ClientCallback = base::OnceCallback<void(T)>;
  void AddRequestAndStartIfNecessary(const GURL& service_url,
                                     ClientCallback callback);

  // Called when the API request finishes. `response` will be absent if there
  // was an error in sending the request, receiving the response, or parsing the
  // response; otherwise, it will hold a response in the API's specified
  // structure.
  virtual T ParseResponse(const base::DictValue* response) = 0;

  // Contains logic for different HTTP error codes that we receive, as they can
  // carry information on the state of the user's Google Photos library.
  virtual std::optional<base::Value> CreateErrorResponse(int error_code);

  // Returns the result of the managed policy
  // WallpaperGooglePhotosIntegrationEnabled, or true if this pref is
  // not managed.
  virtual bool IsGooglePhotosIntegrationPolicyEnabled() const;

 private:
  void OnTokenReceived(
      std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> fetcher,
      const GURL& service_url,
      GoogleServiceAuthError error,
      signin::AccessTokenInfo token_info);
  void OnJsonReceived(std::unique_ptr<network::SimpleURLLoader> loader,
                      const GURL& service_url,
                      std::optional<std::string> response_body);
  void OnResponseReady(const GURL& service_url,
                       std::optional<base::Value> response);

  // Profile associated with the Google Photos account that will be queried.
  const raw_ptr<Profile> profile_;

  // Supplies `token_fetcher_` with `profile_`'s GAIA account information.
  const raw_ptr<signin::IdentityManager> identity_manager_;
  base::ScopedObservation<signin::IdentityManager,
                          signin::IdentityManager::Observer>
      identity_manager_observation_{this};

  // States metadata about the network request that this fetcher sends.
  const net::NetworkTrafficAnnotationTag traffic_annotation_;

  // Callbacks for each distinct query this fetcher has been asked to make. A
  // URL's callbacks are called and then removed when the download finishes,
  // successfully or in error.
  std::map<GURL, std::vector<ClientCallback>> pending_client_callbacks_;

  base::WeakPtrFactory<GooglePhotosFetcher> weak_factory_{this};
};

using GooglePhotosAlbumsCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponsePtr;
// Downloads the Google Photos albums a user has created.
class GooglePhotosAlbumsFetcher
    : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
 public:
  GooglePhotosAlbumsFetcher(const GooglePhotosAlbumsFetcher&) = delete;
  GooglePhotosAlbumsFetcher& operator=(const GooglePhotosAlbumsFetcher&) =
      delete;

  ~GooglePhotosAlbumsFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& resume_token,
      base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosAlbumsFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosAlbumsCbkArgs ParseResponse(
      const base::DictValue* response) override;

 private:
  // Allow delegate to see the constructor.
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosAlbumsFetcherTest;
};

using GooglePhotosAlbumsCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponsePtr;
// Downloads the Google Photos albums a user has created.
class GooglePhotosSharedAlbumsFetcher
    : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
 public:
  GooglePhotosSharedAlbumsFetcher(const GooglePhotosSharedAlbumsFetcher&) =
      delete;
  GooglePhotosSharedAlbumsFetcher& operator=(
      const GooglePhotosSharedAlbumsFetcher&) = delete;

  ~GooglePhotosSharedAlbumsFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& resume_token,
      base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosSharedAlbumsFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosAlbumsCbkArgs ParseResponse(
      const base::DictValue* response) override;
 private:
  friend class WallpaperFetcherDelegateImpl;
};

using ash::personalization_app::mojom::GooglePhotosEnablementState;
// Downloads whether the user is allowed to access Google Photos data.
class GooglePhotosEnabledFetcher
    : public GooglePhotosFetcher<GooglePhotosEnablementState> {
 public:
  GooglePhotosEnabledFetcher(const GooglePhotosEnabledFetcher&) = delete;
  GooglePhotosEnabledFetcher& operator=(const GooglePhotosEnabledFetcher&) =
      delete;

  ~GooglePhotosEnabledFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      base::OnceCallback<void(GooglePhotosEnablementState)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosEnabledFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosEnablementState ParseResponse(
      const base::DictValue* response) override;

 private:
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosEnabledFetcherTest;
};

using GooglePhotosPhotosCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosPhotosResponsePtr;
// Downloads visible photos from a user's Google Photos library.
class GooglePhotosPhotosFetcher
    : public GooglePhotosFetcher<GooglePhotosPhotosCbkArgs> {
 public:
  GooglePhotosPhotosFetcher(const GooglePhotosPhotosFetcher&) = delete;
  GooglePhotosPhotosFetcher& operator=(const GooglePhotosPhotosFetcher&) =
      delete;

  ~GooglePhotosPhotosFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& item_id,
      const std::optional<std::string>& album_id,
      const std::optional<std::string>& resume_token,
      bool shuffle,
      base::OnceCallback<void(GooglePhotosPhotosCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosPhotosFetcher(Profile* profile);

  // GooglePhotosFetcher:
  std::optional<base::Value> CreateErrorResponse(int error_code) override;
  GooglePhotosPhotosCbkArgs ParseResponse(
      const base::DictValue* response) override;

 private:
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosPhotosFetcherTest;
};

}  // namespace wallpaper_handlers
#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_GOOGLE_PHOTOS_WALLPAPER_HANDLERS_H_
