import { ToolTip, toolTips } from '../../useStore'
import {
  Program,
  VariableDeclarator,
  CallExpression,
  SketchGroup,
  SourceRange,
  Path,
} from '../wasm'

export function getSketchSegmentFromSourceRange(
  sketchGroup: SketchGroup,
  [rangeStart, rangeEnd]: SourceRange
): {
  segment: SketchGroup['value'][number]
  index: number
} {
  const startSourceRange = sketchGroup.start?.__geoMeta.sourceRange
  if (
    startSourceRange &&
    startSourceRange[0] <= rangeStart &&
    startSourceRange[1] >= rangeEnd &&
    sketchGroup.start
  )
    return { segment: { ...sketchGroup.start, type: 'base' }, index: -1 }

  const lineIndex = sketchGroup.value.findIndex(
    ({ __geoMeta: { sourceRange } }: Path) =>
      sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
  )
  const line = sketchGroup.value[lineIndex]
  if (!line) throw new Error('could not find matching line')
  return {
    segment: line,
    index: lineIndex,
  }
}

export function isSketchVariablesLinked(
  secondaryVarDec: VariableDeclarator,
  primaryVarDec: VariableDeclarator,
  ast: Program
): boolean {
  /*
  checks if two callExpressions are part of the same pipe
  if not than checks if the second argument is a variable that is linked to the primary variable declaration
  and will keep checking the second arguments recursively until it runs out of variable declarations
  to check or it finds a match.
  that way it can find fn calls that are linked to each other through variables eg:
  const part001 = startSketchAt([0, 0])
    |> xLineTo(1.69, %)
    |> line([myVar, 0.38], %) // ❗️ <- cursor in this fn call (the primary)
    |> line([0.41, baz], %)
    |> xLine(0.91, %)
    |> angledLine([37, 2], %)
  const yo = line([myVar, 0.38], part001)
    |> line([1, 1], %)
  const yo2 = line([myVar, 0.38], yo)
    |> line([1, 1], %) // ❗️ <- and cursor here (secondary) is linked to the one above through variables
  */
  const secondaryVarName = secondaryVarDec?.id?.name
  if (!secondaryVarName) return false
  if (secondaryVarName === primaryVarDec?.id?.name) return true
  const { init } = secondaryVarDec
  if (
    !init ||
    !(init.type === 'CallExpression' || init.type === 'PipeExpression')
  )
    return false
  const firstCallExp = // first in pipe expression or just the call expression
    init?.type === 'CallExpression' ? init : (init?.body[0] as CallExpression)
  if (
    !firstCallExp ||
    !toolTips.includes(firstCallExp?.callee?.name as ToolTip)
  )
    return false
  // convention for sketch fns is that the second argument is the sketch group
  const secondArg = firstCallExp?.arguments[1]
  if (!secondArg || secondArg?.type !== 'Identifier') return false
  if (secondArg.name === primaryVarDec?.id?.name) return true

  let nextVarDec: VariableDeclarator | undefined
  for (const node of ast.body) {
    if (node.type !== 'VariableDeclaration') continue
    const found = node.declarations.find(
      ({ id }) => id?.name === secondArg.name
    )
    if (!found) continue
    nextVarDec = found
    break
  }
  if (!nextVarDec) return false
  return isSketchVariablesLinked(nextVarDec, primaryVarDec, ast)
}
