import { Arg, Query, Resolver } from "@aet/gql-tools/macro"
import { Match } from "~/shared/models/index"
import { getDocs } from "~/background/couchdb"

@Resolver()
export class FindResolver {
  @Query()
  async search(
    @Arg() query: string,
    @Arg() caseSensitive = false,
    @Arg() regExp: boolean,
  ): Promise<Match[]> {
    if (!query) {
      return []
    }

    if (regExp) {
      return searchRegExp(query)
    }

    const files = await getDocs()
    const matches: Match[] = []

    const searchText = caseSensitive ? query : query.toLocaleUpperCase()

    for (const file of files) {
      const content = file.sections[0].code
      let prevIndex: number | null | undefined

      // https://stackoverflow.com/questions/69060325
      const searchFrom = caseSensitive ? content : content.toLocaleUpperCase()

      while (prevIndex !== null) {
        if (matches.length > 100) {
          return matches
        }

        const index = stripInvalid(
          searchFrom.indexOf(searchText, prevIndex ? prevIndex + 1 : 0),
        )
        prevIndex = index
        if (index == null) continue

        const { line, start } = getLineAround(content, index)

        matches.push({
          file,
          line,
          index,
          submatch: {
            match: content.slice(index, index + searchText.length),
            start: index - start,
            end: index - start + query.length,
          },
        })
        await (0 as any)
      }
    }

    return matches
  }
}

function stripInvalid(index: number) {
  return index === -1 ? null : index
}

function getLineAround(text: string, index: number) {
  const start = stripInvalid(text.lastIndexOf("\n", index)) ?? 0
  const end = stripInvalid(text.indexOf("\n", index)) ?? text.length
  return {
    start,
    line: text.substring(start, end),
  }
}

async function searchRegExp(query: string): Promise<Match[]> {
  const files = await getDocs()
  const matches: Match[] = []
  const regexp = new RegExp(query, "g")

  for (const file of files) {
    const content = file.sections[0].code
    const prevIndex: number | null = null

    while (prevIndex != null) {
      for (const matched of content.matchAll(regexp)) {
        if (matched.index == null) continue
        const { index } = matched

        const { line, start } = getLineAround(content, index)
        matches.push({
          file,
          line,
          index,
          submatch: {
            match: matched[0],
            start: index - start,
            end: index - start + matched[0].length,
          },
        })
      }
      await (0 as any)
    }
  }

  return matches
}
