import type { RouteNode } from './Route'

/**
 * Find a RouteNode from the route tree based on the navigation state.
 * Walks through the state's routes recursively to find the deepest matching route.
 */
export function findRouteNodeFromState(
  state: { routes: Array<{ name: string; state?: any }> } | undefined,
  rootNode: RouteNode | null
): RouteNode | null {
  if (!state || !state.routes || !rootNode) {
    return null
  }

  // Get the current route from state (the active one based on index)
  const currentRoute = state.routes[state.routes.length - 1]
  if (!currentRoute) {
    return null
  }

  // Find the matching child node
  const matchingNode = findNodeByRouteName(rootNode, currentRoute.name)
  if (!matchingNode) {
    return null
  }

  // If there's a nested state, continue recursively
  if (currentRoute.state && currentRoute.state.routes) {
    const nestedResult = findRouteNodeFromState(currentRoute.state, matchingNode)
    if (nestedResult) {
      return nestedResult
    }
  }

  return matchingNode
}

/**
 * Find a node by its route name in the tree.
 * Searches children recursively.
 */
function findNodeByRouteName(node: RouteNode, routeName: string): RouteNode | null {
  // Check if this node matches
  if (node.route === routeName) {
    return node
  }

  // Search children
  for (const child of node.children) {
    const found = findNodeByRouteName(child, routeName)
    if (found) {
      return found
    }
  }

  return null
}

/**
 * Extract params from navigation state.
 * Collects params from all routes in the state hierarchy.
 */
export function extractParamsFromState(
  state:
    | { routes: Array<{ name: string; params?: Record<string, any>; state?: any }> }
    | undefined
): Record<string, string | string[]> {
  if (!state || !state.routes) {
    return {}
  }

  const params: Record<string, string | string[]> = {}

  // Collect params from all routes in the state
  for (const route of state.routes) {
    if (route.params) {
      Object.assign(params, route.params)
    }
    // Recurse into nested state
    if (route.state) {
      Object.assign(params, extractParamsFromState(route.state))
    }
  }

  return params
}

/**
 * Extract search params from href string.
 */
export function extractSearchFromHref(href: string): Record<string, string | string[]> {
  const searchIndex = href.indexOf('?')
  if (searchIndex === -1) {
    return {}
  }

  const searchString = href.slice(searchIndex + 1)
  const params: Record<string, string | string[]> = {}
  const searchParams = new URLSearchParams(searchString)

  searchParams.forEach((value, key) => {
    const existing = params[key]
    if (existing === undefined) {
      params[key] = value
    } else if (Array.isArray(existing)) {
      existing.push(value)
    } else {
      params[key] = [existing, value]
    }
  })

  return params
}

/**
 * Extract pathname from href string.
 */
export function extractPathnameFromHref(href: string): string {
  const searchIndex = href.indexOf('?')
  const hashIndex = href.indexOf('#')

  let endIndex = href.length
  if (searchIndex !== -1) endIndex = Math.min(endIndex, searchIndex)
  if (hashIndex !== -1) endIndex = Math.min(endIndex, hashIndex)

  return href.slice(0, endIndex)
}

/**
 * Find all route nodes from root to the current page based on navigation state.
 * Returns an array of RouteNodes in order from root layout to the page.
 * This is used on native to build the full matches array including layouts.
 */
export function findAllRouteNodesFromState(
  state: { routes: Array<{ name: string; state?: any }> } | undefined,
  rootNode: RouteNode | null
): RouteNode[] {
  if (!state || !state.routes || !rootNode) {
    return []
  }

  const nodes: RouteNode[] = []

  function collectNodes(
    currentState:
      | { routes: Array<{ name: string; state?: any; params?: Record<string, any> }> }
      | undefined,
    parentNode: RouteNode | null
  ) {
    if (!currentState || !currentState.routes || !parentNode) {
      return
    }

    // get the current route from state (the active one based on index)
    const currentRoute = currentState.routes[currentState.routes.length - 1]
    if (!currentRoute) {
      return
    }

    // find the matching child node
    const matchingNode = findNodeByRouteName(parentNode, currentRoute.name)
    if (!matchingNode) {
      if (process.env.NODE_ENV === 'development') {
        console.log(
          '[one] findAllRouteNodesFromState: could not find node for',
          currentRoute.name,
          'in',
          parentNode.route
        )
      }
      return
    }

    // add this node to the list
    nodes.push(matchingNode)

    // if there's a nested state, continue recursively
    if (currentRoute.state && currentRoute.state.routes) {
      collectNodes(currentRoute.state, matchingNode)
    } else if (currentRoute.params?.screen) {
      // react navigation uses params.screen to specify child route when state isn't created yet
      // this happens on initial render before the nested navigator mounts
      const childRouteName = currentRoute.params.screen as string
      const childNode = matchingNode.children.find((c) => c.route === childRouteName)
      if (childNode) {
        nodes.push(childNode)
        // if there are nested params, continue recursively
        if (currentRoute.params.params && (currentRoute.params.params as any).screen) {
          collectNodes(
            { routes: [{ name: childRouteName, params: currentRoute.params.params }] },
            childNode
          )
        }
      }
    }
  }

  collectNodes(state, rootNode)
  return nodes
}
