package util

import (
	"context"
	"errors"
	"sync"
)

// this is a decorator for a regular context that contains a custom error and returns the
type contextWithCancellableReason struct {
	context.Context
	mtx sync.Mutex
	err error
}

func (c *contextWithCancellableReason) Err() error {
	c.mtx.Lock()
	defer c.mtx.Unlock()
	if c.err != nil {
		return errors.Join(c.Context.Err(), c.err)
	}
	return c.Context.Err()
}

type CancelCauseFunc func(error)

// WithCancelCause creates a cancellable context that can be cancelled with a custom reason
func WithCancelCause(parent context.Context) (context.Context, CancelCauseFunc) {
	ctx, cancel := context.WithCancel(parent)
	errOnce := sync.Once{}
	result := &contextWithCancellableReason{
		Context: ctx,
	}
	cancelFn := func(reason error) {
		errOnce.Do(func() {
			result.mtx.Lock()
			result.err = reason
			result.mtx.Unlock()
			cancel()
		})
	}
	return result, cancelFn
}
