// File: monorepo/web/maplefile-frontend/src/services/Manager/Tag/TagManager.js
// Unified Tag Manager - Handles all tag operations with E2EE

import CreateTagAPIService from "../../API/Tag/CreateTagAPIService.js";
import ListTagsAPIService from "../../API/Tag/ListTagsAPIService.js";
import UpdateTagAPIService from "../../API/Tag/UpdateTagAPIService.js";
import DeleteTagAPIService from "../../API/Tag/DeleteTagAPIService.js";
import SearchByTagsAPIService from "../../API/Tag/SearchByTagsAPIService.js";
import TagCryptoService from "../../Crypto/TagCryptoService.js";

class TagManager {
  constructor(authManager) {
    this.authManager = authManager;
    this.isLoading = false;

    // Initialize dependent services
    this.createAPIService = new CreateTagAPIService(authManager);
    this.listAPIService = new ListTagsAPIService(authManager);
    this.updateAPIService = new UpdateTagAPIService(authManager);
    this.deleteAPIService = new DeleteTagAPIService(authManager);
    this.searchAPIService = new SearchByTagsAPIService(authManager);
    this.cryptoService = TagCryptoService; // Use singleton

    // Cache for decrypted tags
    this._tagCache = new Map();

    console.log("[TagManager] Tag manager initialized");
  }

  // Initialize the manager
  async initialize() {
    try {
      console.log("[TagManager] Initializing tag manager...");
      await this.cryptoService.initialize();
      console.log("[TagManager] Tag manager initialized successfully");
    } catch (error) {
      console.error("[TagManager] Failed to initialize tag manager:", error);
    }
  }

  // Get user password
  async getUserPassword() {
    try {
      const { default: passwordStorageService } = await import(
        "../../PasswordStorageService.js"
      );
      const password = passwordStorageService.getPassword();

      if (!password) {
        throw new Error(
          "No password available. Please log in again.",
        );
      }

      return password;
    } catch (error) {
      console.error("[TagManager] Failed to get password:", error);
      throw error;
    }
  }

  // Create tag with E2EE
  async createTag(tagData) {
    try {
      this.isLoading = true;
      console.log("[TagManager] Starting tag creation");

      // Use TagCryptoService.encryptTag() which handles everything
      const encryptedTagData = await this.cryptoService.encryptTag(tagData);

      // Generate UUID for tag
      const tagId = this.cryptoService.cryptoService.generateUUID();

      // Prepare API data (user_id is extracted from JWT by backend)
      const apiData = {
        id: tagId,
        encrypted_name: encryptedTagData.encrypted_name,
        encrypted_color: encryptedTagData.encrypted_color,
        encrypted_tag_key: encryptedTagData.encrypted_tag_key,
        created_at: new Date().toISOString(),
        modified_at: new Date().toISOString(),
        version: 1,
        state: "active",
      };

      // Create via API
      const response = await this.createAPIService.createTag(apiData);

      // Clear cache
      this.clearCache();

      // Dispatch event
      if (typeof window !== "undefined") {
        window.dispatchEvent(
          new CustomEvent("tagCreated", {
            detail: { tagId, timestamp: Date.now() },
          }),
        );
      }

      console.log("[TagManager] Tag created successfully");
      return response;
    } catch (error) {
      console.error("[TagManager] Tag creation failed:", error);
      throw error;
    } finally {
      this.isLoading = false;
    }
  }

  // List tags with decryption
  async listTags() {
    try {
      this.isLoading = true;
      console.log("[TagManager] Fetching tags");

      // Fetch from API
      const encryptedTags = await this.listAPIService.listTags();

      if (!encryptedTags || encryptedTags.length === 0) {
        return [];
      }

      // Decrypt tags (TagCryptoService.decryptTag handles getting user keys)
      const decryptedTags = await Promise.all(
        encryptedTags.map(async (tag) => {
          try {
            return await this.cryptoService.decryptTag(tag);
          } catch (error) {
            console.error(`Failed to decrypt tag ${tag.id}:`, error);
            return null;
          }
        }),
      );

      // Filter out failed decryptions
      const validTags = decryptedTags.filter((tag) => tag !== null);

      console.log("[TagManager] Tags fetched:", validTags.length);
      return validTags;
    } catch (error) {
      console.error("[TagManager] Failed to list tags:", error);
      throw error;
    } finally {
      this.isLoading = false;
    }
  }

  // Update tag with E2EE (matching Collection pattern)
  async updateTag(tagId, tagData) {
    try {
      this.isLoading = true;
      console.log("[TagManager] Updating tag:", tagId);

      // First, get the current tag to retrieve the encrypted_tag_key
      const currentTags = await this.listAPIService.listTags();
      const currentTag = currentTags.find((t) => t.id === tagId);

      if (!currentTag || !currentTag.encrypted_tag_key) {
        throw new Error(
          "Cannot update tag: Current tag or encrypted tag key not found.",
        );
      }

      // Prepare API update data - always include current encrypted_tag_key (backend requirement)
      const apiUpdateData = {
        id: tagId,
        version: currentTag.version,
        encrypted_tag_key: currentTag.encrypted_tag_key, // Always required by backend
        created_at: currentTag.created_at, // Required by backend
        state: currentTag.state || "active", // Required by backend
      };

      // Get cached tag key or decrypt it from current tag
      let tagKey = this.cryptoService.getCachedTagKey(tagId);

      if (!tagKey) {
        // Decrypt tag key from current tag data
        const userKeys = await this.cryptoService.getUserKeys();
        tagKey = await this.cryptoService.decryptTagKey(
          currentTag.encrypted_tag_key,
          userKeys.masterKey,
        );
        this.cryptoService.cacheTagKey(tagId, tagKey);
      }

      // Encrypt new name with existing tag key
      if (tagData.name && tagData.name.trim()) {
        console.log("[TagManager] Encrypting new tag name");
        const encryptedName = this.cryptoService.encryptTagName(
          tagData.name.trim(),
          tagKey,
        );
        apiUpdateData.encrypted_name = `${encryptedName.ciphertext}:${encryptedName.nonce}`;
        console.log("[TagManager] Tag name encrypted");
      }

      // Encrypt new color with existing tag key
      if (tagData.color && tagData.color.trim()) {
        console.log("[TagManager] Encrypting new tag color");
        const encryptedColor = this.cryptoService.encryptTagColor(
          tagData.color.trim(),
          tagKey,
        );
        apiUpdateData.encrypted_color = `${encryptedColor.ciphertext}:${encryptedColor.nonce}`;
        console.log("[TagManager] Tag color encrypted");
      }

      // Update modified timestamp
      apiUpdateData.modified_at = new Date().toISOString();

      console.log("[TagManager] Sending update to API");

      // Update via API
      const response = await this.updateAPIService.updateTag(tagId, apiUpdateData);

      // Clear cache
      this.clearCache();

      // Dispatch event
      if (typeof window !== "undefined") {
        window.dispatchEvent(
          new CustomEvent("tagUpdated", {
            detail: { tagId, timestamp: Date.now() },
          }),
        );
      }

      console.log("[TagManager] Tag updated successfully");
      return response;
    } catch (error) {
      console.error("[TagManager] Tag update failed:", error);
      throw error;
    } finally {
      this.isLoading = false;
    }
  }

  // Delete tag
  async deleteTag(tagId) {
    try {
      this.isLoading = true;
      console.log("[TagManager] Deleting tag:", tagId);

      // Delete via API
      await this.deleteAPIService.deleteTag(tagId);

      // Clear cache
      this.clearCache();
      this.cryptoService.clearTagKeyCache(tagId);

      // Dispatch event
      if (typeof window !== "undefined") {
        window.dispatchEvent(
          new CustomEvent("tagDeleted", {
            detail: { tagId, timestamp: Date.now() },
          }),
        );
      }

      console.log("[TagManager] Tag deleted successfully");
    } catch (error) {
      console.error("[TagManager] Tag deletion failed:", error);
      throw error;
    } finally {
      this.isLoading = false;
    }
  }

  // Search by tags (multi-tag search with AND logic)
  async searchByTags(tagIds, limit = 50) {
    try {
      this.isLoading = true;
      console.log("[TagManager] Searching by tags:", tagIds);

      // Call search API
      const results = await this.searchAPIService.searchByTags(tagIds, limit);

      console.log(
        "[TagManager] Search completed:",
        results.collectionCount,
        "collections,",
        results.fileCount,
        "files",
      );

      return results;
    } catch (error) {
      console.error("[TagManager] Tag search failed:", error);
      throw error;
    } finally {
      this.isLoading = false;
    }
  }

  // Clear cache
  clearCache() {
    this._tagCache.clear();
    console.log("[TagManager] Tag cache cleared");
  }
}

export default TagManager;
