import { isAbsolute, resolve } from 'path'
import { getCwd } from '../../utils/state.js'
import { readFileSync } from 'fs'
import { detectFileEncoding } from '../../utils/file.js'
import { type Hunk } from 'diff'
import { getPatch } from '../../utils/diff.js'

/**
 * Applies an edit to a file and returns the patch and updated file.
 * Does not write the file to disk.
 */
export function applyEdit(
  file_path: string,
  old_string: string,
  new_string: string,
): { patch: Hunk[]; updatedFile: string } {
  const fullFilePath = isAbsolute(file_path)
    ? file_path
    : resolve(getCwd(), file_path)

  let originalFile
  let updatedFile
  if (old_string === '') {
    // Create new file
    originalFile = ''
    updatedFile = new_string
  } else {
    // Edit existing file
    const enc = detectFileEncoding(fullFilePath)
    originalFile = readFileSync(fullFilePath, enc)
    if (new_string === '') {
      if (
        !old_string.endsWith('\n') &&
        originalFile.includes(old_string + '\n')
      ) {
        updatedFile = originalFile.replace(old_string + '\n', () => new_string)
      } else {
        updatedFile = originalFile.replace(old_string, () => new_string)
      }
    } else {
      updatedFile = originalFile.replace(old_string, () => new_string)
    }
    if (updatedFile === originalFile) {
      throw new Error(
        'Original and edited file match exactly. Failed to apply edit.',
      )
    }
  }

  const patch = getPatch({
    filePath: file_path,
    fileContents: originalFile,
    oldStr: originalFile,
    newStr: updatedFile,
  })

  return { patch, updatedFile }
}
