package data

import (
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
)

// watchConfig refreshes the in-memory config snapshot on file changes, while
// /system/reloadSystem remains responsible
// for rebuilding database, Redis, storage, and scheduled tasks.
func (d *Data) watchConfig() func() {
	logger := d.logger()
	configPath := d.runtime.ConfigPath()
	if configPath == "" {
		return func() {}
	}
	absolute, err := filepath.Abs(configPath)
	if err != nil {
		logger.Error("resolve config watch path", "mod", "system", "error", err)
		return func() {}
	}
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		logger.Error("create config watcher", "mod", "system", "error", err)
		return func() {}
	}
	if err = watcher.Add(filepath.Dir(absolute)); err != nil {
		logger.Error("watch config directory", "mod", "system", "error", err)
		_ = watcher.Close()
		return func() {}
	}
	done := make(chan struct{})
	var once sync.Once
	go func() {
		var timer *time.Timer
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok || filepath.Clean(event.Name) != absolute || event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 {
					continue
				}
				if timer != nil {
					timer.Stop()
				}
				timer = time.AfterFunc(100*time.Millisecond, func() {
					next, loadErr := readBootstrap(absolute)
					if loadErr != nil {
						logger.Error("reload changed config", "mod", "system", "error", loadErr)
						return
					}
					if next.Data == nil || next.Admin == nil {
						logger.Error("reload changed config: data and admin configuration are required", "mod", "system")
						return
					}
					if current := d.runtime.Admin(); current != nil {
						next.Admin.Storage = current.Storage
						next.Admin.Email = current.Email
					}
					next.Admin.ConfigPath = absolute
					d.runtime.Replace(next.Data, next.Admin)
					logger.Info("config file changed", "mod", "system", "path", absolute)
				})
			case watchErr, ok := <-watcher.Errors:
				if ok {
					logger.Error("config watcher error", "mod", "system", "error", watchErr)
				}
			case <-done:
				if timer != nil {
					timer.Stop()
				}
				return
			}
		}
	}()
	return func() {
		once.Do(func() {
			close(done)
			_ = watcher.Close()
		})
	}
}
