     1	import { requirePackage } from 'hooked-api'
     2	
     3	let generateKeyBetween
     4	try {
     5	  ({ generateKeyBetween } = await import('fractional-indexing'))
     6	} catch (e) {
     7	  requirePackage('fractional-indexing', 'positioning',
     8	    'Fractional indexing is required for the positioning plugin to generate sortable position keys. This is a peer dependency.')
     9	}
    10	
    11	/**
    12	 * Helper functions for fractional positioning
    13	 * Uses the fractional-indexing library for generating position keys
    14	 */
    15	
    16	/**
    17	 * Calculate position for a new/moved item based on beforeId
    18	 * @param {Array} items - Array of items with position field
    19	 * @param {string} beforeId - ID to position before (null = last)
    20	 * @param {string} idField - Name of the ID field
    21	 * @param {string} positionField - Name of the position field
    22	 * @returns {string} New position key
    23	 */
    24	export function calculatePosition (items, beforeId, idField, positionField) {
    25	  // Sort items by position using simple string comparison
    26	  // Important: fractional-indexing expects ASCII/Unicode ordering, not locale-specific
    27	  const sorted = [...items].sort((a, b) => {
    28	    const posA = a[positionField] || ''
    29	    const posB = b[positionField] || ''
    30	    return posA < posB ? -1 : posA > posB ? 1 : 0
    31	  })
    32	
    33	  // If no items, start in the middle
    34	  if (sorted.length === 0) {
    35	    return generateKeyBetween(null, null) // Returns 'a0'
    36	  }
    37	
    38	  // Position at end
    39	  if (beforeId === null || beforeId === undefined) {
    40	    const lastItem = sorted[sorted.length - 1]
    41	    return generateKeyBetween(lastItem[positionField], null)
    42	  }
    43	
    44	  // Find the item to position before
    45	  const beforeIndex = sorted.findIndex(item => String(item[idField]) === String(beforeId))
    46	
    47	  // If beforeId not found, position at end
    48	  if (beforeIndex === -1) {
    49	    const lastItem = sorted[sorted.length - 1]
    50	    return generateKeyBetween(lastItem[positionField], null)
    51	  }
    52	
    53	  // Position before the found item
    54	  const beforeItem = sorted[beforeIndex]
    55	  const prevItem = beforeIndex > 0 ? sorted[beforeIndex - 1] : null
    56	
    57	  const result = generateKeyBetween(
    58	    prevItem ? prevItem[positionField] : null,
    59	    beforeItem[positionField]
    60	  )
    61	
    62	  return result
    63	}
    64	
    65	/**
    66	 * Initialize position for first item in a group
    67	 * @returns {string} Initial position key
    68	 */
    69	export function getInitialPosition () {
    70	  return generateKeyBetween(null, null) // Returns 'a0'
    71	}
    72	
    73	/**
    74	 * Rebalance positions if they get too long (optional maintenance)
    75	 * @param {Array} items - Array of items to rebalance
    76	 * @param {string} positionField - Name of the position field
    77	 * @param {number} maxLength - Maximum position string length before rebalancing
    78	 * @returns {Array} Items with new positions (or original if no rebalance needed)
    79	 */
    80	export function rebalancePositions (items, positionField, maxLength = 50) {
    81	  // Check if rebalancing is needed
    82	  const needsRebalance = items.some(item =>
    83	    item[positionField] && item[positionField].length > maxLength
    84	  )
    85	
    86	  if (!needsRebalance) {
    87	    return items
    88	  }
    89	
    90	  // Sort by current position
    91	  const sorted = [...items].sort((a, b) => {
    92	    const posA = a[positionField] || ''
    93	    const posB = b[positionField] || ''
    94	    return posA < posB ? -1 : posA > posB ? 1 : 0
    95	  })
    96	
    97	  // Generate evenly spaced positions
    98	  const rebalanced = []
    99	  let prevKey = null
   100	
   101	  for (let i = 0; i < sorted.length; i++) {
   102	    // For even spacing, we generate keys sequentially
   103	    const newPosition = generateKeyBetween(prevKey, null)
   104	
   105	    rebalanced.push({
   106	      ...sorted[i],
   107	      [positionField]: newPosition
   108	    })
   109	
   110	    prevKey = newPosition
   111	  }
   112	
   113	  return rebalanced
   114	}
   115	
   116	/**
   117	 * Check if a position value is valid
   118	 * @param {string} position - Position value to check
   119	 * @returns {boolean} True if valid
   120	 */
   121	export function isValidPosition (position) {
   122	  if (!position || typeof position !== 'string') {
   123	    return false
   124	  }
   125	
   126	  // Fractional keys should match pattern: lowercase letters and digits
   127	  return /^[a-z0-9]+$/i.test(position)
   128	}
   129	
   130	/**
   131	 * Get items that need position assignment (for migration)
   132	 * @param {Array} items - Array of items
   133	 * @param {string} positionField - Name of the position field
   134	 * @returns {Array} Items without valid positions
   135	 */
   136	export function getUnpositionedItems (items, positionField) {
   137	  return items.filter(item => !isValidPosition(item[positionField]))
   138	}
   139	
   140	/**
   141	 * Assign initial positions to items that don't have them
   142	 * @param {Array} items - Array of items
   143	 * @param {string} positionField - Name of the position field
   144	 * @param {string} orderByField - Optional field to order by initially
   145	 * @returns {Array} Items with positions assigned
   146	 */
   147	export function assignInitialPositions (items, positionField, orderByField = null) {
   148	  const positioned = items.filter(item => isValidPosition(item[positionField]))
   149	  const unpositioned = items.filter(item => !isValidPosition(item[positionField]))
   150	
   151	  if (unpositioned.length === 0) {
   152	    return items
   153	  }
   154	
   155	  // Sort unpositioned items if orderBy field provided
   156	  if (orderByField) {
   157	    unpositioned.sort((a, b) => {
   158	      const valA = a[orderByField]
   159	      const valB = b[orderByField]
   160	      if (valA < valB) return -1
   161	      if (valA > valB) return 1
   162	      return 0
   163	    })
   164	  }
   165	
   166	  // Get the last positioned item
   167	  const sortedPositioned = [...positioned].sort((a, b) => {
   168	    const posA = a[positionField] || ''
   169	    const posB = b[positionField] || ''
   170	    return posA < posB ? -1 : posA > posB ? 1 : 0
   171	  })
   172	  const lastPositioned = sortedPositioned[sortedPositioned.length - 1]
   173	
   174	  let lastKey = lastPositioned ? lastPositioned[positionField] : null
   175	
   176	  // Assign positions to unpositioned items
   177	  const newlyPositioned = unpositioned.map(item => {
   178	    const newKey = generateKeyBetween(lastKey, null)
   179	    lastKey = newKey
   180	
   181	    return {
   182	      ...item,
   183	      [positionField]: newKey
   184	    }
   185	  })
   186	
   187	  return [...positioned, ...newlyPositioned]
   188	}
