package esr

import (
	"esr/internal/glob"
	"fmt"

	"github.com/fsnotify/fsnotify"
)

type Watcher struct {
	watcher *fsnotify.Watcher
	paths   []string
}

func NewWatcher(paths []string) (*Watcher, error) {
	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("failed to create fsnotify watcher: %v", err)
	}

	var newPaths []string
	for _, path := range paths {
		matchedPaths, err := glob.GlobFiles(path)
		if err != nil {
			return nil, fmt.Errorf("failed to glob files: %v", err)
		}
		newPaths = append(newPaths, matchedPaths...)
	}

	uniquePaths := make(map[string]struct{})
	for _, path := range newPaths {
		uniquePaths[path] = struct{}{}
	}

	finalPaths := make([]string, 0, len(uniquePaths))
	for path := range uniquePaths {
		finalPaths = append(finalPaths, path)
	}

	return &Watcher{
		watcher: fsWatcher,
		paths:   finalPaths,
	}, nil
}

func (w *Watcher) Start(callback func()) {
	for _, path := range w.paths {
		if err := w.watcher.Add(path); err != nil {
			panic(err)
		}
	}

	go func() {
		defer w.watcher.Close()
		for {
			select {
			case event, ok := <-w.watcher.Events:
				if !ok {
					return
				}
				if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
					go callback()
				}
			case err, ok := <-w.watcher.Errors:
				if !ok {
					return
				}
				panic(err)
			}
		}
	}()
}

func (w *Watcher) Close() error {
	return w.watcher.Close()
}
