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

#ifndef COMPONENTS_SKILLS_INTERNAL_SKILLS_SERVICE_IMPL_H_
#define COMPONENTS_SKILLS_INTERNAL_SKILLS_SERVICE_IMPL_H_

#include <memory>
#include <vector>

#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "base/uuid.h"
#include "base/version_info/channel.h"
#include "build/build_config.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/skills/internal/skills_downloader.h"

#if !BUILDFLAG(IS_ANDROID)
#include "components/skills/internal/skills_fetcher.h"
#endif  // !BUILDFLAG(IS_ANDROID)
#include "components/skills/public/skill.h"
#include "components/skills/public/skills_provider.h"
#include "components/skills/public/skills_service.h"
#include "components/skills/public/skills_types.h"
#include "components/sync/model/data_type_store.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace syncer {
class DataTypeControllerDelegate;
}  // namespace syncer

namespace optimization_guide {
class OptimizationGuideDecider;
}  // namespace optimization_guide

namespace signin {
class IdentityManager;
}  // namespace signin

class PrefService;

namespace skills {

class SkillsSyncBridge;

// This class is the main implementation of SkillsService. It is responsible for
// managing skills, maintaining contextually relevant skills, and invoke skills.
// It also notifies observers when skills are changed.
class SkillsServiceImpl : public SkillsService {
 public:
  SkillsServiceImpl(
      PrefService* pref_service,
      optimization_guide::OptimizationGuideDecider* optimization_guide,
      signin::IdentityManager* identity_manager,
      version_info::Channel channel,
      syncer::OnceDataTypeStoreFactory create_store_callback,
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
  ~SkillsServiceImpl() override;

  // KeyedService implementation.
  void Shutdown() override;

  // SkillsService implementation.
  ServiceStatus GetServiceStatus() const override;
  void LoadInitialSkills(
      std::vector<std::unique_ptr<Skill>> initial_skills) override;
  // TODO(crbug.com/475863107) Add strong typing to help caller avoid swapping
  // order of arguments.
  const Skill* AddSkill(const std::string& source_skill_id,
                        const std::string& name,
                        const std::string& icon,
                        const std::string& prompt) override;

  const Skill* AddOrUpdateSkillFromSync(std::string_view skill_id,
                                        std::string_view source_skill_id,
                                        std::string_view name,
                                        std::string_view icon,
                                        std::string_view prompt,
                                        std::string_view description,
                                        base::Time creation_time,
                                        base::Time last_update_time,
                                        sync_pb::SkillSource source) override;

  // TODO(crbug.com/475863107) Add strong typing to help caller avoid swapping
  // order of arguments.
  const Skill* UpdateSkill(std::string_view skill_id,
                           std::string_view name,
                           std::string_view icon,
                           std::string_view prompt) override;

  void DeleteSkill(std::string_view skill_id,
                   UpdateSource update_source) override;
  const Skill* GetSkillById(std::string_view skill_id) const override;
  void RefreshDiscoverySkills() override;
  void FetchDiscoverySkills() override;
  void Handle1pSkills(
      std::unique_ptr<FirstPartySkillData> first_party_skill_data) override;
#if !BUILDFLAG(IS_ANDROID)
  void OnDiscoverySkillsFetchedFromService(
      std::unique_ptr<FirstPartySkillData> first_party_skill_data);
#endif  // !BUILDFLAG(IS_ANDROID)
  const SkillProtoList& Get1PSkills() const override;
  const std::vector<skills::proto::TopicInfo>& Get1PTopicsInfo() const override;
  const std::vector<std::unique_ptr<Skill>>& GetSkills() const override;
  const std::unordered_map<std::string, std::unique_ptr<Skill>>&
  GetProvidedSkills() const override;
  void AddObserver(Observer* observer) override;
  void RemoveObserver(Observer* observer) override;
  base::WeakPtr<syncer::DataTypeControllerDelegate> GetControllerDelegate()
      override;
  void SyncStatusChanged() override;
  void SetServiceStatusForTesting(ServiceStatus status) override;
  void NotifyTemporarySkillDisplayChanged(std::string_view skill_id,
                                          DisplayState display_state) override;
  void NotifyPanelWillOpen() override;
  void AddProvider(std::unique_ptr<SkillsProvider> provider) override;

  void OnProviderSkillsChanged(SkillsProvider* provider);

 private:
  void NotifySkillChanged(std::string_view skill_id,
                          UpdateSource update_source,
                          bool is_position_changed);

  // Adds a skill to the service and returns the created skill.
  const Skill* AddSkillImpl(std::unique_ptr<Skill> skill,
                            UpdateSource update_source);

  // Returns a mutable skill with the given ID or nullptr if not found.
  Skill* GetMutableSkillById(std::string_view skill_id);

  // Returns the position of the skill with the given ID or nullopt if not
  // found.
  std::optional<size_t> GetSkillPosition(std::string_view skill_id) const;

  // Updates an existing `skill` with the given data. `update_time` is used only
  // if the skill is actually updated with new data or if updated from sync.
  void UpdateSkillImpl(Skill* skill,
                       std::string_view name,
                       std::string_view icon,
                       std::string_view prompt,
                       std::string_view description,
                       base::Time update_time,
                       UpdateSource update_source);

  // Whether the service is initialized, i.e. LoadInitialSkills() has been
  // called.
  bool is_initialized_ = false;

  // Sorts the skills by name in alphabetical order.
  void SortSkills();

  // Called when the Skills enabled preference changes.
  void OnSkillsEnabledPrefChanged();

  // The list of skills managed by this service.
  std::vector<std::unique_ptr<Skill>> skills_;

  // The map of non-1P discovery skills provided by registered SkillsProviders
  // to be shown on the browse page.
  std::unordered_map<std::string, std::unique_ptr<Skill>>
      provided_skill_objects_map_;

  // The list of skill providers.
  std::vector<std::unique_ptr<SkillsProvider>> providers_;

  // The list of subscriptions to the skills providers.
  std::vector<base::CallbackListSubscription> provider_subscriptions_;

  // The struct of loaded 1p discovery skill protos and topics list.
  FirstPartySkillData first_party_data_;

  // The map of loaded 1p discovery skill objects.
  SkillIdToSkillMap first_party_skill_objects_map_;

  // The list of observers to be notified on changes.
  base::ObserverList<Observer,
                     /*check_empty=*/true,
                     base::ObserverListReentrancyPolicy::kDisallowReentrancy>
      observers_;

  // Sync bridge for skills.
  std::unique_ptr<SkillsSyncBridge> sync_bridge_;

  // Downloader for 1P skills.
  std::unique_ptr<SkillsDownloader> skills_downloader_;

  // Fetcher for 1P skills from Boq API.
#if !BUILDFLAG(IS_ANDROID)
  std::unique_ptr<SkillsFetcher> skills_fetcher_;
#endif  // !BUILDFLAG(IS_ANDROID)

  raw_ptr<PrefService> pref_service_;
  raw_ptr<optimization_guide::OptimizationGuideDecider> optimization_guide_;
  PrefChangeRegistrar pref_registrar_;

  // Identity manager for OAuth.
  raw_ptr<signin::IdentityManager> identity_manager_;

  // URL loader factory for fetching skills.
  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;

  // Service status for testing purposes which overrides the actual service
  // status.
  std::optional<ServiceStatus> service_status_for_testing_;

  // A timer for periodically fetching discovery skills.
  base::RepeatingTimer discovery_skills_refresh_timer_;

  // The last time the discovery skills were fetched.
  base::Time last_discovery_skills_fetch_time_;

  // Weak pointer factory for posting tasks.
  base::WeakPtrFactory<SkillsServiceImpl> weak_ptr_factory_{this};
};

}  // namespace skills

#endif  // COMPONENTS_SKILLS_INTERNAL_SKILLS_SERVICE_IMPL_H_
