import { $store } from "@racletteCore"
/**
 * Advanced selector for Redux store that supports complex path querying with namespacing
 * and mixed selection patterns
 *
 * // Select specific nested property
 * select(['data', 'testID', 'someProperty'])
 *
 * // Select multiple specific keys from data
 * select(['data', ['testID', 'testID3']])
 *
 * // Select nested properties from multiple selected keys
 * select(['data', ['testID', 'testID3'], '_source'])
 * select(['data', ['testID', 'testID3'], '_source'])
 *
 * // Multiple independent selections with custom names
 * select([
 *   { path: 'data.testID', as: 'currentTest', namespace:'testname' },
 *   { path: 'ui.theme', as: 'appearance' },
 *   { path: 'backend.state', as: 'connection' }
 * ])
 *
 * // Multiple nested selections
 * select([
 *   { path: 'data.testID.nestedProp1', as: 'customKey', namespace: 'testname' },
 *   { path: 'data.testID2.nestedProp2' },
 *   { path: 'ui.theme' }
 * ])
 */
export default (queryPath) =>
  $store.select((state) => {
    // Handle empty query
    if (!queryPath) {
      return state
    }

    // If queryPath is a string or an array that's not an array of objects
    if (
      typeof queryPath === "string" ||
      (Array.isArray(queryPath) &&
        (queryPath.length === 0 ||
          !(
            typeof queryPath[0] === "object" &&
            !Array.isArray(queryPath[0]) &&
            "path" in queryPath[0]
          )))
    ) {
      // If it's a simple selection (string or array path)
      if (typeof queryPath === "string") {
        return state[queryPath]
      } else if (
        Array.isArray(queryPath) &&
        !queryPath.some(
          (item) => typeof item === "object" && !Array.isArray(item),
        )
      ) {
        // It's a simple array path like ['data', 'testID']
        return processPathArray(state, queryPath)
      } else {
        // It's a mixed array with different selection styles
        return processMixedQuery(state, queryPath)
      }
    }

    // Handle single path object
    if (
      typeof queryPath === "object" &&
      !Array.isArray(queryPath) &&
      queryPath.path
    ) {
      return processSinglePathObject(state, queryPath)
    }

    // Handle array of selection objects
    if (Array.isArray(queryPath)) {
      return processMixedQuery(state, queryPath)
    }

    console.warn("Invalid query path format")

    return state
  })

/**
 * Process an array of path segments ['data', 'testID', etc.]
 */
const processPathArray = (state, pathArray) => {
  let current = state
  let multipleKeysIndex = -1
  let multipleKeysResult = null

  for (let i = 0; i < pathArray.length; i++) {
    const segment = pathArray[i]

    // Handle null/undefined current state
    if (current === null || current === undefined) {
      console.warn(
        `Path ${pathArray.slice(0, i).join(".")} resulted in null/undefined`,
      )

      return null
    }

    // Handle different segment types
    if (typeof segment === "string") {
      // Wildcard: select all immediate children
      if (segment === "*") {
        return current
      }

      // Deep wildcard: select all nested children
      if (segment === "**") {
        return current
      }

      // If we previously selected multiple keys, we need to traverse each one
      if (multipleKeysResult !== null) {
        // Apply the property access to each selected key result
        Object.keys(multipleKeysResult).forEach((key) => {
          if (
            multipleKeysResult[key] !== null &&
            multipleKeysResult[key] !== undefined
          ) {
            multipleKeysResult[key] = multipleKeysResult[key][segment]
          }
        })
        current = multipleKeysResult
      } else {
        // Regular string key - normal traversal
        current = current[segment]
      }
    }
    // Array of keys to select at this level
    else if (Array.isArray(segment)) {
      const result = {}

      // Store the index where we found multiple keys for later use
      multipleKeysIndex = i
      multipleKeysResult = result

      segment.forEach((key) => {
        if (current[key] !== undefined) {
          result[key] = current[key]
        }
      })
      current = result
    }
    // Invalid segment type
    else {
      console.warn(`Invalid path segment at index ${i}`)

      return null
    }
  }

  return current
}

/**
 * Process a single path object with optional namespace
 */
const processSinglePathObject = (state, pathObj) => {
  const value = getByStringPath(state, pathObj.path)

  // If namespace is provided, nest the result under that namespace
  if (pathObj.namespace) {
    const result = {}
    const key = pathObj.as || pathObj.path.split(".").pop()

    result[pathObj.namespace] = {
      [key]: value,
    }

    return result
  } else {
    // No namespace, return as a simple key-value
    const result = {}
    const key = pathObj.as || pathObj.path.split(".").pop()

    result[key] = value

    return result
  }
}

/**
 * Process a mixed array of different selection styles
 */
const processMixedQuery = (state, queryItems) => {
  const result = {}
  const namespaces = {}

  queryItems.forEach((item) => {
    // Handle path object style selection
    if (typeof item === "object" && !Array.isArray(item) && item.path) {
      const value = getByStringPath(state, item.path)
      const key = item.as || item.path.split(".").pop()

      // Handle namespaced result
      if (item.namespace) {
        // Initialize namespace if it doesn't exist
        if (!namespaces[item.namespace]) {
          namespaces[item.namespace] = {}
          result[item.namespace] = namespaces[item.namespace]
        }

        // Add value to the appropriate namespace
        namespaces[item.namespace][key] = value
      } else {
        // Non-namespaced result goes directly at the root level
        result[key] = value
      }
    }
    // Handle string style selection (top-level key)
    else if (typeof item === "string") {
      result[item] = state[item]
    }
    // Handle array path style selection
    else if (Array.isArray(item)) {
      // Get the last segment as the key name, or use the full path if empty
      const key =
        item.length > 0
          ? Array.isArray(item[item.length - 1])
            ? `${item.slice(0, -1).join(".")}`
            : typeof item[item.length - 1] === "string"
              ? item[item.length - 1]
              : item.join(".")
          : "result"

      const value = processPathArray(state, item)

      result[key] = value
    }
    // Invalid item type
    else {
      console.warn("Invalid query item", item)
    }
  })

  return result
}

/**
 * Get a value from state using a dot-notation string path
 * e.g., 'data.testID.subField'
 */
const getByStringPath = (state, path) =>
  path.split(".").reduce((obj, key) => {
    // Handle wildcards in path
    if (key === "*" || key === "**") {
      return obj
    }

    return obj && obj[key] !== undefined ? obj[key] : null
  }, state)
