package config

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

	"github.com/fsnotify/fsnotify"
	"github.com/rs/zerolog"
)

// AutomationToolsManager mirrors the Phase 1/3 watcher pattern for
// automation-tools.yaml.
type AutomationToolsManager struct {
	dir            string
	logger         zerolog.Logger
	current        atomic.Pointer[AutomationToolsDocument]
	listeners      []func(*AutomationToolsDocument)
	mu             sync.Mutex
	watcher        *fsnotify.Watcher
	stop           context.CancelFunc
	wg             sync.WaitGroup
	debounceWindow time.Duration
}

func NewAutomationToolsManager(dir string, logger zerolog.Logger) (*AutomationToolsManager, error) {
	initial, err := LoadAutomationToolsFromDir(dir)
	if err != nil {
		return nil, err
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	if err := w.Add(dir); err != nil {
		_ = w.Close()
		return nil, err
	}
	ctx, cancel := context.WithCancel(context.Background())
	m := &AutomationToolsManager{
		dir:            dir,
		logger:         logger.With().Str("component", "automation-tools-config").Logger(),
		watcher:        w,
		stop:           cancel,
		debounceWindow: 200 * time.Millisecond,
	}
	m.current.Store(initial)
	m.wg.Add(1)
	go m.runWatch(ctx)
	return m, nil
}

func (m *AutomationToolsManager) Current() *AutomationToolsDocument {
	return m.current.Load()
}

func (m *AutomationToolsManager) OnReload(f func(*AutomationToolsDocument)) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.listeners = append(m.listeners, f)
}

func (m *AutomationToolsManager) Stop() {
	m.stop()
	_ = m.watcher.Close()
	m.wg.Wait()
}

func (m *AutomationToolsManager) runWatch(ctx context.Context) {
	defer m.wg.Done()
	var pending *time.Timer
	for {
		select {
		case <-ctx.Done():
			if pending != nil {
				pending.Stop()
			}
			return
		case ev, ok := <-m.watcher.Events:
			if !ok {
				return
			}
			if filepath.Base(ev.Name) != AutomationToolsFileName {
				continue
			}
			if ev.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename|fsnotify.Remove) == 0 {
				continue
			}
			if pending != nil {
				pending.Stop()
			}
			pending = time.AfterFunc(m.debounceWindow, m.reload)
		case err, ok := <-m.watcher.Errors:
			if !ok {
				return
			}
			m.logger.Error().Err(err).Msg("fsnotify error")
		}
	}
}

func (m *AutomationToolsManager) reload() {
	next, err := LoadAutomationToolsFromDir(m.dir)
	if err != nil {
		m.logger.Error().Err(err).Msg("automation-tools.yaml reload rejected; previous remains in effect")
		return
	}
	m.current.Store(next)
	m.logger.Info().Int("count", len(next.AutomationTools)).Msg("automation-tools.yaml reloaded")
	m.mu.Lock()
	listeners := append([]func(*AutomationToolsDocument){}, m.listeners...)
	m.mu.Unlock()
	for _, f := range listeners {
		f(next)
	}
}
