package watcher

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

	"firewall_containers/network-go/logger"
)

// FileWatcher periodically polls a file for changes AND triggers a periodic
// reconciliation callback regardless of file changes.
type FileWatcher struct {
	path            string
	period          time.Duration
	lastHash        string
	onChange        func()
	stopCh          chan struct{}
}

// NewFileWatcher creates a new file watcher that:
// 1. Polls the file for content changes at the given period
// 2. Triggers a reconciliation callback every period regardless of changes
//    to ensure desired state is maintained (stateful reconciliation)
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.
// Every period, it checks if the file changed AND triggers a full reconciliation
// to maintain the desired state (handles container restarts, iptables flushes, etc.)
func (fw *FileWatcher) Start() {
	// Compute initial hash
	hash, err := fw.hashFile()
	if err != nil {
		logger.Warn("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()

		logger.Info("WATCHER: started watching %s every %s (periodic reconciliation enabled)", fw.path, fw.period)

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

				fileChanged := hash != fw.lastHash
				if fileChanged {
					logger.Info("WATCHER: detected change in %s", fw.path)
					fw.lastHash = hash
				}

				// Trigger reconciliation every period to maintain state,
				// even if the config file hasn't changed.
				// This ensures container restarts, iptable flushes, etc.
				// are corrected.
				if fw.onChange != nil {
					if fileChanged {
						logger.Info("WATCHER: triggering reconciliation (config changed)")
					} else {
						logger.Debug("WATCHER: triggering periodic state reconciliation")
					}
					fw.onChange()
				}
			}
		}
	}()
}

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