## Git operations for worker coordination
##
## Uses osproc for subprocess execution.
## Key operations: worktree create/remove, rebase, merge

import std/[os, osproc, strutils, strformat, streams]
import ./types
import ./utils

proc runGit*(args: openArray[string], workDir = ""): tuple[output: string, exitCode: int] =
  ## Run a git command and return output + exit code
  ## Note: uses openArray instead of varargs to avoid binding issues
  var cmd = "git"
  var fullArgs: seq[string] = @[]

  if workDir != "":
    fullArgs.add("-C")
    fullArgs.add(workDir)

  for arg in args:
    fullArgs.add(arg)

  let process = startProcess(cmd, args = fullArgs,
                             options = {poUsePath, poStdErrToStdOut})
  result.output = process.outputStream.readAll()
  result.exitCode = process.waitForExit()
  process.close()

proc runGitCheck*(args: openArray[string], workDir = ""): string =
  ## Run git command, raise GitError on failure
  let (output, code) = runGit(args, workDir)
  if code != 0:
    raise newException(GitError, &"Git command failed ({code}): {output}")
  return output.strip()

proc createWorktree*(taskId: string, fromBranch: string = "origin/integration"): tuple[branch, worktree: string] =
  ## Create a worktree for a task
  let branch = branchName(taskId)
  let worktree = worktreePath(taskId)

  # Fetch latest (warn but continue on failure - may be offline)
  let (fetchOut, fetchCode) = runGit(["fetch", "origin"])
  if fetchCode != 0:
    logWarn("createWorktree", "fetch failed, continuing with local state: " & fetchOut.strip())

  # Create branch from base
  discard runGitCheck(["branch", branch, fromBranch])

  # Create worktree
  createDir(parentDir(worktree))
  discard runGitCheck(["worktree", "add", worktree, branch])

  return (branch, worktree)

proc removeWorktree*(taskId: string) =
  ## Remove a worktree
  let worktree = worktreePath(taskId)
  if dirExists(worktree):
    let (output, code) = runGit(["worktree", "remove", "--force", worktree])
    if code != 0:
      logWarn("removeWorktree", "failed for " & taskId & ": " & output.strip())

proc removeBranch*(taskId: string, remote: bool = true) =
  ## Remove feature branch
  let branch = branchName(taskId)
  let (localOut, localCode) = runGit(["branch", "-D", branch])
  if localCode != 0:
    logWarn("removeBranch", "local delete failed for " & branch & ": " & localOut.strip())
  if remote:
    let (remoteOut, remoteCode) = runGit(["push", "origin", "--delete", branch])
    if remoteCode != 0:
      logWarn("removeBranch", "remote delete failed for " & branch & ": " & remoteOut.strip())

proc rebaseOnIntegration*(worktree: string): bool =
  ## Rebase worktree on integration branch. Returns false on conflict.
  let (fetchOut, fetchCode) = runGit(["fetch", "origin"], workDir = worktree)
  if fetchCode != 0:
    logWarn("rebaseOnIntegration", "fetch failed, using local state: " & fetchOut.strip())
  let (output, code) = runGit(["rebase", "origin/integration"], workDir = worktree)

  if code != 0:
    if "conflict" in output.toLowerAscii():
      return false
    # Other error - abort and raise
    discard runGit(["rebase", "--abort"], workDir = worktree)
    raise newException(GitError, &"Rebase failed: {output}")

  return true

proc isRebaseInProgress*(worktree: string): bool =
  ## Check if a rebase is in progress
  return dirExists(worktree / ".git" / "rebase-merge") or
         dirExists(worktree / ".git" / "rebase-apply")

proc pushBranch*(worktree: string, branch: string) =
  ## Push branch to origin
  discard runGitCheck(["push", "-u", "origin", branch], workDir = worktree)

proc mergeToIntegration*(taskId: string, maxRetries: int = 3): bool =
  ## Merge feature branch to integration with retry loop
  let branch = branchName(taskId)

  for attempt in 1..maxRetries:
    # Fetch latest
    discard runGitCheck(["fetch", "origin", "integration", branch])

    # Checkout and reset integration
    discard runGitCheck(["checkout", "integration"])
    discard runGitCheck(["reset", "--hard", "origin/integration"])

    # Merge
    let (mergeOutput, mergeCode) = runGit(["merge", "--no-ff", branch,
                                           "-m", &"Merge {branch}"])
    if mergeCode != 0:
      if "conflict" in mergeOutput.toLowerAscii():
        discard runGit(["merge", "--abort"])
        return false
      raise newException(GitError, &"Merge failed: {mergeOutput}")

    # Push
    let (pushOutput, pushCode) = runGit(["push", "origin", "integration"])
    if pushCode == 0:
      return true

    # Push failed, probably remote changed - retry
    if attempt < maxRetries:
      sleep(1000)  # Brief pause before retry
      continue
    else:
      raise newException(GitError, &"Push failed after {maxRetries} retries: {pushOutput}")

  return false

proc getConflictedFiles*(worktree: string): seq[string] =
  ## Get list of files with conflicts
  let (output, _) = runGit(["diff", "--name-only", "--diff-filter=U"], workDir = worktree)
  for line in output.splitLines():
    if line.strip() != "":
      result.add(line.strip())

