import { Effect } from "effect"
import fuzzysort from "fuzzysort"
import type { Entry } from "./file-status"
import * as Log from "@saeeol/core/util/log"

const log = Log.create({ service: "file" })

const hidden = (item: string) => {
  const normalized = item.replaceAll("\\", "/").replace(/\/+$/, "")
  return normalized.split("/").some((part) => part.startsWith(".") && part.length > 1)
}

const sortHiddenLast = (items: string[], prefer: boolean) => {
  if (prefer) return items
  const visible: string[] = []
  const hiddenItems: string[] = []
  for (const item of items) {
    if (hidden(item)) hiddenItems.push(item)
    else visible.push(item)
  }
  return [...visible, ...hiddenItems]
}

export function makeSearchOp(deps: {
  ensure: () => Effect.Effect<void>
  getCache: () => Effect.Effect<{ cache: Entry }>
}) {
  const search = Effect.fn("File.search")(function* (input: {
    query: string
    limit?: number
    dirs?: boolean
    type?: "file" | "directory"
  }) {
    yield* deps.ensure()
    const { cache } = yield* deps.getCache()

    const query = input.query.trim()
    const limit = input.limit ?? 100
    const kind = input.type ?? (input.dirs === false ? "file" : "all")
    log.info("search", { query, kind })

    const preferHidden = query.startsWith(".") || query.includes("/.")

    if (!query) {
      if (kind === "file") return cache.files.slice(0, limit)
      return sortHiddenLast(cache.dirs.toSorted(), preferHidden).slice(0, limit)
    }

    const items = kind === "file" ? cache.files : kind === "directory" ? cache.dirs : [...cache.files, ...cache.dirs]

    const searchLimit = kind === "directory" && !preferHidden ? limit * 20 : limit
    const sorted = fuzzysort.go(query, items, { limit: searchLimit }).map((item) => item.target)
    const output = kind === "directory" ? sortHiddenLast(sorted, preferHidden).slice(0, limit) : sorted

    log.info("search", { query, kind, results: output.length })
    return output
  })

  return { search }
}
