package jobs

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

	"qycw/go-server/internal/bootstrap"
	"qycw/go-server/internal/domain/commerce"
)

type MaintenanceWorker struct {
	service  *commerce.Service
	logger   *slog.Logger
	schedule map[string]time.Duration
	nextRun  map[string]time.Time
	license  workerLicenseGate
}

func NewMaintenanceWorker(app *bootstrap.App, logger *slog.Logger, executor *TaskExecutor) *MaintenanceWorker {
	service := commerce.NewService(app.DB, app.Redis, logger, app.Plugins).WithTaskExecutor(executor)
	if executor != nil && executor.identity != nil {
		service = service.WithIdentity(executor.identity)
	}
	return &MaintenanceWorker{
		service: service,
		logger:  logger,
		schedule: map[string]time.Duration{
			"agent_lifecycle_evaluate":         time.Hour,
			"zjmf_stock_sync":                  10 * time.Minute,
			"promotion_coupon_refund_expired":  time.Hour,
			"promotion_coupon_notify_expiring": time.Hour,
			"verification_material_cleanup":    time.Hour,
			"instance_real_data_collect":       5 * time.Minute,
			"recharge_maintenance":             5 * time.Minute,
			"cloud_node_health":                5 * time.Minute,
		},
		nextRun: map[string]time.Time{},
	}
}

func (w *MaintenanceWorker) Run(ctx context.Context) error {
	if w.service == nil {
		<-ctx.Done()
		return nil
	}
	if err := w.ProcessDueOnce(ctx, time.Now().UTC()); err != nil && w.logger != nil {
		w.logger.Warn("maintenance worker initial run failed", "error", err)
	}
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return nil
		case now := <-ticker.C:
			if err := w.ProcessDueOnce(ctx, now.UTC()); err != nil && w.logger != nil {
				w.logger.Warn("maintenance worker run failed", "error", err)
			}
		}
	}
}

func (w *MaintenanceWorker) ProcessDueOnce(ctx context.Context, now time.Time) error {
	if err := w.license.check(); err != nil {
		return err
	}
	var firstErr error
	for taskKey, interval := range w.schedule {
		if due := w.nextRun[taskKey]; !due.IsZero() && now.Before(due) {
			continue
		}
		w.nextRun[taskKey] = now.Add(interval)
		result, err := w.service.RunScheduledTask(ctx, taskKey)
		if err != nil {
			if firstErr == nil {
				firstErr = err
			}
			if w.logger != nil {
				w.logger.Warn("scheduled task failed", "task", taskKey, "error", err)
			}
			continue
		}
		if w.logger != nil && result["accepted"] == true {
			w.logger.Info("scheduled task completed", "task", taskKey, "summary", result["summary"])
		}
	}
	return firstErr
}
