/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.com/legal/licensing"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.community.repository;

import org.silverpeas.components.community.model.CommunityMembership;
import org.silverpeas.components.community.model.CommunityOfUsers;
import org.silverpeas.kernel.exception.NotSupportedException;
import org.silverpeas.core.admin.PaginationPage;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.persistence.datasource.repository.EntityRepository;
import org.silverpeas.core.util.ServiceProvider;
import org.silverpeas.core.util.SilverpeasList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
 * A repository to persist the memberships to a community of users. This repository doesn't support
 * the deletion of the entities in the repository neither the getting of all the entities. When a
 * member is removed from a community, only its status is changed: he passes then from the
 * {@link org.silverpeas.components.community.model.MembershipStatus#COMMITTED} status to the
 * {@link org.silverpeas.components.community.model.MembershipStatus#REMOVED} one; this is for
 * keeping the history of the memberships of a community. Members of a community is really deleted
 * only when their community is also deleted.
 * @author mmoquillon
 */
public interface CommunityMembershipRepository extends EntityRepository<CommunityMembership> {

  /**
   * The table of memberships of a given community of users. This table gathers all the memberships
   * of the users to the given community; it includes both the removed memberships, the pending ones
   * and the committed ones. In fact, when a member is removed from a community, his membership in
   * the table is never deleted but just updated with the status
   * {@link org.silverpeas.components.community.model.MembershipStatus#REMOVED}.
   */
  interface CommunityMembershipsTable {

    /**
     * Deletes all the memberships in this table. This method has to be invoked only when the
     * community is being itself deleted. This method should be invoked only by the
     * {@link org.silverpeas.components.community.CommunityInstancePreDestruction} bean.
     */
    void deleteAll();

    /**
     * Gets the membership to the  community of the specified user.  Only committed or pending
     * membership is considered.
     * @param user a user.
     * @return either a {@link CommunityMembership} instance related to the specified user or
     * nothing if the user isn't (anymore) member of the community of users.
     */
    Optional<CommunityMembership> getByUser(final User user);

    /**
     * Gets the membership that are pending and included in the specified pagination page. If the
     * page is null then all the pending memberships are returned.
     * @param page the page from which the pending membership are got or null to get all the pending
     * memberships from this table.
     * @return a paginated list of pending memberships to the community if users.
     */
    SilverpeasList<CommunityMembership> getPending(@Nullable final PaginationPage page);

    /**
     * Gets the committed memberships of the specified community included in the specified
     * pagination page.
     * @param page the page from which the memberships are got.
     * @return a paginated list of committed memberships to the community of users.
     */
    SilverpeasList<CommunityMembership> getMembers(@Nonnull final PaginationPage page);

    /**
     * Gets all the memberships registered into this table and included in the specified pagination
     * page. All the memberships are taken, whatever their status, and hence even those being
     * pending, removed or refused.
     * @param page the page from which the memberships are got.
     * @return a paginated list of memberships to the community of users.
     */
    SilverpeasList<CommunityMembership> getAll(@Nonnull final PaginationPage page);

    /**
     * Gets all the committed or pending memberships to the community of users. This method is
     * mainly to be used for synchronization as no pagination is performed.
     * @return a list of actual memberships of the community.
     */
    List<CommunityMembership> getAllMembers();

    /**
     * Is this table is empty?
     * @return true if the community of users hasn't yet any memberships registered. False
     * otherwise.
     */
    boolean isEmpty();

  }

  /**
   * Gets the single instance of this repository.
   */
  static CommunityMembershipRepository get() {
    return ServiceProvider.getService(CommunityMembershipRepository.class);
  }

  /**
   * Gets the table of members of the specified community.
   * @param community a community of users.
   * @return a {@link CommunityMembershipsTable} instance through which the members of the specified
   * community can be requested.
   */
  CommunityMembershipsTable getMembershipsTable(final CommunityOfUsers community);

  /**
   * Deletes all the members of the community of users managed by the specified component instance.
   * The method has to be invoked only when the given component instance, and thus the community
   * managed of users by it, is being deleted.
   * @param componentInstanceId the unique component instance identifier.
   * @return the number of deleted members.
   * @see CommunityMembershipRepository.CommunityMembershipsTable#deleteAll()
   */
  @Override
  long deleteByComponentInstanceId(final String componentInstanceId);

  /**
   * @param entity the entity/entities to delete.
   * @apiNote Not supported
   */
  @Override
  default void delete(CommunityMembership... entity) {
    delete(Arrays.asList(entity));
  }

  /**
   * @param entities the entities to delete.
   * @apiNote Not supported
   */
  @Override
  default void delete(List<CommunityMembership> entities) {
    throw new NotSupportedException("Deletion of members of a community isn't supported");
  }

  /**
   * @param ids the identifiers of the entities to delete.
   * @return nothing
   * @apiNote Not supported
   */
  @Override
  default long deleteById(final String... ids) {
    return deleteById(Arrays.asList(ids));
  }

  /**
   * @param ids the identifiers of the entities to delete.
   * @return nothing
   * @apiNote Not supported
   */
  @Override
  default long deleteById(final Collection<String> ids) {
    throw new NotSupportedException("Deletion of members of a community isn't supported");
  }

  /**
   * @return nothing
   * @apiNote Not supported
   */
  @Override
  default SilverpeasList<CommunityMembership> getAll() {
    throw new NotSupportedException(
        "Getting all members of all community isn't supported for secure and performance reasons");
  }
}
