package sync

import (
	"context"
	"log/slog"
	"time"

	"git.mchus.pro/mchus/quoteforge/internal/db"
)

// Worker performs background synchronization at regular intervals
type Worker struct {
	service  *Service
	connMgr  *db.ConnectionManager
	interval time.Duration
	logger   *slog.Logger
	stopCh   chan struct{}
}

// NewWorker creates a new background sync worker
func NewWorker(service *Service, connMgr *db.ConnectionManager, interval time.Duration) *Worker {
	return &Worker{
		service:  service,
		connMgr:  connMgr,
		interval: interval,
		logger:   slog.Default(),
		stopCh:   make(chan struct{}),
	}
}

// isOnline checks if the database connection is available
func (w *Worker) isOnline() bool {
	return w.connMgr.IsOnline()
}

// Start begins the background sync loop in a goroutine
func (w *Worker) Start(ctx context.Context) {
	w.logger.Info("starting background sync worker", "interval", w.interval)

	ticker := time.NewTicker(w.interval)
	defer ticker.Stop()

	// Run once immediately
	w.runSync()

	for {
		select {
		case <-ctx.Done():
			w.logger.Info("background sync worker stopped by context")
			return
		case <-w.stopCh:
			w.logger.Info("background sync worker stopped")
			return
		case <-ticker.C:
			w.runSync()
		}
	}
}

// Stop gracefully stops the worker
func (w *Worker) Stop() {
	w.logger.Info("stopping background sync worker")
	close(w.stopCh)
}

// runSync performs a single sync iteration
func (w *Worker) runSync() {
	// Check if online
	if !w.isOnline() {
		w.logger.Debug("offline, skipping background sync")
		return
	}

	if readiness, err := w.service.EnsureReadinessForSync(); err != nil {
		w.logger.Warn("background sync: blocked by readiness guard",
			"error", err,
			"reason_code", readiness.ReasonCode,
			"reason_text", readiness.ReasonText,
		)
		return
	}

	// Populate component catalog on first run (empty local_components)
	if err := w.service.SyncComponentsIfEmpty(); err != nil {
		w.logger.Warn("background sync: initial component sync failed", "error", err)
	}

	// Push pending changes first
	pushed, err := w.service.PushPendingChanges()
	if err != nil {
		w.logger.Warn("background sync: failed to push pending changes", "error", err)
	} else if pushed > 0 {
		w.logger.Info("background sync: pushed pending changes", "count", pushed)
	}

	// Then check for new pricelists
	err = w.service.SyncPricelistsIfNeeded()
	if err != nil {
		w.logger.Warn("background sync: failed to sync pricelists", "error", err)
		return
	}

	// Pull partnumber books together with pricelists
	if _, err := w.service.PullPartnumberBooks(); err != nil {
		w.logger.Warn("background sync: failed to pull partnumber books", "error", err)
	}

	w.logger.Info("background sync cycle completed")
}
