import { queryOneWithRLS, queryWithRLS, mutateWithRLS } from "../db.js";
class MediaService {
  // ============================================
  // QUERIES
  // ============================================
  /**
   * Get media item by ID (team-scoped)
   *
   * @param id - Media ID
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @returns Media or null if not found
   */
  static async getById(id, userId, teamId) {
    if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    return queryOneWithRLS(
      `SELECT * FROM "media" WHERE id = $1 AND "teamId" = $2 AND status = 'active'`,
      [id, teamId],
      userId
    );
  }
  /**
   * List media with pagination, filtering, and search (team-scoped)
   *
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @param options - List options (pagination, filtering, search, sort)
   * @returns Paginated media list result
   */
  static async list(userId, teamId, options = {}) {
    var _a;
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const {
      limit = 20,
      offset = 0,
      orderBy = "createdAt",
      orderDir = "desc",
      type = "all",
      search,
      status = "active",
      tagIds,
      tagSlugs
    } = options;
    const conditions = ["m.status = $1", 'm."teamId" = $2'];
    const params = [status, teamId];
    let paramIndex = 3;
    if (type === "image") {
      conditions.push(`m."mimeType" LIKE 'image/%'`);
    } else if (type === "video") {
      conditions.push(`m."mimeType" LIKE 'video/%'`);
    }
    if (search) {
      conditions.push(`(
        (m."searchVector" IS NOT NULL AND m."searchVector" @@ plainto_tsquery('english', $${paramIndex}))
        OR lower(coalesce(m.title, '')) LIKE $${paramIndex + 1}
        OR lower(m.filename) LIKE $${paramIndex + 1}
        OR lower(coalesce(m.alt, '')) LIKE $${paramIndex + 1}
        OR lower(coalesce(m.caption, '')) LIKE $${paramIndex + 1}
      )`);
      params.push(search);
      params.push(`%${search.toLowerCase()}%`);
      paramIndex += 2;
    }
    if (tagIds && tagIds.length > 0) {
      conditions.push(`m.id IN (
        SELECT etr."entityId" FROM "entity_taxonomy_relations" etr
        WHERE etr."entityType" = 'media' AND etr."taxonomyId" = ANY($${paramIndex})
      )`);
      params.push(tagIds);
      paramIndex++;
    }
    if (tagSlugs && tagSlugs.length > 0) {
      conditions.push(`m.id IN (
        SELECT etr."entityId" FROM "entity_taxonomy_relations" etr
        JOIN "taxonomies" t ON t.id = etr."taxonomyId"
        WHERE etr."entityType" = 'media' AND t.type = 'media_tag' AND t.slug = ANY($${paramIndex})
      )`);
      params.push(tagSlugs);
      paramIndex++;
    }
    const whereClause = conditions.join(" AND ");
    const validOrderBy = {
      createdAt: 'm."createdAt"',
      filename: "m.filename",
      fileSize: 'm."fileSize"'
    };
    const orderColumn = validOrderBy[orderBy] || 'm."createdAt"';
    const validDir = orderDir === "asc" ? "ASC" : "DESC";
    const countResult = await queryWithRLS(
      `SELECT COUNT(*)::text as count FROM "media" m WHERE ${whereClause}`,
      params,
      userId
    );
    const total = parseInt(((_a = countResult[0]) == null ? void 0 : _a.count) || "0", 10);
    params.push(limit, offset);
    const data = await queryWithRLS(
      `SELECT m.* FROM "media" m
       WHERE ${whereClause}
       ORDER BY ${orderColumn} ${validDir}
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params,
      userId
    );
    return { data, total, limit, offset };
  }
  /**
   * Find existing media by filename and fileSize (duplicate detection, team-scoped)
   *
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @param filename - Original filename
   * @param fileSize - File size in bytes
   * @returns Matching media items (same name+size = likely duplicate)
   */
  static async findDuplicates(userId, teamId, filename, fileSize) {
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    return queryWithRLS(
      `SELECT * FROM "media"
       WHERE filename = $1 AND "fileSize" = $2 AND "teamId" = $3 AND status = 'active'
       ORDER BY "createdAt" DESC
       LIMIT 5`,
      [filename, fileSize, teamId],
      userId
    );
  }
  // ============================================
  // MUTATIONS
  // ============================================
  /**
   * Create a new media record
   *
   * @param userId - User ID (uploader)
   * @param teamId - Team ID for isolation
   * @param data - Media data
   * @returns Created media record
   */
  static async create(userId, teamId, data) {
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const result = await mutateWithRLS(
      `INSERT INTO "media" (
        "userId", "teamId", url, filename, "fileSize", "mimeType",
        width, height, title, alt, caption, status
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 'active')
      RETURNING *`,
      [
        userId,
        teamId,
        data.url,
        data.filename,
        data.fileSize,
        data.mimeType,
        data.width ?? null,
        data.height ?? null,
        data.title ?? null,
        data.alt ?? null,
        data.caption ?? null
      ],
      userId
    );
    if (!result.rows[0]) throw new Error("Failed to create media record");
    return result.rows[0];
  }
  /**
   * Update media metadata (team-scoped)
   *
   * @param id - Media ID
   * @param userId - User ID for RLS context
   * @param data - Update data (title, alt, caption)
   * @param teamId - Team ID for isolation
   * @returns Updated media record or null if not found
   */
  static async update(id, userId, data, teamId) {
    if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const setClauses = [];
    const params = [];
    let paramIndex = 1;
    if (data.title !== void 0) {
      setClauses.push(`title = $${paramIndex++}`);
      params.push(data.title);
    }
    if (data.alt !== void 0) {
      setClauses.push(`alt = $${paramIndex++}`);
      params.push(data.alt);
    }
    if (data.caption !== void 0) {
      setClauses.push(`caption = $${paramIndex++}`);
      params.push(data.caption);
    }
    if (setClauses.length === 0) throw new Error("No fields to update");
    setClauses.push(`"updatedAt" = NOW()`);
    params.push(id);
    paramIndex++;
    params.push(teamId);
    const result = await mutateWithRLS(
      `UPDATE "media"
       SET ${setClauses.join(", ")}
       WHERE id = $${paramIndex - 1} AND status = 'active' AND "teamId" = $${paramIndex}
       RETURNING *`,
      params,
      userId
    );
    return result.rows[0] || null;
  }
  /**
   * Soft delete a media record (team-scoped)
   *
   * @param id - Media ID
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @returns True if deleted successfully
   */
  static async softDelete(id, userId, teamId) {
    if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const result = await mutateWithRLS(
      `UPDATE "media" SET status = 'deleted', "updatedAt" = NOW()
       WHERE id = $1 AND "teamId" = $2 AND status = 'active'`,
      [id, teamId],
      userId
    );
    return result.rowCount > 0;
  }
  // ============================================
  // TAG OPERATIONS (team-scoped)
  // ============================================
  /**
   * Get all media tags for a team
   *
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @returns Array of media tags scoped to the team
   */
  static async getTags(userId, teamId) {
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    return queryWithRLS(
      `SELECT id, type, slug, name, description, icon, color, "order", "isActive", "createdAt", "updatedAt"
       FROM "taxonomies"
       WHERE type = 'media_tag' AND "teamId" = $1 AND "isActive" = true AND "deletedAt" IS NULL
       ORDER BY "order" ASC, name ASC`,
      [teamId],
      userId
    );
  }
  /**
   * Get tags for a specific media item
   *
   * @param mediaId - Media item ID
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation (media ownership verified before calling)
   * @returns Array of tags assigned to the media
   */
  static async getMediaTags(mediaId, userId, teamId) {
    if (!(mediaId == null ? void 0 : mediaId.trim())) throw new Error("Media ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    return queryWithRLS(
      `SELECT t.id, t.type, t.slug, t.name, t.description, t.icon, t.color, t."order", t."isActive", t."createdAt", t."updatedAt"
       FROM "taxonomies" t
       JOIN "entity_taxonomy_relations" etr ON etr."taxonomyId" = t.id
       WHERE etr."entityType" = 'media' AND etr."entityId" = $1
         AND t."teamId" = $2
         AND t."isActive" = true AND t."deletedAt" IS NULL
       ORDER BY t."order" ASC, t.name ASC`,
      [mediaId, teamId],
      userId
    );
  }
  /**
   * Add a tag to a media item
   *
   * @param mediaId - Media item ID
   * @param tagId - Tag taxonomy ID
   * @param userId - User ID for RLS context
   * @returns True if tag was added
   */
  static async addTag(mediaId, tagId, userId) {
    if (!(mediaId == null ? void 0 : mediaId.trim())) throw new Error("Media ID is required");
    if (!(tagId == null ? void 0 : tagId.trim())) throw new Error("Tag ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    const result = await mutateWithRLS(
      `INSERT INTO "entity_taxonomy_relations" ("entityType", "entityId", "taxonomyId")
       VALUES ('media', $1, $2)
       ON CONFLICT ("entityType", "entityId", "taxonomyId") DO NOTHING`,
      [mediaId, tagId],
      userId
    );
    return result.rowCount > 0;
  }
  /**
   * Remove a tag from a media item
   *
   * @param mediaId - Media item ID
   * @param tagId - Tag taxonomy ID
   * @param userId - User ID for RLS context
   * @returns True if tag was removed
   */
  static async removeTag(mediaId, tagId, userId) {
    if (!(mediaId == null ? void 0 : mediaId.trim())) throw new Error("Media ID is required");
    if (!(tagId == null ? void 0 : tagId.trim())) throw new Error("Tag ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    const result = await mutateWithRLS(
      `DELETE FROM "entity_taxonomy_relations"
       WHERE "entityType" = 'media' AND "entityId" = $1 AND "taxonomyId" = $2`,
      [mediaId, tagId],
      userId
    );
    return result.rowCount > 0;
  }
  /**
   * Set tags for a media item (replace all existing tags)
   *
   * @param mediaId - Media item ID
   * @param tagIds - Array of tag taxonomy IDs
   * @param userId - User ID for RLS context
   */
  static async setTags(mediaId, tagIds, userId) {
    if (!(mediaId == null ? void 0 : mediaId.trim())) throw new Error("Media ID is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    await mutateWithRLS(
      `DELETE FROM "entity_taxonomy_relations"
       WHERE "entityType" = 'media' AND "entityId" = $1`,
      [mediaId],
      userId
    );
    if (tagIds.length > 0) {
      const values = tagIds.map((_, i) => `('media', $1, $${i + 2})`).join(", ");
      await mutateWithRLS(
        `INSERT INTO "entity_taxonomy_relations" ("entityType", "entityId", "taxonomyId")
         VALUES ${values}
         ON CONFLICT DO NOTHING`,
        [mediaId, ...tagIds],
        userId
      );
    }
  }
  /**
   * Create a new media tag (team-scoped)
   *
   * @param name - Tag display name
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @returns The created tag
   */
  static async createTag(name, userId, teamId) {
    if (!(name == null ? void 0 : name.trim())) throw new Error("Tag name is required");
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
    const result = await mutateWithRLS(
      `INSERT INTO "taxonomies" (type, slug, name, "teamId", "isActive")
       VALUES ('media_tag', $1, $2, $3, true)
       ON CONFLICT (type, slug, "teamId") WHERE "teamId" IS NOT NULL
       DO UPDATE SET name = EXCLUDED.name
       RETURNING id, slug, name, color, icon, "order", "isActive"`,
      [slug, name.trim(), teamId],
      userId
    );
    return result.rows[0];
  }
  /**
   * Count media items (team-scoped)
   *
   * @param userId - User ID for RLS context
   * @param teamId - Team ID for isolation
   * @param options - Count options (type filter, status filter)
   * @returns Total count
   */
  static async count(userId, teamId, options = {}) {
    var _a;
    if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
    if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
    const { type = "all", status = "active" } = options;
    const conditions = ["status = $1", '"teamId" = $2'];
    const params = [status, teamId];
    if (type === "image") conditions.push(`"mimeType" LIKE 'image/%'`);
    else if (type === "video") conditions.push(`"mimeType" LIKE 'video/%'`);
    const result = await queryWithRLS(
      `SELECT COUNT(*)::text as count FROM "media" WHERE ${conditions.join(" AND ")}`,
      params,
      userId
    );
    return parseInt(((_a = result[0]) == null ? void 0 : _a.count) || "0", 10);
  }
}
export {
  MediaService
};
