package watcher

import (
	"crypto/md5"
	"fmt"
	"log"
	"os"
	"time"
)

// FileWatcher periodically checks a file for changes using MD5 hash
type FileWatcher struct {
	path    string
	period  time.Duration
	lastHash string
	onChange func()
	stopCh  chan struct{}
}

// NewFileWatcher creates a new file watcher that polls the file at the given period
func NewFileWatcher(path string, period time.Duration, onChange func()) *FileWatcher {
	return &FileWatcher{
		path:      path,
		period:    period,
		lastHash:  "",
		onChange:  onChange,
		stopCh:    make(chan struct{}),
	}
}

// hashFile computes an MD5 hash of the file contents
func (fw *FileWatcher) hashFile() (string, error) {
	data, err := os.ReadFile(fw.path)
	if err != nil {
		return "", fmt.Errorf("failed to read file %s: %w", fw.path, err)
	}
	return fmt.Sprintf("%x", md5.Sum(data)), nil
}

// Start begins polling the file for changes in a goroutine
func (fw *FileWatcher) Start() {
	// Compute initial hash
	hash, err := fw.hashFile()
	if err != nil {
		log.Printf("WATCHER: initial hash computation failed for %s: %v", fw.path, err)
	} else {
		fw.lastHash = hash
	}

	go func() {
		ticker := time.NewTicker(fw.period)
		defer ticker.Stop()

		log.Printf("WATCHER: started watching %s every %s", fw.path, fw.period)

		for {
			select {
			case <-fw.stopCh:
				log.Printf("WATCHER: stopped watching %s", fw.path)
				return
			case <-ticker.C:
				hash, err := fw.hashFile()
				if err != nil {
					log.Printf("WATCHER: failed to hash %s: %v", fw.path, err)
					continue
				}

				if hash != fw.lastHash {
					log.Printf("WATCHER: detected change in %s", fw.path)
					fw.lastHash = hash
					if fw.onChange != nil {
						fw.onChange()
					}
				}
			}
		}
	}()
}

// Stop signals the watcher goroutine to stop
func (fw *FileWatcher) Stop() {
	close(fw.stopCh)
}