package main

import (
	"io/fs"
	"log/slog"
	"os"
	"path/filepath"
	"time"

	"github.com/fsnotify/fsnotify"
)

const (
	WATCHER_DEBOUNCE = 300 * time.Millisecond
)

// INFO: this is hot reload for poor people
type Watcher struct {
	*fsnotify.Watcher
}

func New(fn func()) (*Watcher, error) {
	watcher := Watcher{}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	watcher.Watcher = w

	done := make(chan bool)

	go func() {
		var reloadTimer *time.Timer

		for {
			select {
			case event := <-watcher.Events:
				if event.Op&(fsnotify.Create|fsnotify.Write|fsnotify.Remove|fsnotify.Rename) != 0 {
					if reloadTimer != nil {
						reloadTimer.Stop()
					}
					reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
						slog.Debug("Changes detected...")
						fn()
					})
				}

				if event.Op&fsnotify.Create == fsnotify.Create {
					fi, statErr := os.Stat(event.Name)
					if statErr == nil && fi.IsDir() {
						_ = watcher.Add(event.Name)
						slog.Debug("Now watching new directory", "path", event.Name)
					}
				}

			case err := <-watcher.Errors:
				if err != nil {
					slog.Error("fsnotify error", "error", err)
				}

			case <-done:
				watcher.Close()
				return
			}
		}
	}()

	return &watcher, nil
}

func (w *Watcher) AddRecursive(root string) error {
	return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			werr := w.Add(path)
			if werr != nil {
				return werr
			}
			slog.Debug("Now watching directory", "path", path)
		}
		return nil
	})
}
