import { convertPathToJSONTraversal } from './convertPathToJSONTraversal.js'
import { formatJSONPathSegment } from './formatJSONPathSegment.js'

const operatorMap = {
  contains: '~*',
  equals: '=',
  like: '~*',
}

type FromArrayArgs = {
  isRoot?: true
  operator: string
  pathSegments: string[]
  treatAsArray?: string[]
  value: unknown
}

const fromArray = ({ isRoot, operator, pathSegments, treatAsArray, value }: FromArrayArgs) => {
  const newPathSegments = pathSegments.slice(isRoot ? 1 : 2)
  const alias = `${pathSegments[isRoot ? 0 : 1]}_alias_${newPathSegments.length}`

  newPathSegments.unshift(alias)

  const arrayElements = isRoot
    ? pathSegments[0]
    : `${pathSegments[0]} -> ${formatJSONPathSegment(pathSegments[1])}`

  return `EXISTS (
    SELECT 1
    FROM jsonb_array_elements(${arrayElements}) AS ${alias}
    WHERE ${createJSONQuery({
      operator,
      pathSegments: newPathSegments,
      treatAsArray,
      value,
    })}
  )`
}

type CreateConstraintArgs = {
  operator: string
  pathSegments: string[]
  treatAsArray?: string[]
  value: unknown
}

const createConstraint = ({ operator, pathSegments, value }: CreateConstraintArgs): string => {
  const jsonQuery = convertPathToJSONTraversal(pathSegments)
  return `${pathSegments[0]}${jsonQuery} ${operatorMap[operator]} '${value}'`
}

type Args = {
  operator: string
  pathSegments: string[]
  treatAsArray?: string[]
  treatRootAsArray?: boolean
  value: unknown
}

export const createJSONQuery = ({
  operator,
  pathSegments,
  treatAsArray,
  treatRootAsArray,
  value,
}: Args): string => {
  if (treatRootAsArray) {
    return fromArray({
      isRoot: true,
      operator,
      pathSegments,
      treatAsArray,
      value,
    })
  }

  if (treatAsArray.includes(pathSegments[1])) {
    return fromArray({
      operator,
      pathSegments,
      treatAsArray,
      value,
    })
  }

  return createConstraint({ operator, pathSegments, treatAsArray, value })
}
