import { queryOneWithRLS, queryWithRLS, mutateWithRLS } from "../../lib/db.js";
import { isPatternReference } from "./patterns.types.js";
function containsPatternReference(blocks) {
  if (!Array.isArray(blocks)) return false;
  return blocks.some((block) => isPatternReference(block));
}
class PatternsService {
  // ============================================
  // READ OPERATIONS
  // ============================================
  /**
   * Get a pattern by ID
   *
   * Respects RLS policies. Since patterns has shared: true,
   * all team members can access patterns from their team.
   *
   * @param id - Pattern ID
   * @param userId - Current user ID for RLS
   * @returns Pattern data or null if not found/not authorized
   *
   * @example
   * const pattern = await PatternsService.getById('pattern-uuid', currentUserId)
   */
  static async getById(id, userId) {
    try {
      if (!(id == null ? void 0 : id.trim())) {
        throw new Error("Pattern ID is required");
      }
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required for authentication");
      }
      const pattern = await queryOneWithRLS(
        `
        SELECT
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        FROM "patterns"
        WHERE id = $1
        `,
        [id],
        userId
      );
      if (!pattern) {
        return null;
      }
      return {
        id: pattern.id,
        userId: pattern.userId,
        teamId: pattern.teamId,
        title: pattern.title,
        slug: pattern.slug,
        blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
        status: pattern.status,
        description: pattern.description ?? void 0,
        createdAt: pattern.createdAt,
        updatedAt: pattern.updatedAt
      };
    } catch (error) {
      console.error("[PatternsService.getById] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to fetch pattern"
      );
    }
  }
  /**
   * Get a pattern by slug and team ID
   *
   * @param slug - Pattern slug
   * @param teamId - Team ID
   * @param userId - Current user ID for RLS
   * @returns Pattern data or null if not found
   *
   * @example
   * const pattern = await PatternsService.getBySlug('newsletter-cta', teamId, userId)
   */
  static async getBySlug(slug, teamId, userId) {
    try {
      if (!(slug == null ? void 0 : slug.trim())) {
        throw new Error("Slug is required");
      }
      if (!(teamId == null ? void 0 : teamId.trim())) {
        throw new Error("Team ID is required");
      }
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required for authentication");
      }
      const pattern = await queryOneWithRLS(
        `
        SELECT
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        FROM "patterns"
        WHERE slug = $1 AND "teamId" = $2
        `,
        [slug, teamId],
        userId
      );
      if (!pattern) {
        return null;
      }
      return {
        id: pattern.id,
        userId: pattern.userId,
        teamId: pattern.teamId,
        title: pattern.title,
        slug: pattern.slug,
        blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
        status: pattern.status,
        description: pattern.description ?? void 0,
        createdAt: pattern.createdAt,
        updatedAt: pattern.updatedAt
      };
    } catch (error) {
      console.error("[PatternsService.getBySlug] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to fetch pattern by slug"
      );
    }
  }
  /**
   * List patterns with pagination and filtering
   *
   * @param userId - Current user ID for RLS
   * @param teamId - Team ID
   * @param options - List options (limit, offset, status, orderBy, orderDir)
   * @returns Object with patterns array and total count
   *
   * @example
   * const { data, total } = await PatternsService.list(userId, teamId, {
   *   status: 'published',
   *   limit: 10
   * })
   */
  static async list(userId, teamId, options = {}) {
    var _a;
    try {
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required for authentication");
      }
      if (!(teamId == null ? void 0 : teamId.trim())) {
        throw new Error("Team ID is required");
      }
      const {
        limit = 20,
        offset = 0,
        status,
        orderBy = "createdAt",
        orderDir = "desc"
      } = options;
      const conditions = ['"teamId" = $1'];
      const params = [teamId];
      let paramIndex = 2;
      if (status) {
        conditions.push(`status = $${paramIndex}`);
        params.push(status);
        paramIndex++;
      }
      const whereClause = `WHERE ${conditions.join(" AND ")}`;
      const validOrderBy = ["title", "slug", "status", "createdAt", "updatedAt"].includes(orderBy) ? orderBy : "createdAt";
      const validOrderDir = orderDir === "asc" ? "ASC" : "DESC";
      const orderColumnMap = {
        title: "title",
        slug: "slug",
        status: "status",
        createdAt: '"createdAt"',
        updatedAt: '"updatedAt"'
      };
      const orderColumn = orderColumnMap[validOrderBy] || '"createdAt"';
      const countResult = await queryWithRLS(
        `SELECT COUNT(*)::text as count FROM "patterns" ${whereClause}`,
        params,
        userId
      );
      const total = parseInt(((_a = countResult[0]) == null ? void 0 : _a.count) || "0", 10);
      params.push(limit, offset);
      const patterns = await queryWithRLS(
        `
        SELECT
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        FROM "patterns"
        ${whereClause}
        ORDER BY ${orderColumn} ${validOrderDir}
        LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
        `,
        params,
        userId
      );
      return {
        data: patterns.map((pattern) => ({
          id: pattern.id,
          userId: pattern.userId,
          teamId: pattern.teamId,
          title: pattern.title,
          slug: pattern.slug,
          blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
          status: pattern.status,
          description: pattern.description ?? void 0,
          createdAt: pattern.createdAt,
          updatedAt: pattern.updatedAt
        })),
        total
      };
    } catch (error) {
      console.error("[PatternsService.list] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to list patterns"
      );
    }
  }
  /**
   * List only published patterns for a team
   *
   * Convenience method to fetch all published patterns.
   * Used by the block picker to show available patterns for insertion.
   *
   * @param userId - Current user ID for RLS
   * @param teamId - Team ID
   * @returns Array of published patterns
   *
   * @example
   * const publishedPatterns = await PatternsService.listPublished(userId, teamId)
   */
  static async listPublished(userId, teamId) {
    try {
      const { data } = await this.list(userId, teamId, {
        status: "published",
        limit: 1e3,
        // Large limit to get all published patterns
        orderBy: "title",
        orderDir: "asc"
      });
      return data;
    } catch (error) {
      console.error("[PatternsService.listPublished] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to fetch published patterns"
      );
    }
  }
  /**
   * Get multiple patterns by IDs (batch fetch)
   *
   * Used for resolving pattern references in pages.
   * Efficiently fetches all referenced patterns in a single query.
   *
   * @param ids - Array of pattern IDs
   * @param userId - Current user ID for RLS
   * @returns Array of patterns (may be fewer than ids if some not found)
   *
   * @example
   * const patternRefs = [{ ref: 'uuid-1' }, { ref: 'uuid-2' }]
   * const patterns = await PatternsService.getByIds(
   *   patternRefs.map(p => p.ref),
   *   userId
   * )
   */
  static async getByIds(ids, userId) {
    try {
      if (!Array.isArray(ids) || ids.length === 0) {
        return [];
      }
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required for authentication");
      }
      const validIds = ids.filter((id) => id && id.trim());
      if (validIds.length === 0) {
        return [];
      }
      const placeholders = validIds.map((_, i) => `$${i + 1}`).join(", ");
      const patterns = await queryWithRLS(
        `
        SELECT
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        FROM "patterns"
        WHERE id IN (${placeholders})
        `,
        validIds,
        userId
      );
      return patterns.map((pattern) => ({
        id: pattern.id,
        userId: pattern.userId,
        teamId: pattern.teamId,
        title: pattern.title,
        slug: pattern.slug,
        blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
        status: pattern.status,
        description: pattern.description ?? void 0,
        createdAt: pattern.createdAt,
        updatedAt: pattern.updatedAt
      }));
    } catch (error) {
      console.error("[PatternsService.getByIds] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to fetch patterns by IDs"
      );
    }
  }
  // ============================================
  // WRITE OPERATIONS
  // ============================================
  /**
   * Create a new pattern
   *
   * @param userId - Current user ID (will be pattern owner)
   * @param teamId - Team ID (pattern belongs to team)
   * @param data - Pattern data
   * @returns Created pattern
   *
   * @example
   * const pattern = await PatternsService.create(userId, teamId, {
   *   title: 'Newsletter CTA',
   *   slug: 'newsletter-cta',
   *   blocks: [...],
   *   status: 'draft'
   * })
   */
  static async create(userId, teamId, data) {
    var _a, _b;
    try {
      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");
      }
      if (!((_a = data.title) == null ? void 0 : _a.trim())) {
        throw new Error("Title is required");
      }
      if (!((_b = data.slug) == null ? void 0 : _b.trim())) {
        throw new Error("Slug is required");
      }
      if (data.blocks && containsPatternReference(data.blocks)) {
        throw new Error("Patterns cannot contain other patterns");
      }
      const result = await mutateWithRLS(
        `
        INSERT INTO "patterns" (
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description
        )
        VALUES ($1, $2, $3, $4, $5, $6, $7)
        RETURNING
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        `,
        [
          userId,
          teamId,
          data.title,
          data.slug,
          JSON.stringify(data.blocks || []),
          data.status || "draft",
          data.description || null
        ],
        userId
      );
      if (!result || result.rows.length === 0) {
        throw new Error("Failed to create pattern");
      }
      const pattern = result.rows[0];
      return {
        id: pattern.id,
        userId: pattern.userId,
        teamId: pattern.teamId,
        title: pattern.title,
        slug: pattern.slug,
        blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
        status: pattern.status,
        description: pattern.description ?? void 0,
        createdAt: pattern.createdAt,
        updatedAt: pattern.updatedAt
      };
    } catch (error) {
      console.error("[PatternsService.create] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to create pattern"
      );
    }
  }
  /**
   * Update an existing pattern
   *
   * @param id - Pattern ID
   * @param userId - Current user ID for RLS
   * @param data - Pattern data to update
   * @returns Updated pattern
   *
   * @example
   * const pattern = await PatternsService.update('pattern-id', userId, {
   *   title: 'Updated Title',
   *   status: 'published'
   * })
   */
  static async update(id, userId, data) {
    try {
      if (!(id == null ? void 0 : id.trim())) {
        throw new Error("Pattern ID is required");
      }
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required");
      }
      if (data.blocks !== void 0 && containsPatternReference(data.blocks)) {
        throw new Error("Patterns cannot contain other patterns");
      }
      const updates = [];
      const params = [];
      let paramIndex = 1;
      if (data.title !== void 0) {
        updates.push(`title = $${paramIndex}`);
        params.push(data.title);
        paramIndex++;
      }
      if (data.slug !== void 0) {
        updates.push(`slug = $${paramIndex}`);
        params.push(data.slug);
        paramIndex++;
      }
      if (data.blocks !== void 0) {
        updates.push(`blocks = $${paramIndex}`);
        params.push(JSON.stringify(data.blocks));
        paramIndex++;
      }
      if (data.status !== void 0) {
        updates.push(`status = $${paramIndex}`);
        params.push(data.status);
        paramIndex++;
      }
      if (data.description !== void 0) {
        updates.push(`description = $${paramIndex}`);
        params.push(data.description || null);
        paramIndex++;
      }
      if (updates.length === 0) {
        throw new Error("No fields to update");
      }
      params.push(id);
      const result = await mutateWithRLS(
        `
        UPDATE "patterns"
        SET ${updates.join(", ")}
        WHERE id = $${paramIndex}
        RETURNING
          id,
          "userId",
          "teamId",
          title,
          slug,
          blocks,
          status,
          description,
          "createdAt",
          "updatedAt"
        `,
        params,
        userId
      );
      if (!result || result.rows.length === 0) {
        throw new Error("Pattern not found or not authorized");
      }
      const pattern = result.rows[0];
      return {
        id: pattern.id,
        userId: pattern.userId,
        teamId: pattern.teamId,
        title: pattern.title,
        slug: pattern.slug,
        blocks: Array.isArray(pattern.blocks) ? pattern.blocks : [],
        status: pattern.status,
        description: pattern.description ?? void 0,
        createdAt: pattern.createdAt,
        updatedAt: pattern.updatedAt
      };
    } catch (error) {
      console.error("[PatternsService.update] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to update pattern"
      );
    }
  }
  /**
   * Delete a pattern
   *
   * Uses "Lazy Cleanup" strategy: patterns can be deleted even if in use.
   * - ON DELETE CASCADE cleans up pattern_usages
   * - Orphaned PatternReferences in entities are filtered out on next save
   * - Public rendering gracefully skips missing patterns
   *
   * @param id - Pattern ID
   * @param userId - Current user ID for RLS
   * @returns True if deleted successfully
   *
   * @example
   * await PatternsService.delete('pattern-id', userId)
   */
  static async delete(id, userId) {
    try {
      if (!(id == null ? void 0 : id.trim())) {
        throw new Error("Pattern ID is required");
      }
      if (!(userId == null ? void 0 : userId.trim())) {
        throw new Error("User ID is required");
      }
      try {
        const { PatternUsageService } = await import("../../lib/services/pattern-usage.service.js");
        const usageCount = await PatternUsageService.getUsageCount(id, userId);
        if (usageCount > 0) {
          console.info(
            `[PatternsService.delete] Deleting pattern ${id} with ${usageCount} active usages. References will be cleaned up on next entity save.`
          );
        }
      } catch {
      }
      const result = await mutateWithRLS(
        `
        DELETE FROM "patterns"
        WHERE id = $1
        RETURNING id
        `,
        [id],
        userId
      );
      return result && result.rows.length > 0;
    } catch (error) {
      console.error("[PatternsService.delete] Error:", error);
      throw new Error(
        error instanceof Error ? error.message : "Failed to delete pattern"
      );
    }
  }
}
export {
  PatternsService
};
