/**
 * SQL query constants for Item operations.
 * Centralizes all item-related queries to avoid duplication.
 */
export class ItemQueries {
  /**
   * Base SELECT for items with common fields.
   * Includes LEFT JOIN to Logos. Folder paths are computed in the repository layer.
   */
  public static readonly BASE_SELECT = `
    SELECT DISTINCT
      i.Id,
      i.Name,
      i.ItemType,
      i.FolderId,
      l.FileData as Logo,
      CASE WHEN EXISTS (SELECT 1 FROM Passkeys pk WHERE pk.ItemId = i.Id AND pk.IsDeleted = 0) THEN 1 ELSE 0 END as HasPasskey,
      CASE WHEN EXISTS (SELECT 1 FROM Attachments att WHERE att.ItemId = i.Id AND att.IsDeleted = 0) THEN 1 ELSE 0 END as HasAttachment,
      CASE WHEN EXISTS (SELECT 1 FROM TotpCodes tc WHERE tc.ItemId = i.Id AND tc.IsDeleted = 0) THEN 1 ELSE 0 END as HasTotp,
      i.CreatedAt,
      i.UpdatedAt
    FROM Items i
    LEFT JOIN Logos l ON i.LogoId = l.Id`;

  /**
   * Get all active items (not deleted, not in trash).
   */
  public static readonly GET_ALL_ACTIVE = `
    ${ItemQueries.BASE_SELECT}
    WHERE i.IsDeleted = 0 AND i.DeletedAt IS NULL
    ORDER BY i.CreatedAt DESC`;

  /**
   * Get a single item by ID.
   */
  public static readonly GET_BY_ID = `
    SELECT
      i.Id,
      i.Name,
      i.ItemType,
      i.FolderId,
      l.FileData as Logo,
      CASE WHEN EXISTS (SELECT 1 FROM Passkeys pk WHERE pk.ItemId = i.Id AND pk.IsDeleted = 0) THEN 1 ELSE 0 END as HasPasskey,
      CASE WHEN EXISTS (SELECT 1 FROM Attachments att WHERE att.ItemId = i.Id AND att.IsDeleted = 0) THEN 1 ELSE 0 END as HasAttachment,
      CASE WHEN EXISTS (SELECT 1 FROM TotpCodes tc WHERE tc.ItemId = i.Id AND tc.IsDeleted = 0) THEN 1 ELSE 0 END as HasTotp,
      i.CreatedAt,
      i.UpdatedAt
    FROM Items i
    LEFT JOIN Logos l ON i.LogoId = l.Id
    WHERE i.Id = ? AND i.IsDeleted = 0`;

  /**
   * Get all recently deleted items (in trash).
   */
  public static readonly GET_RECENTLY_DELETED = `
    SELECT
      i.Id,
      i.Name,
      i.ItemType,
      i.FolderId,
      l.FileData as Logo,
      CASE WHEN EXISTS (SELECT 1 FROM Passkeys pk WHERE pk.ItemId = i.Id AND pk.IsDeleted = 0) THEN 1 ELSE 0 END as HasPasskey,
      CASE WHEN EXISTS (SELECT 1 FROM Attachments att WHERE att.ItemId = i.Id AND att.IsDeleted = 0) THEN 1 ELSE 0 END as HasAttachment,
      CASE WHEN EXISTS (SELECT 1 FROM TotpCodes tc WHERE tc.ItemId = i.Id AND tc.IsDeleted = 0) THEN 1 ELSE 0 END as HasTotp,
      i.CreatedAt,
      i.UpdatedAt,
      i.DeletedAt
    FROM Items i
    LEFT JOIN Logos l ON i.LogoId = l.Id
    WHERE i.IsDeleted = 0 AND i.DeletedAt IS NOT NULL
    ORDER BY i.DeletedAt DESC`;

  /**
   * Count of recently deleted items.
   */
  public static readonly COUNT_RECENTLY_DELETED = `
    SELECT COUNT(*) as count
    FROM Items
    WHERE IsDeleted = 0 AND DeletedAt IS NOT NULL`;

  /**
   * Get field values for multiple items.
   * @param itemCount - Number of items (for placeholder generation)
   * @returns Query with placeholders
   */
  public static getFieldValuesForItems(itemCount: number): string {
    const placeholders = Array(itemCount).fill('?').join(',');
    return `
      SELECT
        fv.ItemId,
        fv.FieldKey,
        fv.FieldDefinitionId,
        fd.Label as CustomLabel,
        fd.FieldType as CustomFieldType,
        fd.IsHidden as CustomIsHidden,
        fd.EnableHistory as CustomEnableHistory,
        fv.Value,
        fv.Weight as DisplayOrder
      FROM FieldValues fv
      LEFT JOIN FieldDefinitions fd ON fv.FieldDefinitionId = fd.Id
      WHERE fv.ItemId IN (${placeholders})
        AND fv.IsDeleted = 0
      ORDER BY fv.ItemId, fv.Weight`;
  }

  /**
   * Get field values for a single item.
   */
  public static readonly GET_FIELD_VALUES_FOR_ITEM = `
    SELECT
      fv.FieldKey,
      fv.FieldDefinitionId,
      fd.Label as CustomLabel,
      fd.FieldType as CustomFieldType,
      fd.IsHidden as CustomIsHidden,
      fd.EnableHistory as CustomEnableHistory,
      fv.Value,
      fv.Weight as DisplayOrder
    FROM FieldValues fv
    LEFT JOIN FieldDefinitions fd ON fv.FieldDefinitionId = fd.Id
    WHERE fv.ItemId = ? AND fv.IsDeleted = 0
    ORDER BY fv.Weight`;

  /**
   * Get tags for multiple items.
   * @param itemCount - Number of items (for placeholder generation)
   * @returns Query with placeholders
   */
  public static getTagsForItems(itemCount: number): string {
    const placeholders = Array(itemCount).fill('?').join(',');
    return `
      SELECT
        it.ItemId,
        t.Id,
        t.Name,
        t.Color
      FROM ItemTags it
      INNER JOIN Tags t ON it.TagId = t.Id
      WHERE it.ItemId IN (${placeholders})
        AND it.IsDeleted = 0
        AND t.IsDeleted = 0
      ORDER BY t.DisplayOrder, t.Name`;
  }

  /**
   * Get tags for a single item.
   */
  public static readonly GET_TAGS_FOR_ITEM = `
    SELECT
      t.Id,
      t.Name,
      t.Color
    FROM ItemTags it
    INNER JOIN Tags t ON it.TagId = t.Id
    WHERE it.ItemId = ? AND it.IsDeleted = 0 AND t.IsDeleted = 0
    ORDER BY t.DisplayOrder, t.Name`;

  /**
   * Insert a new item.
   */
  public static readonly INSERT_ITEM = `
    INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Update an existing item (preserves LogoId if null is passed).
   */
  public static readonly UPDATE_ITEM = `
    UPDATE Items
    SET Name = ?,
        ItemType = ?,
        FolderId = ?,
        LogoId = COALESCE(?, LogoId),
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Update an existing item with explicit LogoId setting (can clear LogoId to null).
   */
  public static readonly UPDATE_ITEM_WITH_LOGO = `
    UPDATE Items
    SET Name = ?,
        ItemType = ?,
        FolderId = ?,
        LogoId = ?,
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Move item to trash (set DeletedAt).
   */
  public static readonly TRASH_ITEM = `
    UPDATE Items
    SET DeletedAt = ?,
        UpdatedAt = ?
    WHERE Id = ? AND IsDeleted = 0`;

  /**
   * Restore item from trash (clear DeletedAt).
   */
  public static readonly RESTORE_ITEM = `
    UPDATE Items
    SET DeletedAt = NULL,
        UpdatedAt = ?
    WHERE Id = ? AND IsDeleted = 0 AND DeletedAt IS NOT NULL`;

  /**
   * Convert item to tombstone for permanent deletion.
   */
  public static readonly TOMBSTONE_ITEM = `
    UPDATE Items
    SET IsDeleted = 1,
        Name = NULL,
        LogoId = NULL,
        FolderId = NULL,
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Get all unique email addresses from field values.
   */
  public static readonly GET_ALL_EMAIL_ADDRESSES = `
    SELECT DISTINCT fv.Value as Email
    FROM FieldValues fv
    INNER JOIN Items i ON fv.ItemId = i.Id
    WHERE fv.FieldKey = ?
      AND fv.Value IS NOT NULL
      AND fv.Value != ''
      AND fv.IsDeleted = 0
      AND i.IsDeleted = 0
      AND i.DeletedAt IS NULL`;

  /**
   * Look up an item (id + name) by an email address stored in any of its login email fields.
   */
  public static readonly GET_ITEM_BY_EMAIL = `
    SELECT i.Id as Id, i.Name as Name
    FROM FieldValues fv
    INNER JOIN Items i ON fv.ItemId = i.Id
    WHERE fv.FieldKey = ?
      AND fv.Value = ?
      AND fv.IsDeleted = 0
      AND i.IsDeleted = 0
      AND i.DeletedAt IS NULL
    LIMIT 1`;

  /**
   * Get item-level fields for change detection during updates.
   */
  public static readonly GET_ITEM_FIELDS = `
    SELECT Name, ItemType, FolderId, LogoId
    FROM Items
    WHERE Id = ?`;
}

/**
 * SQL query constants for FieldValue operations.
 */
export class FieldValueQueries {
  /**
   * Get existing field values for an item.
   */
  public static readonly GET_EXISTING_FOR_ITEM = `
    SELECT Id, FieldKey, FieldDefinitionId, Value, Weight
    FROM FieldValues
    WHERE ItemId = ? AND IsDeleted = 0`;

  /**
   * Insert a new field value.
   */
  public static readonly INSERT = `
    INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Update an existing field value.
   */
  public static readonly UPDATE = `
    UPDATE FieldValues
    SET Value = ?,
        Weight = ?,
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Soft delete a field value.
   */
  public static readonly SOFT_DELETE = `
    UPDATE FieldValues
    SET IsDeleted = 1,
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Get existing field values for history tracking.
   */
  public static readonly GET_FOR_HISTORY = `
    SELECT FieldKey, Value
    FROM FieldValues
    WHERE ItemId = ? AND IsDeleted = 0 AND FieldKey IS NOT NULL`;
}

/**
 * SQL query constants for FieldDefinition operations.
 */
export class FieldDefinitionQueries {
  /**
   * Check if a field definition exists.
   */
  public static readonly EXISTS = `
    SELECT Id FROM FieldDefinitions WHERE Id = ?`;

  /**
   * Check if a field definition exists and is not deleted.
   */
  public static readonly EXISTS_ACTIVE = `
    SELECT Id FROM FieldDefinitions WHERE Id = ? AND IsDeleted = 0`;

  /**
   * Insert a new field definition.
   */
  public static readonly INSERT = `
    INSERT INTO FieldDefinitions (Id, FieldType, Label, IsMultiValue, IsHidden, EnableHistory, Weight, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Update an existing field definition.
   */
  public static readonly UPDATE = `
    UPDATE FieldDefinitions
    SET Label = ?,
        FieldType = ?,
        IsHidden = ?,
        Weight = ?,
        UpdatedAt = ?
    WHERE Id = ?`;
}

/**
 * SQL query constants for FieldHistory operations.
 */
export class FieldHistoryQueries {
  /**
   * Insert a history record.
   */
  public static readonly INSERT = `
    INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, FieldKey, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Get history records for a field.
   */
  public static readonly GET_FOR_FIELD = `
    SELECT
      Id,
      ItemId,
      FieldKey,
      ValueSnapshot,
      ChangedAt,
      CreatedAt,
      UpdatedAt
    FROM FieldHistories
    WHERE ItemId = ? AND FieldKey = ? AND IsDeleted = 0
    ORDER BY ChangedAt DESC
    LIMIT ?`;

  /**
   * Get all history records for pruning.
   */
  public static readonly GET_FOR_PRUNING = `
    SELECT Id, ChangedAt
    FROM FieldHistories
    WHERE ItemId = ? AND FieldKey = ? AND IsDeleted = 0
    ORDER BY ChangedAt DESC`;

  /**
   * Soft delete old history records.
   * @param count - Number of records to delete
   * @returns Query with placeholders
   */
  public static softDeleteOld(count: number): string {
    const placeholders = Array(count).fill('?').join(',');
    return `
      UPDATE FieldHistories
      SET IsDeleted = 1, UpdatedAt = ?
      WHERE Id IN (${placeholders})`;
  }

  /**
   * Soft delete a single history record.
   */
  public static readonly SOFT_DELETE = `
    UPDATE FieldHistories
    SET IsDeleted = 1, UpdatedAt = ?
    WHERE Id = ?`;
}

/**
 * SQL query constants for TotpCode operations.
 */
export class TotpCodeQueries {
  /**
   * Get existing TOTP codes for an item.
   */
  public static readonly GET_BY_ITEM_ID = `
    SELECT Id, Name, SecretKey
    FROM TotpCodes
    WHERE ItemId = ? AND IsDeleted = 0`;

  /**
   * Insert a new TOTP code.
   */
  public static readonly INSERT = `
    INSERT INTO TotpCodes (Id, Name, SecretKey, ItemId, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Update an existing TOTP code.
   */
  public static readonly UPDATE = `
    UPDATE TotpCodes
    SET Name = ?,
        SecretKey = ?,
        UpdatedAt = ?
    WHERE Id = ?`;

  /**
   * Soft delete a TOTP code.
   */
  public static readonly SOFT_DELETE = `
    UPDATE TotpCodes
    SET IsDeleted = 1,
        UpdatedAt = ?
    WHERE Id = ?`;
}

/**
 * SQL query constants for Attachment operations.
 */
export class AttachmentQueries {
  /**
   * Insert a new attachment.
   */
  public static readonly INSERT = `
    INSERT INTO Attachments (Id, Filename, Blob, ItemId, CreatedAt, UpdatedAt, IsDeleted)
    VALUES (?, ?, ?, ?, ?, ?, ?)`;

  /**
   * Soft delete an attachment. Also zeroes the Blob bytes so storage is reclaimed
   * immediately while the row remains as a tombstone for LWW sync.
   */
  public static readonly SOFT_DELETE = `
    UPDATE Attachments
    SET IsDeleted = 1,
        Blob = X'',
        UpdatedAt = ?
    WHERE Id = ?`;
}
