import AssignTagAPIService from "../../API/Tag/AssignTagAPIService.js";
import UnassignTagAPIService from "../../API/Tag/UnassignTagAPIService.js";
import ListCollectionsByTagAPIService from "../../API/Tag/ListCollectionsByTagAPIService.js";

/**
 * CollectionTagManager
 *
 * Orchestrates tag assignment and filtering for collections.
 * Handles API calls and event dispatching for tag operations.
 */
class CollectionTagManager {
  constructor(authManager, getCollectionManager, listCollectionManager) {
    this.authManager = authManager;
    this.getCollectionManager = getCollectionManager;
    this.listCollectionManager = listCollectionManager;

    // Initialize API services
    this.assignTagAPI = new AssignTagAPIService(authManager);
    this.unassignTagAPI = new UnassignTagAPIService(authManager);
    this.listByTagAPI = new ListCollectionsByTagAPIService(authManager);
  }

  /**
   * Assign tags to a collection
   *
   * @param {string} collectionId - UUID of the collection
   * @param {Array<string>} tagIds - Array of tag UUIDs to assign
   * @returns {Promise<void>}
   */
  async assignTagsToCollection(collectionId, tagIds) {
    try {
      console.log("[CollectionTagManager] Assigning tags to collection", {
        collectionId,
        tagCount: tagIds.length
      });

      // Assign each tag
      for (const tagId of tagIds) {
        await this.assignTagAPI.assignTag(tagId, {
          entity_id: collectionId,
          entity_type: "collection"
        });
      }

      // Clear relevant caches
      this.getCollectionManager?.clearAllCache();
      this.listCollectionManager?.clearAllCache();

      // Dispatch event
      window.dispatchEvent(new CustomEvent('collectionTagsAssigned', {
        detail: { collectionId, tagIds }
      }));

      console.log("[CollectionTagManager] Tags assigned successfully");
    } catch (error) {
      console.error("[CollectionTagManager] Failed to assign tags:", error);
      throw error;
    }
  }

  /**
   * Unassign tags from a collection
   *
   * @param {string} collectionId - UUID of the collection
   * @param {Array<string>} tagIds - Array of tag UUIDs to unassign
   * @returns {Promise<void>}
   */
  async unassignTagsFromCollection(collectionId, tagIds) {
    try {
      console.log("[CollectionTagManager] Unassigning tags from collection", {
        collectionId,
        tagCount: tagIds.length
      });

      // Unassign each tag
      for (const tagId of tagIds) {
        await this.unassignTagAPI.unassignTag(tagId, collectionId, "collection");
      }

      // Clear relevant caches
      this.getCollectionManager?.clearAllCache();
      this.listCollectionManager?.clearAllCache();

      // Dispatch event
      window.dispatchEvent(new CustomEvent('collectionTagsUnassigned', {
        detail: { collectionId, tagIds }
      }));

      console.log("[CollectionTagManager] Tags unassigned successfully");
    } catch (error) {
      console.error("[CollectionTagManager] Failed to unassign tags:", error);
      throw error;
    }
  }

  /**
   * Update collection tags (replace all tags)
   *
   * @param {string} collectionId - UUID of the collection
   * @param {Array<string>} currentTagIds - Current tag IDs on the collection
   * @param {Array<string>} newTagIds - New tag IDs to set
   * @returns {Promise<void>}
   */
  async updateCollectionTags(collectionId, currentTagIds = [], newTagIds = []) {
    try {
      console.log("[CollectionTagManager] Updating collection tags", {
        collectionId,
        currentCount: currentTagIds.length,
        newCount: newTagIds.length
      });

      // Determine which tags to add and remove
      const tagsToAdd = newTagIds.filter(id => !currentTagIds.includes(id));
      const tagsToRemove = currentTagIds.filter(id => !newTagIds.includes(id));

      // Assign new tags
      if (tagsToAdd.length > 0) {
        await this.assignTagsToCollection(collectionId, tagsToAdd);
      }

      // Unassign removed tags
      if (tagsToRemove.length > 0) {
        await this.unassignTagsFromCollection(collectionId, tagsToRemove);
      }

      console.log("[CollectionTagManager] Collection tags updated successfully", {
        added: tagsToAdd.length,
        removed: tagsToRemove.length
      });
    } catch (error) {
      console.error("[CollectionTagManager] Failed to update collection tags:", error);
      throw error;
    }
  }

  /**
   * Get collections by tags (AND logic - collections must have ALL tags)
   *
   * @param {Array<string>} tagIds - Array of tag UUIDs
   * @param {Object} options - Query options (limit, cursor)
   * @returns {Promise<Object>} - Response with collections, cursor, has_more
   */
  async getCollectionsByTags(tagIds, options = {}) {
    try {
      console.log("[CollectionTagManager] Fetching collections by tags", {
        tagCount: tagIds.length,
        options
      });

      const response = await this.listByTagAPI.getCollectionsByTags(tagIds, options);

      // Note: Collections come back encrypted from API
      // They will need to be decrypted by GetCollectionManager if needed

      return response;
    } catch (error) {
      console.error("[CollectionTagManager] Failed to get collections by tags:", error);
      throw error;
    }
  }
}

export default CollectionTagManager;
