package maintenancewindow

import (
	"context"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/tidbcloud/pkgv2/worker"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

func (svc *service) StartSyncMaintenanceWindowWorker() {
	const syncMaintenanceWindowLeaseTTL = 5 * time.Minute
	w := worker.NewPeriodWorker(
		tilog.With(zap.String("worker", "syncMaintenanceWindow")),
		calcMaintenanceWorkerLockAttemptInterval(syncMaintenanceWindowLeaseTTL),
		func(ctx context.Context) error {
			owner := genLeaseAttemptOwnerID(maintenanceWorkerOwnerID())
			acquired, err := svc.leaseLocker.Acquire(
				ctx,
				"tidb-mgmt-service:worker:syncMaintenanceWindow",
				owner,
				syncMaintenanceWindowLeaseTTL,
			)
			if err != nil {
				tilog.Error(ctx, "SyncMaintenanceWindowWorker acquire lease failed", zap.Error(err))
				return err
			}
			if !acquired {
				tilog.Info(ctx, "SyncMaintenanceWindowWorker skip as lease held by another replica")
				return nil
			}
			tilog.Info(ctx, "SyncMaintenanceWindowWorker start")
			if _, err := svc.SyncMaintenanceWindow(ctx, &tidbmgmtservicev1.SyncMaintenanceWindowRequest{}); err != nil {
				tilog.Error(ctx, "SyncMaintenanceWindowWorker failed", zap.Error(err))
			}
			tilog.Info(ctx, "SyncMaintenanceWindowWorker done")
			return nil
		},
	)
	w.Start()
}

const (
	maintenanceLockAttemptDivider = 10
	maintenanceMinAttemptInterval = time.Second
)

var (
	maintenanceWorkerOwnerOnce sync.Once
	maintenanceWorkerOwner     string
)

func maintenanceWorkerOwnerID() string {
	maintenanceWorkerOwnerOnce.Do(func() {
		podName := os.Getenv("POD_NAME")
		if podName != "" {
			maintenanceWorkerOwner = podName
			return
		}

		maintenanceWorkerOwner = "pid-" + strconv.Itoa(os.Getpid())
	})

	return maintenanceWorkerOwner
}

func calcMaintenanceWorkerLockAttemptInterval(leaseTTL time.Duration) time.Duration {
	interval := leaseTTL / maintenanceLockAttemptDivider
	if interval < maintenanceMinAttemptInterval {
		return maintenanceMinAttemptInterval
	}
	return interval
}

func genLeaseAttemptOwnerID(baseOwnerID string) string {
	return baseOwnerID + ":" + strconv.FormatInt(time.Now().UnixNano(), 10)
}
