import type { CommandResult } from "./run.ts"
import type { ShoutFile } from "./parse.ts"
import { matchOutput } from "./match.ts"
import { isCommentLine } from "./parse.ts"
import { trimTrailingEmpty } from "./utils.ts"

export function rewriteFile(
  file: ShoutFile,
  results: CommandResult[],
  originalContent: string,
): string {
  const lines = originalContent.split("\n")
  const output: string[] = []

  let cmdIdx = 0

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]!

    if (isCommentLine(line)) {
      // Preserve comment lines as-is
      output.push(line)
    } else if (line.startsWith("$ ") && !line.startsWith("\\$ ")) {
      // Emit the command line as-is
      output.push(line)

      const cmd = file.commands[cmdIdx]
      const result = results[cmdIdx]
      if (!cmd || !result) {
        cmdIdx++
        continue
      }

      // Skip past old expected output lines in the original
      let j = i + 1
      while (j < lines.length && !isCommentLine(lines[j]!) && !(lines[j]!.startsWith("$ ") && !lines[j]!.startsWith("\\$ "))) {
        j++
      }
      // Collect old expected lines (before trimming trailing blanks for separator)
      const oldExpectedRaw = lines.slice(i + 1, j)

      // Check if old expected output had an exit code marker
      const oldTrimmed = trimTrailingEmpty(oldExpectedRaw)
      let oldExitMarker: string | null = null
      if (oldTrimmed.length > 0) {
        const last = oldTrimmed[oldTrimmed.length - 1]!
        if (/^\[(\d+|\*)\]$/.test(last)) {
          oldExitMarker = last
        }
      }

      // Determine how many trailing blank lines the original had
      let trailingBlanks = 0
      for (let k = oldExpectedRaw.length - 1; k >= 0; k--) {
        if (oldExpectedRaw[k] === "") trailingBlanks++
        else break
      }

      // If wildcards match, keep original expected output
      if (matchOutput(cmd.expected, result.actual)) {
        // Output original lines as-is
        for (const ol of oldExpectedRaw) output.push(ol)
      } else {
        // Replace with actual output (escape lines starting with "$ ")
        for (const al of result.actual) output.push(escapeDollar(al))
        // Re-add exit code marker if it existed
        if (oldExitMarker) output.push(oldExitMarker)
        // Preserve trailing blank lines as separators
        for (let k = 0; k < trailingBlanks; k++) output.push("")
      }

      // Skip original expected output lines (we already handled them)
      i = j - 1
      cmdIdx++
    } else if (cmdIdx === 0) {
      // Lines before first command (shouldn't normally exist but preserve them)
      output.push(line)
    }
  }

  return output.join("\n")
}

function escapeDollar(line: string): string {
  return line.startsWith("$ ") ? "\\" + line : line
}
