package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
)

func runFindDelete(searchString, dir, workDir string) {
	cmd := exec.Command("git", "grep", "-l", "-F", searchString)
	cmd.Stderr = os.Stderr
	cmd.Dir = dir

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatalf("Failed to create stdout pipe for git grep: %s\n", err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatalf("Failed to start git grep: %s\n", err)
	}

	var filePaths []string
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		filePaths = append(filePaths, scanner.Text())
	}

	if err := cmd.Wait(); err != nil {
		if strings.HasSuffix(err.Error(), "exit status 1") {
			fmt.Fprintf(os.Stderr, "Pattern not found in: %s\n", dir)
			return
		}
		log.Fatalf("Failed to wait for git grep: %s\n", err)
	}

	relDir, err := filepath.Rel(workDir, dir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
		return
	}

	fmt.Fprintf(os.Stderr, "Found at %s in %d files...\n", relDir, len(filePaths))

	var wg sync.WaitGroup
	for i, relFilePath := range filePaths {
		fmt.Fprintf(os.Stderr, "Deleting in: %s\n", relFilePath)
		absFilePath := filepath.Join(dir, relFilePath)

		wg.Add(1)
		go func(i int, filePath string) {
			defer wg.Done()

			fileContent, err := os.ReadFile(filePath)
			if err != nil {
				log.Printf("Failed to read file %s: %s\n", filePath, err)
				return
			}

			lines := strings.Split(string(fileContent), "\n")
			var newLines []string
			for _, line := range lines {
				if !strings.Contains(line, searchString) {
					newLines = append(newLines, line)
				}
			}

			err = os.WriteFile(filePath, []byte(strings.Join(newLines, "\n")), 0644)
			if err != nil {
				log.Printf("Failed to write to file %s: %s\n", filePath, err)
				return
			}

			fmt.Fprintf(os.Stderr, "[%d/%d]: deleted in %s\n", i+1, len(filePaths), filepath.Base(filePath))
		}(i, absFilePath)
	}

	wg.Wait()
}

func main() {
	if len(os.Args) != 2 {
		log.Fatalf("Usage: %s <search string>\n", os.Args[0])
	}

	searchString := os.Args[1]

	currentDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Failed to get current directory: %s\n", err)
	}

	// If the .git directory does not exist in the current dir, check if it exists in child dirs.
	if _, err := os.Stat(filepath.Join(currentDir, ".git")); os.IsNotExist(err) {
		// .git does not exist, iterate through subdirectories
		filepath.Walk(currentDir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err // propagate the error
			}
			if info.IsDir() && info.Name() == ".git" {
				// Found a .git directory, run find-delete in its parent directory
				gitDir := filepath.Dir(path)
				runFindDelete(searchString, gitDir, currentDir)
				return filepath.SkipDir // skip remaining files in this directory
			}
			return nil
		})
	}

	// Try running the delete in the current dir as well
	runFindDelete(searchString, currentDir, currentDir)
}