// 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 COMPONENTS_COLLABORATION_PUBLIC_COMMENTS_COMMENTS_SERVICE_H_
#define COMPONENTS_COLLABORATION_PUBLIC_COMMENTS_COMMENTS_SERVICE_H_

#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "components/data_sharing/public/group_data.h"
#include "components/keyed_service/core/keyed_service.h"
#include "url/gurl.h"

namespace collaboration::comments {

using CollaborationId = data_sharing::GroupId;
using CommentId = base::Uuid;

// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.collaboration.comments
enum class AttributionLevel {
  kUrl,
  kContent,
  // The comment is not attributed to anything specific. This is the default.
  kNone,
};

struct UrlAttribution {
  // The URL being attributed.
  GURL url;
};

struct ContentAttribution {
  // Human-readable preview of the highlighted text.
  std::string preview_text;
  // The URL component used by the browser to find and highlight the text.
  std::string text_fragment;
};

// Represents the specific details of where a comment is attributed.
// A comment with ContentAttribution must also have UrlAttribution.
class AttributionData {
 public:
  AttributionData();
  explicit AttributionData(UrlAttribution url_data);
  AttributionData(UrlAttribution url_data, ContentAttribution content_data);
  ~AttributionData();

  AttributionData(const AttributionData&);
  AttributionData& operator=(const AttributionData&);
  AttributionData(AttributionData&&);
  AttributionData& operator=(AttributionData&&);

  AttributionLevel GetAttributionLevel() const;
  std::optional<UrlAttribution> GetAsUrl() const;
  std::optional<ContentAttribution> GetAsContent() const;

 private:
  std::optional<UrlAttribution> url_data_;
  std::optional<ContentAttribution> content_data_;
};

// Defines a key used for deterministically sorting comments in a distributed
// system. This ensures that all clients render comments in the same stable
// order, regardless of network timing or processing order.
struct OrderKey {
  // The primary sorting timestamp, typically from the server. This value may
  // be locally adjusted to be >= the timestamp of the preceding comment from
  // the same source to guarantee monotonic ordering per source.
  base::Time sort_timestamp;

  // A unique identifier for the device or client that authored the comment
  // (e.g., a UUID). This serves as the main tie-breaker for comments with
  // identical timestamps.
  std::string source_id;

  // A strictly monotonically increasing sequence number generated by the source
  // device. This provides a final, definitive ordering for comments created
  // by the same source, even if they arrive out of order.
  uint64_t local_sequence;
};

// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.collaboration.comments
enum class CommentType {
  kComment,
};

// Represents a single comment. The Query API returns a flat sorted list of
// these objects to the UI.
struct Comment {
  Comment();
  ~Comment();
  Comment(const Comment&);
  Comment& operator=(const Comment&);
  Comment(Comment&&);
  Comment& operator=(Comment&&);

  // Unique ID of the comment.
  CommentId id;
  // If present, this comment is a reply to the specified parent comment.
  std::optional<CommentId> parent_comment_id;
  // The text of the comment.
  std::string content;
  // Information about the user who added the comment.
  std::optional<data_sharing::GroupMember> author_info;
  // Timestamp to be used for display purposes. This might be the creation
  // timestamp or the latest reply timestamp for a thread.
  base::Time display_timestamp;
  // ID of the collaboration context this comment belongs to.
  CollaborationId collaboration_id;
  // Specifies the level and associated data for where the  comment is
  // attributed (e.g., URL, content, group).
  AttributionData data;
  // Defines a key used for deterministically ordering comments.
  OrderKey order_key;
  // Tells the type of comment.
  CommentType comment_type;
};

// Defines the filtering criteria for a query.
struct FilterCriteria {
  FilterCriteria();
  ~FilterCriteria();
  FilterCriteria(const FilterCriteria&);
  FilterCriteria& operator=(const FilterCriteria&);
  FilterCriteria(FilterCriteria&&);
  FilterCriteria& operator=(FilterCriteria&&);

  std::optional<CollaborationId> collaboration_id;
  std::optional<GURL> url;
  // Whether to include replies in the results.
  bool include_threads = true;
};

// Defines the pagination settings for a query.
struct PaginationCriteria {
  // If set to 0, indicates a request for all matching results (no pagination).
  // If set to a positive value, indicates the maximum number of results to
  // return.
  uint64_t max_total_results = 0;
};

// Represents a comment and its replies in a hierarchical structure.
struct HierarchicalComment {
  HierarchicalComment();
  ~HierarchicalComment();
  HierarchicalComment(const HierarchicalComment&);
  HierarchicalComment& operator=(const HierarchicalComment&);
  HierarchicalComment(HierarchicalComment&&);
  HierarchicalComment& operator=(HierarchicalComment&&);

  Comment comment;               // The top-level comment
  std::vector<Comment> replies;  // Single-level replies
};

// Contains the results from an asynchronous Query API call.
struct QueryResult {
  QueryResult();
  ~QueryResult();
  QueryResult(const QueryResult&);
  QueryResult& operator=(const QueryResult&);
  QueryResult(QueryResult&&);
  QueryResult& operator=(QueryResult&&);

  std::vector<HierarchicalComment> comments;

  // Indicates whether there are more comments available beyond what was
  // returned in the current QueryResult. This is particularly useful when
  // `PaginationCriteria::max_total_results` > 0 and UI can show "load more"
  // based on this info.
  bool has_more_comments = false;
};

// Interface for clients (like the UI) to observe real-time changes to comments.
class CommentsObserver {
 public:
  virtual ~CommentsObserver() = default;

  // Called exactly once when the CommentsService has finished initializing
  // and is ready to be used. If the service has already initialized when the
  // observer is added, a call to OnServiceInitialized() will be posted
  // immediately.
  virtual void OnServiceInitialized() = 0;

  // Called when new comments are added. The `new_comments` vector is
  // topologically sorted, meaning a parent comment is guaranteed to appear
  // before any of its replies within the vector.
  virtual void OnCommentsAdded(const std::vector<Comment>& new_comments) = 0;

  // Called when any existing comments are modified and matches the filtering
  // criteria registered by the observer. `old_comments` is useful to determines
  // what has changed and efficiently update display.
  virtual void OnCommentsModified(const std::vector<Comment>& updated_comments,
                                  const std::vector<Comment>& old_comments) = 0;

  // Called when comments are deleted and match the filtering criteria
  // registered by the observer. The `deleted_comment_ids` vector contains the
  // IDs of comments that are no longer present.
  virtual void OnCommentsDeleted(
      const std::vector<CommentId>& deleted_comment_ids) = 0;

  // Called when the overall set of comments for a given collaboration/URL
  // might have changed significantly, requiring a full re-query by the UI.
  // This is a broad notification indicating that the UI should re-fetch all
  // relevant comments for its current view, rather than trying to reconcile
  // individual additions, modifications, or deletions.
  // Ex : User redirects to a different URL in the same tab:
  virtual void OnCommentsInvalidated() = 0;
};

// This interface serves as the primary interaction point for the UI to query
// for and manage comments. It provides a purely asynchronous API for all data
// access to ensure the UI thread is never blocked.
class CommentsService : public KeyedService, public base::SupportsUserData {
 public:
  ~CommentsService() override = default;

  // Returns whether the service has fully initialized.
  virtual bool IsInitialized() const = 0;

  // Returns true if this is an empty implementation.
  virtual bool IsEmptyService() const = 0;

  // Adds a new comment or, if `parent_comment_id` has a value, a reply.
  // Returns the new comment's ID synchronously. The success or failure of the
  // asynchronous save operation is reported via `success_callback`.
  virtual CommentId AddComment(
      const CollaborationId& collaboration_id,
      const GURL& url,
      const std::string& content,
      const std::optional<CommentId>& parent_comment_id,
      base::OnceCallback<void(bool)> success_callback) = 0;

  // Asynchronously edits the content of an existing comment identified by
  // `comment_id`. Reports success via `success_callback`.
  virtual void EditComment(const CommentId& comment_id,
                           const std::string& new_content,
                           base::OnceCallback<void(bool)> success_callback) = 0;

  // Asynchronously deletes a comment (and all of its replies) identified by
  // `comment_id`. Reports success via `success_callback`.
  virtual void DeleteComment(
      const CommentId& comment_id,
      base::OnceCallback<void(bool)> success_callback) = 0;

  // Asynchronously retrieves comments based on the given `filter_criteria` with
  // optional pagination. The results are returned via the `callback`.
  virtual void QueryComments(
      const FilterCriteria& filter_criteria,
      const PaginationCriteria& pagination_criteria,
      base::OnceCallback<void(QueryResult)> callback) = 0;

  // Adds an observer to be notified of real-time updates to comments that
  // match the given `filter_criteria`. An empty `filter_criteria` will result
  // in receiving all updates.
  virtual void AddObserver(CommentsObserver* observer,
                           const FilterCriteria& filter_criteria) = 0;

  // Removes a previously added observer.
  virtual void RemoveObserver(CommentsObserver* observer) = 0;
};

}  // namespace collaboration::comments

#endif  // COMPONENTS_COLLABORATION_PUBLIC_COMMENTS_COMMENTS_SERVICE_H_
