package containerd

import (
	"context"
	"time"

	"github.com/containerd/containerd/leases"
	"github.com/containerd/log"
)

const (
	leaseExpireDuration = 8 * time.Hour
	expireLabel         = "containerd.io/gc.expire" // Copied from containerd
	pruneLeaseLabel     = "moby/prune.images"
	pruneLeaseFilter    = `labels."moby/prune.images"`
)

// withLease is used to prevent content or snapshots from being eligible for
// garbage collection while they are being used. Leases should always be
// released when complete to make the resources eligible again.
// If cancellable is set to true, then the lease will remain if the context
// is canceled until its expiration or deletion via prune.
func (i *ImageService) withLease(ctx context.Context, cancellable bool) (context.Context, func(), error) {
	_, ok := leases.FromContext(ctx)
	if ok {
		return ctx, func() {}, nil
	}

	ls := i.client.LeasesService()

	expireAt := time.Now().Add(leaseExpireDuration)
	l, err := ls.Create(ctx,
		leases.WithRandomID(),
		leases.WithLabels(map[string]string{
			pruneLeaseLabel: "true",
			expireLabel:     expireAt.Format(time.RFC3339),
		}))
	if err != nil {
		return ctx, func() {}, err
	}

	ctx = leases.WithLease(ctx, l.ID)
	return ctx, func() {
		if ctx.Err() != nil && cancellable {
			log.G(ctx).WithFields(log.Fields{"lease": l.ID, "expires_at": expireAt}).Info("Cancel with lease, leased resources will remain until expiration")
			return
		}
		if err := ls.Delete(context.WithoutCancel(ctx), l); err != nil {
			log.G(ctx).WithError(err).WithFields(log.Fields{"lease": l.ID, "expires_at": expireAt}).Warn("Error deleting lease, leased resources will remain until expiration")
		}
	}, nil
}
