import { Effect, Stream, Semaphore } from "effect"
import { ChildProcess } from "effect/unstable/process"
import * as Log from "@saeeol/core/util/log"

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

export const coreFlags = ["-c", "core.longpaths=true", "-c", "core.symlinks=true"]
export const cfgFlags = ["-c", "core.autocrlf=false", ...coreFlags]
export const quoteFlags = [...cfgFlags, "-c", "core.quotepath=false"]
export const limit = 2 * 1024 * 1024

export interface GitResult {
  readonly code: number
  readonly text: string
  readonly stderr: string
}

export interface Spawner {
  spawn: (proc: any) => Effect.Effect<any, any, any>
}

export const mkArgs = (gitdir: string, worktree: string, cmd: string[]) =>
  ["--git-dir", gitdir, "--work-tree", worktree, ...cmd]

export const feed = (list: string[]) =>
  Stream.make(new TextEncoder().encode(list.join("\0") + "\0"))

export const mkGit = (spawner: Spawner) =>
  Effect.fnUntraced(
    function* (
      cmd: string[],
      opts?: { cwd?: string; env?: Record<string, string>; stdin?: any },
    ) {
      const proc = ChildProcess.make("git", cmd, {
        cwd: opts?.cwd, env: opts?.env, extendEnv: true, stdin: opts?.stdin,
      })
      const handle = yield* spawner.spawn(proc)
      const [text, stderr] = yield* Effect.all(
        [Stream.mkString(Stream.decodeText(handle.stdout)), Stream.mkString(Stream.decodeText(handle.stderr))],
        { concurrency: 2 },
      )
      const code = yield* handle.exitCode
      return { code, text, stderr } as GitResult
    },
    Effect.scoped,
    Effect.catch((err) =>
      Effect.succeed({ code: 1 as any, text: "", stderr: err instanceof Error ? err.message : String(err) } as GitResult),
    ),
  )

export const locked = <A, E, R>(
  locks: Map<string, Semaphore.Semaphore>,
  key: string,
  fx: Effect.Effect<A, E, R>,
) => {
  const hit = locks.get(key)
  const sem = hit ?? Semaphore.makeUnsafe(1)
  if (!hit) locks.set(key, sem)
  return sem.withPermits(1)(fx)
}
